Синтаксис с использованием LINQ ToList для приведения списка GENERIC к списку его базового типа
Я рассмотрел множество вопросов и ответов о стековом потоке, касающихся ToList и общих ограничений, но я не нашел такого, который объясняет "синтаксическую ошибку" в последнем возвращении ниже. Почему я должен явно Select
а также cast
элементы ("B")?
public interface I1
{
}
public class C2 : I1
{
public static List<I1> DowncastListA( List<C2> list )
{
// "A": A concrete class works.
return list == null ? null : list.ToList<I1>();
}
public static List<I1> DowncastListB<T2>( List<T2> list ) where T2 : I1
{
// "B": WORKS, if explicitly Select and Cast each element.
return list == null ? null : list.Select( a => (I1)a ).ToList();
}
public static List<I1> DowncastListC<T2>( List<T2> list ) where T2 : I1
{
// "C": Syntax Error: 'List<T2>' does not contain a definition for 'ToList' and the best extension method overload 'ParallelEnumerable.ToList<I1>(ParallelQuery<I1>)' requires a receiver of type 'ParallelQuery<I1>'
return list == null ? null : list.ToList<I1>();
}
}
Некоторые связанные вопросы:
/questions/20763685/c-universalnyij-gde-ogranichenie-s-opredeleniem-lyuboj-universalnyij-tip/20763694#20763694
Как преобразовать список
2 ответа
Метод расширения IEnumerable<T>.ToList<T>()
не позволяет указывать тип цели. T
это тип источника IEnumerable
(что неявно известно из исходной коллекции).
Вместо этого вы можете использовать это:
public static List<I1> DowncastListC<T2>( List<T2> list ) where T2 : I1
{
return list == null ? null : list.Cast<I1>().ToList();
}
Т.е. вы сначала разыгрываете каждый элемент (в результате чего IEnumerable<I1>
), затем создайте список из этого.
Кстати, вы могли бы даже сделать это методом расширения, чтобы упростить его использование:
public static class Extensions
{
public static List<I1> Downcast<T2>(this List<T2> list) where T2 : I1
{
return list == null ? null : list.Cast<I1>().ToList();
}
}
По предложению @AluanHaddad
public static IReadOnlyList<I1> DowncastList<T2>( List<T2> list ) where T2 : class, I1
{
return list;
}
Обратите внимание на добавленное ограничение T2 : class
,
Этот ответ не требует кастинга list
, так как IReadOnlyList<T>
является ковариантным, и в списке уже есть члены, которые реализуют I1. (Может альтернативно сделать тип возврата IEnumerable<I1>
, но мне нужно было индексировать, поэтому решил выставить более высокий интерфейс.)
Или как альтернатива, если хотите выставить полную List
функциональность:
public static List<I1> DowncastList<T2>( List<T2> list ) where T2 : class, I1
{
return list == null ? null : list.ToList<I1>();
}
Обратите внимание на добавленное ограничение T2 : class
, Это дает достаточно информации для IEnumerable<T2>
(который List<T2>
Implements), чтобы найти реализацию ToList<>`.
Теперь, когда это работает, вот вторая версия выше, использующая условный ноль C# 6:
public static List<I1> DowncastList<T2>( List<T2> list ) where T2 : class, I1
{
return list?.ToList<I1>();
}