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 вычисляется немедленно. У вас есть несколько вариантов, в том числе метательные 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();
вы должны преобразовать 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());
}