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 to Object. How can I make IReadOnlyCollection<String> convertible to IReadOnlyCollection<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.