Как эффективно использовать List <T>?
У меня есть
List<InputField>
но мне нужен
List<IDataField>
Есть ли способ сделать это в С#? Или использовать Linq для получения того же результата?
У меня есть два класса, которые реализуют один и тот же интерфейс:
interface IDataField { }
class InputField : IDataField { }
class PurchaseField : IDataField { }
Этот список исходит из запроса Linq-to-Sql:
List<InputField> list = (from i .... select i).ToList();
Ответы
Ответ 1
Оба .OfType <T> и .Cast <T> вернет список T, но смысл этих двух методов отличается.
list.OfType фильтрует исходный список и возвращает все элементы, имеющие тип T, и пропускает те, которые не относятся к этому типу.
list.Cast выдает все элементы в исходном списке, чтобы ввести T, и выдает исключение для элементов, которые нельзя отнести к этому типу.
В вашем случае оба будут давать одинаковый результат, но использование .Cast будет передавать ваше намерение намного более четко, поэтому я бы рекомендовал использовать это.
List<InputField> list = (from i .... select i).Cast<IDataField>.ToList();
Ответ 2
List<InputField> raw = (from i .... select i).ToList();
List<IDataField> result = raw.OfType<IDataField>().ToList();
Ответ 3
Вы также можете использовать List.ConvertAll.
Документация: http://msdn.microsoft.com/en-us/library/73fe8cwf.aspx
Пример:
List<IDataField> newList = oldList.ConvertAll(i => i as IDataField);
Ответ 4
Поскольку список исходит из
List<InputField> list = (from i .... select i).ToList();
Не могли бы вы просто исправить часть "select i", вместо этого вместо этого верните IDataField?
Что-то вроде этого:
List<InputField> list = (from i .... select (IDataField)i).ToList();
Если это не сработает, возможно, расширение "Cast" для IEnumerable будет работать:
List<DataField> list2 = list.Cast<IDataField>();
Ответ 5
На всякий случай: у меня мало опыта С#, но если эта общая конструкция означает то же самое, что и в Java, то вы должны создать целый новый список, параметризованный супертипом. Другими словами, если каждый экземпляр Bangle
также является экземпляром Akthud
, он выполняет не, что каждый List<Bangle>
также является List<Akthud>
.
Причиной этого является то, что вы можете иметь две ссылки на этот List<Bangle>
. Если вторая ссылка срабатывает, а затем ссылается на нее как List<Akthud>
, тогда ей разрешено добавлять к ней Akthud
, но теперь первая ссылка имеет List<Bangle>
, члены которой не все Bangle
s. Нарушение!
Считая, что ответ Дэвида Б действительно должен делать то, что вы хотите, правильно, AFAICT. (Это похоже на операцию копирования.)
[И если я недопонимаю семантику генераторов С#, я надеюсь, что кто-то поправит меня в комментарии!]
Ответ 6
Я не знаю, что прямой эффект будет иметь желаемый эффект. Редкие несколько раз, когда я это делал, обычно это что-то вроде:
List<InputField> list = .....
List<IDataField> list2 = new (List<IDataField>((IDataField[])list.ToArray()));
Ответ 7
ConvertAll кажется лучшим решением, поскольку он не зависит от библиотеки Linq и короче и интуитивно понятен.
Но оба они скорее обходные пути, чем решения. Оба они создают новую коллекцию с теми же данными. Должна быть поддержка общей ковариации в .net, то есть повышение уровня в общих коллекциях, что позволит вам сделать это естественным образом. Например:
List<IDataField> ls = (List<IDataField>)List<InputField>
Из моего поиска до сих пор, мой вывод, заключается в том, что .Net с 3.5 не поддерживает эту функцию. Следовательно, мы должны обходиться обходными решениями.
Это дискуссии по теме:
Ответ 8
У меня есть следующая строка
var str = "2 4 7 8 10";
когда я произнесу как следовать
var numbersInt2 = numbers.Split(' ')
.ToList().Cast<int>().ToList();
возвращает исключение, но
System.InvalidCastException: 'Указанное приведение недействительно.'
при использовании следующего
var numbersInt = numbers.Split(' ')
.ToList().ConvertAll(int.Parse);
это работает.