I've looked at lots of stackoverflow Q&As about ToList and generic constraints, but I haven't found one that explains the "Syntax Error" in the final return below. Why do I have to explicitly Select
and cast
the elements ("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>();
}
}
Some related Qs:
https://stackoverflow.com/a/1541173/199364
How to Cast List<T> To List<ClassName>
The extension method IEnumerable<T>.ToList<T>()
doesn't allow to specify a target type. T
is the type of the source IEnumerable
(which is implicitly known from the source collection).
Instead you can use this:
public static List<I1> DowncastListC<T2>( List<T2> list ) where T2 : I1
{
return list == null ? null : list.Cast<I1>().ToList();
}
I.e. you first cast each element (resulting in an IEnumerable<I1>
), then create a list from that.
BTW: you could even make that an extension method to simplify its usage:
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();
}
}
As suggested by @AluanHaddad
public static IReadOnlyList<I1> DowncastList<T2>( List<T2> list ) where T2 : class, I1
{
return list;
}
Note the added constraint T2 : class
.
That answer doesn't require any casting of list
, because IReadOnlyList<T>
is covariant, and list already has members that implement I1. (Could alternatively make the return type IEnumerable<I1>
, but I needed indexing, so chose to expose a higher interface.)
Or as an alternative, if wish to expose the full List
functionality:
public static List<I1> DowncastList<T2>( List<T2> list ) where T2 : class, I1
{
return list == null ? null : list.ToList<I1>();
}
Note the added constraint T2 : class
. This gives enough information for IEnumerable<T2>
(which List<T2>
implements) to find the implementation of ToList<>`.
Now that this works, here is second version above, using C# 6 null conditional:
public static List<I1> DowncastList<T2>( List<T2> list ) where T2 : class, I1
{
return list?.ToList<I1>();
}