Noobnewbier
Noobnewbier

Reputation: 149

Contra/covariance and nested generics

I have a question on typing and contra/covairance.

Given the following classes

public class BoardItemsHolderRepository<THolder, TBoardItem> : DataRepository<IList<THolder>> 
    where TBoardItem : BoardItem
    where THolder : IBoardItemHolder<TBoardItem>
{
}
public interface IDataRepository<T> : IDataGetRepository<T>, IDataSetRepository<T> where T : class
{
}

public interface IDataGetRepository<out T> where T : class
{
    IObservable<T> GetObservableStream();
    IObservable<T> GetMostRecent();
}

public interface IDataSetRepository<in T> where T : class
{
    void Set(T value);
}

public abstract class DataRepository<T> : IDataRepository<T> where T : class
{
  ..implementation details
}
public class ConstructHolder : IBoardItemHolder<Construct>
{
  ..implementation details
}

Given the above 3 files, can someone explain to me why is the following happening?:

IDataGetRepository<IList<IBoardItemHolder<Construct>>> wontCompile = new BoardItemsHolderRepository<ConstructHolder, Construct>(); //illegal
IDataGetRepository<IList<ConstructHolder>> compile = new BoardItemsHolderRepository<ConstructHolder, Construct>();  //legal

I can't understand why implicit casting for the first line would not work, as the following line compiles(as expected)

IBoardItemHolder<Construct>> compile = new ConstructHolder();

Upvotes: 1

Views: 158

Answers (1)

Sweeper
Sweeper

Reputation: 271175

Let's expand the right hand side of the illegal line step by step. First, we start with

BoardItemsHolderRepository<ConstructHolder, Construct>

This is a DataRepository<IList<THolder>>, so the above is a kind of:

DataRepository<IList<ConstructHolder>>

which in turn is IDataGetRepository<T>, so the above is a kind of:

IDataGetRepository<IList<ConstructHolder>>

IDataGetRepository<T> is covariant on T. Recall exactly what this means: If U is a subtype of T, then IDataGetRepository<U> is a subtype of IDataGetRepository<T>. For example, IDataGetRepository<Cat> is a subtype of IDataGetRepository<Animal>, and so an instance of the former type can be assigned to a variable of the latter type.

However, IDataGetRepository<IList<ConstructHolder>> is not a subtype of IDataGetRepository<IList<IBoardItemHolder<Construct>>> and so cannot be assigned to it. Why? Because IList<ConstructHolder> is not a subtype of IList<IBoardItemHolder<Construct>>! IList<T> is invariant on T!

So what you are trying to do violates type safety, according to the type-checker. Maybe try using a IEnumerable rather than IList?

Upvotes: 2

Related Questions