Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c# - LINQ select from Collection

I'm attempting to write a simple Select method on a class that inherits from IList.

public class RowDataCollection : IList<RowData> {
  private List<RowData> rowList;

  internal RowDataCollection(List<RowData> data) {
    rowList = data;
  }
  // ...
}

public RowDataCollection Rows;

public RowDataCollection Select(string colName, object value) {
  List<RowData> rowList = from item in Rows
         where item[colName].Value == value
         select item;
  return new RowDataCollection(rowList);
}

Some problems I'm having:

First:

  • VS2010 reports Cannot implicitly convert type 'IEnumerable<RowData>' to 'List<RowData>'. An explicit conversion exists (are you missing a cast?)

OK, where does the CAST go?

Second:

  • Someone could pass in an invalid colName value (i.e. String.IsNullOrEmpty(colName)) or a null parameter (object value == null).

How would I handle the way my function returns if the input parameters are invalid?

[Solved]

I edited my Select statement (even renamed it per the suggestions here). I had to use a switch to cast to the data type that the data was in, but it does work.

public RowDataCollection SelectRow(string colName, object value) {
  if (!String.IsNullOrEmpty(colName) && (value != null) && (0 < Rows.Count)) {
    switch (Rows[0][colName].GetValueType()) {
      case TableDataType.Boolean:
        return new RowDataCollection(Rows.Where(r => (bool)r[colName].Value == (bool)value).ToList());
      case TableDataType.Character:
        return new RowDataCollection(Rows.Where(r => (char)r[colName].Value == (char)value).ToList());
      case TableDataType.DateTime:
        return new RowDataCollection(Rows.Where(r => (DateTime)r[colName].Value == (DateTime)value).ToList());
      case TableDataType.Decimal:
        return new RowDataCollection(Rows.Where(r => (Decimal)r[colName].Value == (Decimal)value).ToList());
      case TableDataType.Integer:
        return new RowDataCollection(Rows.Where(r => (int)r[colName].Value == (int)value).ToList());
      case TableDataType.String:
        return new RowDataCollection(Rows.Where(r => r[colName].Value.ToString() == value.ToString()).ToList());
    }
  }
  return null;
}

[Solved (short version)]

Jon Skeet posted this about the same time I posted my solution, and (as always) his code is much nicer.

public RowDataCollection SelectRow(string colName, object value) {
  List<RowData> rowList = Rows.Where(r => r[colName].Value.Equals(value)).ToList();
  return new RowDataCollection(rowList);
}

@Jon Skeet: If I ever see your face in the same line at some software developer position I'm applying for, I'm just going to turn around and go home.

@Everyone: Thanks for all the help!

like image 804
jp2code Avatar asked Jul 12 '11 16:07

jp2code


1 Answers

The result of a query like that isn't a List<T>, it's an IEnumerable<T>. If you want to convert that into a List<T>, just call ToList:

List<RowData> rowList = (from item in Rows
                         where item[colName].Value == value
                         select item).ToList();

As it happens, you're only calling Where in your query. I would rewrite this as:

List<RowData> rowList = Rows.Where(item => item[colName].Value.Equals(value))
                            .ToList();

I'd also rename the method to something which is obviously filtering rather than projecting, given that the latter is the more common use of the term "select" in LINQ.

As for input parameters - I suggest you validate the arguments and throw an exception if they're not valid:

if (string.IsNullOrEmpty(colName))
{
    throw new ArgumentException("colName");
}
like image 121
Jon Skeet Avatar answered Sep 21 '22 12:09

Jon Skeet