C # Generics, Interfaces and Inheritance

I have two interfaces:

public interface IAmA { } public interface IAmB<T> where T : IAmA { } 

And two classes that implement these interfaces:

 public class ClassA : IAmA { } public class ClassB : IAmB<ClassA> { } 

When trying to use these classes as shown:

 public class Foo { public void Bar() { var list = new List<IAmB<IAmA>>(); list.Add(new ClassB()); } } 

I get this compiler error:

cannot convert from 'ClassB' to 'IAmB<IAmA>'

I know that I can make the compiler happy using:

 public class ClassB : IAmB<IAmA> { } 

But I need to be the Type parameter for IAmB<> in the ClassB implementation of IAmA .

+6
source share
5 answers

The quick answer is that you can do what you ask by declaring a type parameter IAmB<T> as covariant , only if the type is used as the return type :

 public interface IAmB<out T> where T : IAmA { T SomeMethod(string someparam); } 

out T means that you can use a more specific type than the one specified in the restrictions.

You cannot use T as a parameter. The following commands will not compile:

 public interface IAmB<out T> where T : IAmA { void SomeMethod(T someparam); } 

From the documentation

You can use the covariance type parameter as the return value of a method belonging to the interface, or as the return type of a delegate. You cannot use a covariant type parameter as a general type constraint for interface methods.

This is not compiler complexity. Assuming you can declare a covariant method parameter, your list will contain some objects that cannot handle the IAmB<IAmA> parameter - they would expect to enter ClassA or more specific. Your code will compile but crash at runtime.

What begs the question - why do you want to use IAmB<ClassA> ?

You should think before using this, although there may be other, more suitable ways to solve your real problem. It is unusual to use a common interface that implements a particular type, but tries to use it as if it were implementing another interface.

You can check the MSDN documentation section on covariance and contravariance , as well as Eric Lippert and John Skeet's answers to this CO-question: the difference between covariance and contravariance

+11
source

Quick answer: make a general covariance type (see msdn ) in your interface

 public interface IAmB<out T> where T : IAmA { } 

this will solve the compiler problem.

But this will not answer the question why asked by Panagiotis Kanavos!

+3
source

The trick makes a type T constraint on IAmB<T> covariant, with the out keyword:

 public interface IAmB<out T> where T : IAmA { } 

This allows you to use a more specific type than originally indicated, in this case allows you to assign IAmB<ClassA> variable of type IAmB<IAmA> .

See the documentation for more information.

+2
source

I will simply explain why this error reported.

if your IAmB has a method

 public interface IAmB<T> where T : IAmA { void foo(T p); } public class ClassB : IAmB<ClassA> { void foo(ClassA p) { p.someIntField++; } } 

and we have another class

 public class ClassC : IAmB<ClassA2> { void foo(ClassA2 p) { p.someOtherIntField++; } } 

and suppose List<IAmB<IAmA>>.Add(T p) implement like this

 IAmA mParam = xxxx; void Add(IAmB<IAmA>> p){ p.foo(mParam); } 

I think everyone compiles OK. you pass an instance of ClassB to List.Add , it becomes

 void Add(IAmB<IAmA>> p){ //p is ClassB now p.foo(mParam);//COMPILER CAN NOT MAKE SURE mParam fit ClassB.foo } 
0
source

It can be solved with contravariance and covariance.

 public interface IAmA { } **public interface IAmB<out T> where T : IAmA { }** public class ClassA : IAmA { } public class ClassB : IAmB<ClassA> { } public class Foo { public void Bar() { var list = new List<IAmB<IAmA>>(); **list.Add(new ClassB());** } } 

Now you are not getting a compiler error. The compiler is happy.

0
source

All Articles