Note: from .NET 4.5 onwards there is IReadOnlyList<out T> and IReadOnlyCollection<out T> which are both covariant, so there's no reason to use this project. This project is aimed at older versions of .NET
C# type-safe List<T> wrapper which can be cast to ILists of any (known) subtype.
The goal was to create a collection which could be cast to any collection whose elements inherit from the main type without any extra memory allocations.
Let's say we want to make the following code work:
class Foo { }
class Bar : Foo { }
var myCollection = SomeCollectionOfBars;
Test1(myCollection);
Test2(myCollection);
void Test1(IList<Bar> bars)
{
}
void Test2(IList<Foo> foos)
{
}
This could simply be achieved by casting our collection and creating a copy like so:
Test1(myCollection);
Test2(myCollection.Cast<Foo>().ToList());
However, this means we need to copy the whole list which in some cases can be too slow.
Another approach is to use IEnumerables:
Test1(myCollection);
Test2(myCollection.Cast<Foo>());
void Test1(IEnumerable<Bar> bars)
{
}
void Test2(IEnumerable<Foo> foos)
{
}
However, this means that we can no longer access the elements in random order efficiently.
With CovariantCollections this is possible and doesn't incur any copies:
var myCollection = new CovariantList<Foo, Bar>();
Test1(myCollection);
Test2(myCollection);
CovariantList has the same methods as List of the last given type (e.g. CovariantList<T1, T2, T3, T4, T5> has the same methods as List<T5>). However, it can be cast to IList<> or ICollection<> of any of the given types.
For example CovariantList<T1, T2, T3> can be cast to: List<T3>, IList<T3>, IList<T1>, ICollection<T2>, IEnumerable<T1>, and so on.
Another example:
class A { }
class B : A { }
class C : B { }
IList<C> list = new CovariantList<A, B, C>();
Test(list);
void Test(IList<C> list)
{
Test1((IList<A>)list); // OK
Test2((IList<B>)list); // OK
}
void Test1(IList<A> list)
{
}
void Test2(IList<B> list)
{
}