I want to implement a Cast
method becuase I have tons of ugly source.Select(x => type(x)).ToArray()
. So I write a simple extension:
public static IEnumerable<TResult> CastConvertible<TResult>(this IEnumerable<IConvertible> source)
{
foreach (var value in source)
{
yield return (TResult) Convert.ChangeType(value, typeof (TResult));
}
}
But it doesn't work because of error:
Error CS1929 'IEnumerable< int>' does not contain a definition for 'CastConvertible' and the best extension method overload 'ZEnumerable.CastConvertible< short>(IEnumerable< IConvertible>)' requires a receiver of type 'IEnumerable< IConvertible>'
But int
is IConvertible
while we know that IEnumerable<out T>
is covariant so IEnumerable<DerivedType>
could be converted to IEnumerable<BaseType>
.
Here is an example:
int a = 10;
int[] b = {a};
IConvertible aa = a;
IEnumerable<IConvertible> bb = b;
So I should remove where
constraint to be able to use this method but in this case I lose compile-time checking that type can be converted.
Why covariance doesn't work in this case?
I'm not using Enumerable.Cast<T>
because it doesn't work for builtin types. For example short[] shorts = new int[] {1, 2, 3}.Cast<short>().ToArray();
will throw an exception, because Cast
method uses internally non-generic IEnumerable
so each value is boxed and then throws an exception, becuase an unboxing is only valid for exact initial type.