c# - LINQ выбрать из коллекции

Я пытаюсь написать простой Select метод в классе, который наследуется от 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);
}

некоторые проблемы у меня возникли:

первый:

  • VS2010 сообщает Cannot implicitly convert type 'IEnumerable<RowData>' to 'List<RowData>'. An explicit conversion exists (are you missing a cast?)

хорошо, куда идет гипс?

второй:

  • кто-то может пройти в инвалидном colName значение (т. е. String.IsNullOrEmpty(colName)) или нулевой параметр (object value == null).

как я буду обрабатывать способ возврата моей функции, если входные параметры недопустимы?

[решена]

я отредактировал мой Select заявление (даже переименовал его в соответствии с предложениями здесь). Мне пришлось использовать переключатель для приведения к типу данных, данных В, но он работает.

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;
}

[решена (короткая версия)]

Джон Скит опубликовал это примерно в то же время, когда я опубликовал свой решение, и (как всегда) его код намного приятнее.

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: если я когда-нибудь увижу ваше лицо в одной линии на какой-нибудь должности разработчика программного обеспечения, на которую я подаю заявку, я просто развернусь и пойду домой.

@Everyone: Спасибо за помощь!

4 ответов


результат такого запроса не является List<T>, это IEnumerable<T>. Если вы хотите преобразовать это в List<T>, просто позвоните ToList:

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

так получилось, что вы только звоните Where в запросе. Я бы переписал это как:

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

Я бы также переименовал метод в то, что, очевидно,фильтрация, а не проектирование, учитывая, что последнее является более распространенным использованием термина "select" в В LINQ.

Что касается входных параметров-я предлагаю вам проверить Аргументы и создать исключение, если они недействительны:

if (string.IsNullOrEmpty(colName))
{
    throw new ArgumentException("colName");
}

вы получаете сообщение об ошибке, потому что запросы LINQ возвращают IEnumerable, а не List.

Если вам нужен список, это достаточно просто:

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

вы не можете напрямую разыгрывать IEnumerable<RowData> до List<RowData>, однако, существует удобная функция Enumerable.ToList<T>(), используется вот так:

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

Что касается вашего второго вопроса, исключение произойдет во время ToList() вызов как выражение LINQ вычисляется немедленно. У вас есть несколько вариантов, в том числе метательные ArgumentExceptions или возврат пустого списка. Это зависит от ваших сценариев использования. Я бы предложил просто выбросить исключение (предполагая, что у вас есть некоторые HasColumn() метод RowData класс):

if (colName == null)
{
    throw new ArgumentNullException("colName");
}
else if (!Rows.All(row => row.HasColumn(colName)))
{
    throw new ArgumentException("No such column " + colName, "colName");
}

за ваше редактирование, другой подход, если отсутствует столбец, не обязательно является "проблемой":

...
// note the change to Any()
else if (!Rows.Any(row => row.HasColumn(colName))
{
    throw new ArgumentException("No such column " + colName, "colName");
}

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

вы должны преобразовать IQueriable в List, вызвав ToList ();

  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.ToList());
    }