I have some generic interface linked one to each other.
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:
The type 'b' cannot be used as type parameter 'T' in the generic type or method 'IC'. There is no implicit reference conversion from 'b' to 'IB'.
How can I properly use generic interfaces in this case ?
T
in IC<T>
is required to be an IB<IA>
. You have given it an IB<A>
. You have no guarantee that an IB<A>
can be used as an IB<IA>
just because A
implements IA
.
Think about it this way: if IB<T>
means "I can eat anything of type T", and IA
means "I am a fruit", and A
means "apple", then IB<IA>
means "I can eat any fruit", and IB<A>
means "I can eat any apple". If some code wants to feed you bananas and grapes then it needs to take an IB<IA>
, not an IB<A>
.
Let's suppose IB<A>
could be converted to IB<IA>
and see what goes 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 a property of type Apple
to a reference to an object of type Orange
.
That's why the conversion has to be illegal.
So how can you make this legal?
As the code stands, you cannot, because as I've just shown, it isn't typesafe.
You can make it legal by marking T
as out
like this: interface IB<out T>
. However, it is then illegal to use T
in any "input context". In particular, you cannot have any property of type T
that has a setter. If we make that restriction then the problem disappears because a_val
cannot be set to an instance of Orange
because it is read-only.
This question is asked extremely frequently on SO. Look for questions about "covariance and contravariance" in C# for plenty of examples.
I don't know if it can be done easier (and a bit more clean), 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; }
}
What's even more important, it don't let you do something like that:
public class a1 : IA
{
public int val { get; set; }
}
public class c : IC<b, a1>
{
public b b_val { get; set; }
}
Compiler throws following error:
The type 'ConsoleApplication2.b' cannot be used as type parameter 'T' in the generic type or method 'ConsoleApplication2.IC'. There is no implicit reference conversion from 'ConsoleApplication2.b' to 'ConsoleApplication2.IB'.
And that is really cool feature, isn't it?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With