Dr. Strangelove
Dr. Strangelove

Reputation: 3328

The type X cannot be used as type parameter T in the generic type Y

I don't see what is wrong with the following code, or how I can fix it. (Maybe I am misunderstanding generic types.)

class X<I>
   where I : ISomeInterface
{ }

class Y<T>
   where T : X<ISomeInterface>
{ }

class Z<I>
   where I : ISomeInterface
{
   Y<X<I>> AData { get; } // What compiler does not like
   Y<X<ISomeInterface>> BData { get; } // What compiler likes
}

Compiler complains that it cannot use X<I> as type parameter T in the generic type Y<T>.

Upvotes: 2

Views: 2133

Answers (1)

stackh34p
stackh34p

Reputation: 9009

Here is a working solution

interface ISomeInterface {}

class X<I>
   where I : ISomeInterface
{ }

class Y<T, I>
   where T : X<I>
   where I : ISomeInterface
{ }

class Z<I>
   where I : ISomeInterface
{
   Y<X<I>, I> MyOtherData { get; set; }
   Y<X<ISomeInterface>, ISomeInterface> MyData { get; set; }
}

Notice the additional generic parameter I and constraints I have added to the definition of type Y.

Your initial definition of Y is too restrictive. C# makes a difference between ISomeInterface and any type implementing ISomeInterface.

Why is it so ?

The .NET compiler has a special way of handing generic types. For each generic type the compiler will create a separate type based on the generic type arguments, which is instead used during runtime. For example List<int> and List<string> will be completely different types, all exhibiting the behaviours defined by the List<_> generic type, but with the actual type int or string baked into the concrete type generated by the compiler.

When you define class Y<T> where T : X<ISomeInterface> you "seal" the generic parameter of X to be ISomeInterface. The compiler will accept any type inheriting X<ISomeInterface> but not X<SomethingElse> even if SomethingElse is itself an implementation of ISomeInterface. This is because the fragment where T : X<ISomeInterface> causes the ISomeInterface to be "baked in" within the type definition of Y<T>. That behaviour is expected and is a side effect of how generics are compiled to actual code. For the same reason one cannot do the following:

List<object> x = new List<string>();

even though the type string inherits from object.

Upvotes: 4

Related Questions