C# generic inheritance and covariance part 2
Here is my original thread: C# generic inheritance and covariance
On just my read-only interfaces, I want inheritence to work.
public delegate Boolean EnumerateItemsDelegate<out ItemType>(ItemType item);
public interface IReadOnlyCollection<out ItemType>
{
Boolean ContainsItem(ItemType item);
Array CopyToArray();
void EnumerateItems(EnumerateItemsDelegate<ItemType> enumerateDelegate);
UInt32 Count { get; }
UInt32 Capacity { get; }
}
Like this except where it compiles :-p
This is what I would like to work:
IReadOnlyCollection<String> s;
IReadOnlyCollection<Object> o = s;
There does not appear to be any question in this question, so I'll make up a few questions to answer.
What is a covariant conversion?
Let's suppose we have some types Fruit
and Apple
and Banana
with the obvious relationships; Apple
is a kind of Fruit
, and so on.
A covariant conversion is one where the convertability of the type argument implies the convertibility of the generic type. If Apple
is convertible to Fruit
, and Bowl<Apple>
is convertible to Bowl<Fruit>
, then Bowl<T>
is covariant in T.
What is a contravariant conversion?
A contravariant conversion is a covariant conversion that reverses the direction instead of preserving it. If Eater<Fruit>
is convertible to Eater<Apple>
then Eater<T>
is contravariant in T.
How do I mark an interface or delegate as being covariant or contravariant in its type parameters?
Covariant type parameters are marked out
and contravariant type parameters are marked in
.
This is intended to be mnemonic: covariant interfaces typically have the type parameter appear in output positions and contravariant interfaces typeically have the type parameter appear in input positions.
String
is convertible toObject
. How can I makeIReadOnlyCollection<String>
convertible toIReadOnlyCollection<Object>
?
Make IReadOnlyCollection<T>
covariant in T. Mark it out
.
Consider the following code:
delegate void Action<in T>(T t);
interface IFoo<in X>
{
void M(Action<X> action);
}
Why does the compiler say that this is not valid?
Because it is not valid. Let's see why.
class Foo : IFoo<Fruit>
{
public void M(Action<Fruit> action)
{
action(new Apple()); // An apple is a fruit.
}
}
...
IFoo<Fruit> iff = new Foo();
IFoo<Banana> ifb = iff; // Contravariant!
ifb.M(banana => { banana.Peel(); });
Follow the logic. This program passes an apple as the "this" of Banana.Peel()
, which is clearly wrong.
The compiler knows that this can happen, and so disallows the interface to be declared in the first place.
What should I do if I have more questions about variance?
You should start by reading my articles on the design and implementation of the feature. Start from the bottom; they are listed in reverse chronological order:
http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/
If you still have a question then you should post questions here that actually contain questions, instead of making people guess what the question really is.