Yatrix
Yatrix

Reputation: 13775

Can someone explain why this isn't valid implementation?

public class ServiceCodeController : ControllerBase {
    // the red squiggly under IJobRepository is saying it's not convertible
    private LazyRepo<IJobRepository> _domainRepo2; 
}

public class LazyRepo<TRepo> where TRepo : IRepository<IDomainEntity> { ... }

public interface IJobRepository : IRepository<JobDomain>, IListRepository { ... }

public interface IRepository<T> : IRepositoryRead<T>, 
    IRepositoryCreate<T>, 
    IRepositoryDelete<T>, 
    IRepositoryUpdate<T> 
    where T : IDomainEntity { ... }

public class JobDomain : BaseDomainEntity { ... }

public abstract class BaseDomainEntity : IDomainEntity, 
    IDomainEntityModifiable,
    IDomainEntityActivatable, 
    IDomainEntityNameable { ... }

My thinking is that LazyRepo takes something that implements IRepository that takes something that implements IDomainEntity. As you can see, IJobRepository implements IRepository that takes JobDomain that inherits from BaseDomainEntity which, at long last, implements IDomainEntity.

For my money, this should work for setting up the LazyRepo class.

Can someone explain to me why I'm getting this error? The type 'IJobRepository' cannot be used as type parameter 'TRepo' in the generic type or method 'LazyRepo'. There is no implicit reference conversion from 'IJobRepository' to 'IRepository'

Upvotes: 0

Views: 113

Answers (2)

Tyree Jackson
Tyree Jackson

Reputation: 2608

Try this:

public class ServiceCodeController : ControllerBase {
    // the red squiggly under IJobRepository is saying it's not convertible
    private LazyRepo<IJobRepository, JobDomain> _domainRepo2; 
}

public class LazyRepo<TRepo, TDomain> where TRepo : IRepository<TDomain> where TDomain : IDomainEntity {  }

By specifying the TDomain as a generic parameter constrained to IDomainEntity and constraining TRepo to IRepository of TDomain, you provide all of the information needed by the compiler to resolve IJobRepository and JobDomain as arguments for LazyRepo. This provides an alternative to using variance.

The issue has to deal with the fact that IRepository<IDomainEntity> != IRepository<JobDomain>. It's the classic fruit bowl issue that's been discussed on SO. However, if you substitute a generic parameter for IDomainEntity, then you can fully qualify the run-time definition of TRepo for LazyRepo.

For completeness, here is a modified version of your code that compiles:

public class ControllerBase {}
public interface IDomainEntity {}
public interface IDomainEntityModifiable {}
public interface IDomainEntityActivatable {}
public interface IDomainEntityNameable {}
public interface IListRepository {}
public interface IRepositoryRead<out TDomain> where TDomain : IDomainEntity {}
public interface IRepositoryCreate<out TDomain> where TDomain : IDomainEntity {}
public interface IRepositoryDelete<out TDomain> where TDomain : IDomainEntity {}
public interface IRepositoryUpdate<out TDomain> where TDomain : IDomainEntity {}
public class ServiceCodeController : ControllerBase 
{
    private LazyRepo<IJobRepository, JobDomain> _domainRepo2; 
}

public class LazyRepo<TRepo, TDomain> where TRepo : IRepository<TDomain> where TDomain : IDomainEntity {  }

public interface IJobRepository : IRepository<JobDomain>, IListRepository {  }

public interface IRepository<out T> : IRepositoryRead<T>, 
    IRepositoryCreate<T>, 
    IRepositoryDelete<T>, 
    IRepositoryUpdate<T> 
    where T : IDomainEntity {  }

public class JobDomain : BaseDomainEntity { }

public abstract class BaseDomainEntity : IDomainEntity, 
    IDomainEntityModifiable,
    IDomainEntityActivatable, 
    IDomainEntityNameable {  }

Upvotes: 0

Ann L.
Ann L.

Reputation: 13965

I think this is where the concepts of contravariance and covariance come in.

A covariant interface allows its methods to return more derived types than those specified in the interface. A contravariant interface allows its methods to accept parameters of less derived types than those specified in the interface.

source: https://msdn.microsoft.com/en-us/library/dd465120.aspx

You fix this by using the in and out keywords:

public interface IRepository<out T> : ...

(source: https://msdn.microsoft.com/en-us/library/dd997386.aspx)

Upvotes: 3

Related Questions