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'
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)
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 { }
Related
StateBase implements iState
WolfState ineherits from StateBase
WolfController inherits from ControllerBase
I want to be able to do a
WolfState that uses WolfController inheriting from State
SheepState
that uses SheepController and inherits from State
WolfController and SheepController would both inherit from StateController.
How should i declare WolfState?
The way i try to do it doesnt work.
public interface IState <T> where T : StateController
{
}
public abstract class State<T> where T : StateController, IState<T>
{
}
// THIS IS HOW I WOULD LIKE TO DO IT BUT ITS NOT ACCEPTED
public class WolfState : State<WolfController>
{
}
public class SheepState : State<SheepController>
{
}
It looks like you intend State<T> to implement IState<T>. You currently have a constraint on the type of T. Change the definition of State<T> to:
public abstract class State<T> : IState<T> where T : StateController
{
}
I'm currently learning C# and I'm coming from Java. In Java, I can do something like this:
public interface BasePresenterView<T extends BaseActivityPresenter> {}
public interface BaseActivityPresenter<T extends BasePresenterView> {}
and in C# I'm having a hard time achieving the same thing.
This is called generic constrained in .NET.
Your example in C# will look like this.
public interface BasePresenterView<T> where T: 'Interface or Type'{}
public interface BaseActivityPresenter<T> where T: 'Interface or Type' {}
More info: https://msdn.microsoft.com/en-us/library/d5x73970.aspx
EDIT: As already mentioned circular dependencies are not possible in C#, so you can not constraint them in the same way as in your example.
You want something like this if you are using abstract base classes...
public abstract class MyBasePresenter
{
}
public abstract class MyBasePresenter<TView> : MyBasePresenter
where TView : MyBaseView
{
}
public abstract class MyBaseView
{
}
public abstract class MyBaseView<TPresenter> : MyBaseView
where TPresenter : MyBasePresenter
{
}
public class MyView : MyBaseView<MyPresenter>
{
}
public class MyPresenter : MyBasePresenter<MyView>
{
}
... do this if you want interfaces ...
public interface IMyPresenter
{
}
public interface IMyPresenter<TView> : IMyPresenter
where TView : IMyView
{
}
public interface IMyView
{
}
public interface IMyView<TPresenter> : IMyView
where TPresenter : IMyPresenter
{
}
public class MyView : IMyView<MyPresenter>
{
}
public class MyPresenter : IMyPresenter<MyView>
{
}
... of you really want to go crazy you can even nest the generics ...
public class MyView : IMyView<IMyPresenter<IMyView>>
{
}
You can use generic type constraints, which restrict compile-time types using inheritance.
public interface BasePresenterView<T> where T : IBaseView
Because inheritance cannot be circular, you cannot have the circular dependancy as in the question.
However, if they declare the same methods or properties, they could inherit the same interface:
public interface BasePresenterView : IBaseView
public interface BaseActivityPresenter : IBaseView
I think I resolved it :)
public interface BaseActivityPresenter<T, K>
where T : BasePresenterView<T, K>
where K : BaseActivityPresenter<T, K> {}
public interface BasePresenterView<T, K>
where T : BasePresenterView<T, K>
where K : BaseActivityPresenter<T, K> {}
That seems to be working... for now. I don't know if this is a proper approach.
I have below class structure:
public class FirstContextController : AFirstContextController
public abstract class AFirstContextController : ABaseController<AFirstContextView>
public abstract class ABaseController<TView> where TView : ABaseView
public abstract class AFirstContextView : ABaseContextView
public abstract class ABaseContextView : ABaseView
public abstract class ABaseView : UserControl
Now I create an instance of my controller:
AFirstContextController firstContextController = new FirstContextController();
and need to return it from the method:
public static ABaseController<ABaseContextView> GetContextController( ... )
{
return firstContextController;
}
But then I get an error:
Cannot implicitly convert type 'AFirstContextController' to 'ABaseController'
So I tryied many casting (for example):
return (ABaseController<AFirstContextView>)firstContextController;
return (ABaseController<ABaseContextView>)( (ABaseController<AFirstContextView>)firstContextController );
But still get similar errors according to conversion.
So what conversion should I apply to return it ?
You need an interface derived from ABaseController<ABaseContextView> to make it work.
This has to do with variance and covariance. Reed more about it on MSDN: Covariance and Contravariance in Generics.
You will have to mark the ABaseContextView with out in your interface definition, so the signature should be something like IBaseController<out ABaseContextView>.
Example:
public abstract class ABaseController<TView> : IBaseController<TView> where TView : ABaseView
{
public void SomeMethod() { }
}
And the interface:
public interface IBaseController<out TView> where TView : ABaseView
{
void SomeMethod();
}
Then your current method should read:
public static IBaseController<ABaseContextView> GetContextController( ... )
{
return firstContextController;
}
Say I have the following three classes/interfaces:
public interface IImportViewModel
{
}
public class TestImportViewModel : IImportViewModel
{
}
public class ValidationResult<TViewModel> where TViewModel : IImportViewModel
{
}
As TestImportViewModel implements IImportViewModel, why will the following not compile?
ValidationResult<IImportViewModel> r = new ValidationResult<TestImportViewModel>();
I understand what the error message "Cannot implicitly convert type 'ValidationResult' to 'ValidationResult'" means. I just don't understand why this is the case. Would this not be covariance?
Would this not be covariance?
Yes, except that in C# 4.0 covariance works on interfaces only. So you will have to make your ValidationResult implement a covariant interface (one for which the generic parameter is defined as out):
public interface IImportViewModel
{
}
public class TestImportViewModel : IImportViewModel
{
}
public interface IValidationResult<out TViewModel> where TViewModel : IImportViewModel
{
}
public class ValidationResult<TViewModel> : IValidationResult<TViewModel> where TViewModel : IImportViewModel
{
}
and now you can do this:
IValidationResult<IImportViewModel> r = new ValidationResult<TestImportViewModel>();
I have an application that is structured as an service layer wich uses a repository layer for persistence.
I'm trying to create a generic controller class to reuse shared behavior but I'm having trouble trying to set the generic parameters. The following code:
public class BusinessEntity
{ }
public class Person : BusinessEntity
{ }
public interface IRepository<T> where T : BusinessEntity
{ }
public interface IService<T, R>
where T : BusinessEntity
where R : IRepository<T>
{ }
public partial interface IPersonRepository : IRepository<Person>
{ }
public interface IPersonService : IService<Person, IPersonRepository>
{ }
public abstract class BaseController<X, Y>
where X : BusinessEntity
where Y : IService<X, IRepository<X>>
{ }
public class PersonController : BaseController<Person, IPersonService>
{ }
fails at compilation with
The type ConsoleApplication.IPersonService cannot be used as type parameter Y in the generic type or method ConsoleApplication.BaseController<X,Y>. There is no implicit reference conversion from ConsoleApplication.IPersonService to ConsoleApplication.IService<ConsoleApplication.Person,ConsoleApplication.IRepository<ConsoleApplication.Person>>
this works
public interface IPersonService : IService<Person, IRepository<Person>>
but I lose the custom repository
There is a way to make the compiler realize IPersonRepository is an IRepository<Person>?
public class BusinessEntity
{ }
public class Person : BusinessEntity
{ }
public interface IRepository<T> where T : BusinessEntity
{ }
public interface IService<T, R>
where T : BusinessEntity
where R : IRepository<T>
{ }
public partial interface IPersonRepository : IRepository<Person>
{ }
public interface IPersonService : IService<Person, IPersonRepository>
{ }
public abstract class BaseController<X, Y, Z>
where X : BusinessEntity
where Y : IService<X, Z>
where Z : IRepository<X>
{ }
public class PersonController : BaseController<Person, IPersonService, IPersonRepository>
{ }
To address your comment:
IPersonService can extend the base service class to add custom facilities, like FindPersonsUnderAge(). For this it requires a custom repository. Actually LINQ avoids a lot of custom repository code, but sometimes they are required.
Couldn't IPersonService do that without requiring the repository type to be a type parameter? For example:
public interface IService<T> where T : BusinessEntity { }
public interface IPersonService : IService<Person>
{
IEnumerable<Person> FindPersonsByAge(double minAge, double maxAge);
}
public class Service<T, R> : IService<T>
where T : BusinessEntity
where R : IRepository<T>
{ }
public class PersonService : Service<Person, IPersonRepository>, IPersonService
{ }
Thanks to sll for pointing me in the right direction
public interface IService<T, out R>
where T : BusinessEntity
where R : IRepository<T>
{ }
does the trick