Variant Generic Interfaces

I have a generic interface, and a class implementing that interface with a concrete type parameter. I also have a generic class using the generic interface as its type constraint, but the type parameter is restricted to be a subclass of a certain base class. I want to instance the generic class with the class implementing that interface but have a problem of converting the class to that interface. The following code illustrates all the classes I mentioned:

The base class:

class DomainBase
{
}

The class used as the type parameter in the interface

class Person : DomainBase
{
}

The generic interface:

public interface IRepository<T> where T : class
{
    IEnumerable<T> Fetch();
    T Persist(T item);
}

The class implementing the generic interface:

class PersonRepository : IRepository<Person>
{
    public IEnumerable<Person> Fetch()
    {
        ...
    }
    public Person Persist(Person item)
    {
        ...
    }
}

The generic class using the generic interface:

class DomainBaseViewModel<Repository>
    where Repository : IRepository<DomainBase>, new()
{
    private Repository repository = new Repository();
    private ObservableCollection<DomainBase> items;
}

However, the following line can't get compiled because PersonRepository is unable to be converted to IRepository<DomainBase>:

var viewModel = new DomainBaseViewModel<PersonRepository>();

Although I can solve this issue by covariance but it disallows the use of the type parameter in parameter lists:

public interface IRepository<out T> where T : class
{
    ...
    T Persist(object item);
}

class PersonRepository : IRepository<Person>
{
    public Person Persist(object item)
    {
        ...
    }
}

So I have to convert the parameter to Person, which compromises type safety.

Is there a better way to allow covariance and the use of type parameter in parameter lists in this case?

Jon Skeet
people
quotationmark

No - the whole point of the restriction on covariance is that it guarantees safety. A PersonRepository isn't an IRepository<DomainBase> because you can't ask it to persist any arbitrary DomainBase object. What would you expect this code to do?

class Product : DomainBase {}
...
IRepository<DomainBase> repository = new PersonRepository();
repository.Persist(new Product());

PersonRepository doesn't know how to persist Product values.

If in some cases you only need the "read" parts of the repository interface, you could always call that out explicitly:

public interface IRepositoryReader<out T>
{
    IEnumerable<T> Fetch();
}

public interface IRepository<T> : IRepositoryReader<T>
{
    T Persist(T item);
}

Then your DomainBaseViewModel class could be:

class DomainBaseViewModel<TRepository>
    where TRepository : IRepositoryReader<DomainBase>, new()
{
    private TRepository repository = new TRepository();
    private ObservableCollection<DomainBase> items;
}

That doesn't work if you want your DomainBaseViewModel to persist items as well though. In that case, perhaps it should be generic in the type of model as well:

class DomainBaseViewModel<TRepository, TEntity>
    where TRepository : IRepository<TEntity>, new()
{
    private TRepository repository = new Repository();
    private ObservableCollection<TEntity> items;
}

Then:

var viewModel = new DomainBaseViewModel<PersonRepository, Person>();

people

See more on this question at Stackoverflow