General hierarchical structure of the interface

I have a common interface connected to one another.

public interface IA { int val { get; set; } } public interface IB<T> where T:IA { T a_val { get; set; } } public interface IC<T> where T : IB<IA> { T b_val { get; set; } } public class a:IA { public int val { get; set; } } public class b:IB<a> { public a a_val { get; set; } } public class c:IC<b> { public b b_val { get; set; } } 

For the last class c, I have an error:

Type 'b' cannot be used as a parameter of type 'T' in a generic type or IC method. There is no implicit conversion of links from 'b' to "IB".

How can I use common interfaces correctly in this case?

+4
source share
2 answers

T in IC<T> requires IB<IA> . You gave him IB<A> . You have no guarantee that IB<A> can be used as IB<IA> only because A implements IA .

Think of it this way: if IB<T> means "I can eat something like T," and IA means "I am the fruit," and A means "apple," then IB<IA> means "I can eat fruit." , and IB<A> means "I can eat any apple." If some code wants to feed you with bananas and grapes, then he needs to take IB<IA> , not IB<A> .

Suppose that IB<A> can be converted to IB<IA> and see what is wrong:

 class AppleEater : IB<Apple> { public Apple a_val { get; set; } } class Apple : IA { public int val { get; set; } } class Orange : IA { public int val { get; set; } } ... IB<Apple> iba = new AppleEater(); IB<IA> ibia = iba; // Suppose this were legal. ibia.a_val = new Orange(); // ibia.a_val is of type IA and Orange implements IA 

And now we just set iba.val , which is an Apple type property to reference an object of type Orange .

That is why conversion must be illegal.

So how can you make this legal?

Since the code is worth it, you cannot, because, as I just showed, it is not typical.

You can make this legal by marking T as out as follows: interface IB<out T> . However, then using T in any "input context" is prohibited. In particular, you cannot have any property of type T that has a setter. If we make this restriction, the problem will disappear because a_val cannot be set to an instance of Orange , because it is read-only.

This question is asked very often on SO. Look for "covariance and contravariance" questions in C # for many examples.

+7
source

I don't know if it can be made simpler (and a little cleaner), but this code compiles:

 public interface IA { int val { get; set; } } public interface IB<T> where T : IA { T a_val { get; set; } } public interface IC<T, U> where T : IB<U> where U : IA { T b_val { get; set; } } public class a : IA { public int val { get; set; } } public class b : IB<a> { public a a_val { get; set; } } public class c : IC<b, a> { public b b_val { get; set; } } 

More importantly, it does not allow you to do something like this:

 public class a1 : IA { public int val { get; set; } } public class c : IC<b, a1> { public b b_val { get; set; } } 

The compiler produces the following error:

The type 'ConsoleApplication2.b' cannot be used as a parameter of type 'T' in the general type or method "ConsoleApplication2.IC". There is no implicit conversion of links from 'ConsoleApplication2.b' to 'ConsoleApplication2.IB'.

And that's a really cool feature, isn't it?

+3
source

All Articles