c # - LINQ выбрать из коллекции - PullRequest
9 голосов
/ 12 июля 2011

Я пытаюсь написать простой 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);
}

У меня проблемы:

First:

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

Хорошо, куда уходит 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);
}

@ Джон Скит: Если я когда-нибудь увижу ваше лицо в одной строке в какой-то должности разработчика программного обеспечения, на которую я претендую, я просто поверну и уйду домой.

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

Ответы [ 4 ]

11 голосов
/ 12 июля 2011

Результат такого запроса не 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();

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

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

if (string.IsNullOrEmpty(colName))
{
    throw new ArgumentException("colName");
}
1 голос
/ 12 июля 2011

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

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

List<RowData> rowList = (from item in Rows
                         where item[colName].Value == value
                         select item).ToList();
1 голос
/ 12 июля 2011

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

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

Что касается вашего второго вопроса, во время вызова ToList() произойдет исключение, так как выражение LINQ вычисляется немедленно. У вас есть несколько вариантов, в том числе выбрасывание ArgumentException s или возврат пустого списка. Это зависит от ваших вариантов использования. Я бы предложил просто вызвать исключение (при условии, что у вас есть 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();
0 голосов
/ 12 июля 2011

Вы должны конвертировать 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());
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...