IList using covariance and contravariance in c#, is this possible?
Asked Answered
A

3

6

would this be possible? (I don't have vs. 2010, so I can't try it myself, sorry)

public interface IComplexList<out TOutput, in TInput> where TOutput : TInput
{
    public IEnumerator<TOutput> GetEnumerator();
    public void Add(TInput item);
}

public interface IList<T> : IComplexList<T, T>
{
}

If I get it right, you could use this to actually implement covariance and contravariance in the same interface.

Alethaalethea answered 11/8, 2009 at 8:44 Comment(0)
B
5

No, you can't. In your example IList<T> is invariant. IList<T> would require to declare in/out to be covariant/contravariant. It's not possible to do that just by inheriting some interface that is covariant.

Beauchamp answered 11/8, 2009 at 8:45 Comment(0)
A
8

Well, your question is slightly confusing because of the existing IList<T> type. However, the following does compile:

public interface IComplexList<out TOutput, in TInput> where TOutput : TInput
{
    IEnumerator<TOutput> GetEnumerator();
    void Add(TInput item);
}

public interface ISimpleList<T> : IComplexList<T, T>
{
}

You can even change it to extend IEnumerable<TOutput>:

public interface IComplexList<out TOutput, in TInput>
    : IEnumerable<TOutput>
    where TOutput : TInput
{        
    void Add(TInput item);
}

public interface ISimpleList<T> : IComplexList<T, T>
{
}

The indexer is tricky, because you'd want different types involved. You could do:

TOutput Get(int index);
void Set(int index, TInput item);

and then put the indexer into ISimpleList<T> instead of course...

That doesn't let you use ISimpleList<T> variantly though, because you've basically forced TInput=TOutput.

An alternative approach is to separate out the input from the output:

public interface IReadableList<out T> : IEnumerable<T>
{
    T Get(int index);
}

public interface IWritableList<in T>
{
    void Add(T item);
    void Set(int index, T item);
}

 public interface IMyList<T> : IReadableList<T>, IWritableList<T> {}

Then you could write:

public void Foo(IWritableList<string> x) { ... }

IMyList<object> objects = new MyList<object>();
Foo(objects);

and vice versa for IReadableList. In other words, you allow variance for each side individually, but you never get variance for the two sides together.

Armful answered 11/8, 2009 at 8:49 Comment(3)
It certainly does compile but it wouldn't be co/contra-variant. ISimpleList<string> cannot be used as ISimpleList<object>.Beauchamp
I'll update my answer to be more specific - sorry, still thinking all this through.Armful
One point not yet mentioned: in addition to splitting the covariant read-accessors and the contravariant write-accessors, one could also benefit from splitting out members like Count which are type-independent. Incidentally, it would be possible to sort a list-like object without regard for the type of object contained therein if the interface provided CompareAt(index1, index2) and SwapAt(index1, index2) methods.Swansea
B
5

No, you can't. In your example IList<T> is invariant. IList<T> would require to declare in/out to be covariant/contravariant. It's not possible to do that just by inheriting some interface that is covariant.

Beauchamp answered 11/8, 2009 at 8:45 Comment(0)
S
0

If an implementation of a read-write property were also considered an implementation of a read-only property, one could add a useful form of List covariance and contravariance by having IList(of T) derive from IReadableList(of Out T) and IAddableList(of In T). Provided that those interfaces simply included members that were present in IList(Of T) before they were defined, code which implemented IList(Of T) would automatically implement those other members. Unfortunately, for IReadableList to be covariant, it would have to have a read-only indexer property; the implementation of the read-write property in IList could not be substituted. Having IList(Of T) inherit from a usable IReadableList(Of Out T) would thus break all implementations of IList(Of T).

Swansea answered 23/12, 2010 at 17:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.