Reputation: 14334
Type variance makes my head hurt. I was under the impression that the out
variance modifier on IEnumerable<out T>
should allow this behavior.
interface ISampleInterface
{
}
/// <summary>
/// This works great!
/// </summary>
static void GenericMethod<TSeq>(TSeq sequence) where TSeq : IEnumerable<ISampleInterface>
{
UseInterface(sequence);
}
/// <summary>
/// This does not compile :(
/// </summary>
static void GenericMethod<T>(IEnumerable<T> sequence) where T : ISampleInterface
{
UseInterface(sequence);
}
static void UseInterface(IEnumerable<ISampleInterface> item)
{
throw new NotImplementedException();
}
The error is;
Error CS1503 Argument 1: cannot convert from
System.Collections.Generic.IEnumerable<T>
toSystem.Collections.Generic.IEnumerable<ISampleInterface>
Why does GenericMethod<T>
fail to compile, but GenericMethod<TSeq>
compiles just fine?
Upvotes: 2
Views: 56
Reputation: 42225
The problem is that variance (e.g. IEnumerable<out T>
) only applies to reference conversions. You can't do:
IEnumerable<object> x = new List<int>(); // Boxing conversion
IEnumerable<long> x = new List<int>(); // User-defined type conversion
(This intuitively makes sense: the code using x
doesn't know that the underlying collection is of a different type, so there's no way for it to insert the code to box each int
, or convert each int
to a long
.)
In:
static void GenericMethod<T>(IEnumerable<T> sequence) where T : ISampleInterface
{
UseInterface(sequence);
}
The compiler does know that T
has to implement ISampleInterface
, but that doesn't stop you from defining:
public struct Foo : ISampleInterface { }
and passing that. Because there's no reference conversion from Foo
to ISampleInterface
, covariance isn't allowed.
If you tell the compiler that T
must additionally be a reference type, by doing:
static void GenericMethod<T>(IEnumerable<T> sequence) where T : class, ISampleInterface
then the compiler can be sure that there is a reference conversion from T
to ISampleInterface
, and it compiles.
Upvotes: 4