Gusdor
Gusdor

Reputation: 14334

Generic type with parameter type restriction cannot be passed to method with non-generic parameter

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> to System.Collections.Generic.IEnumerable<ISampleInterface>

Why does GenericMethod<T> fail to compile, but GenericMethod<TSeq> compiles just fine?

Upvotes: 2

Views: 56

Answers (1)

canton7
canton7

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

Related Questions