C# Generic Covariance - c#

I am getting error on DerivedFileInfo class declaration.
Error : The type 'StorageManager.Test.DerivedStorageProvider' cannot
be used as type parameter 'TStorageProvider' in the generic type or
method 'StorageManager.Test.BaseFileInfo'. There is no implicit
reference conversion from 'StorageManager.Test.DerivedStorageProvider'
to 'StorageManager.Test.IStorageProvider'.
Is there is something missing for generic constrains or I will require generic covariant?
public interface ICacheProvider
{
}
public class BaseCacheProvider : ICacheProvider
{
}
public class DerivedCacheProvider : BaseCacheProvider
{
}
public interface IStorageProvider<TCacheProvider> where TCacheProvider : ICacheProvider
{
}
public abstract class BaseStorageProvider<TCacheProvider> : IStorageProvider<TCacheProvider> where TCacheProvider : ICacheProvider
{
}
public class DerivedStorageProvider : BaseStorageProvider<DerivedCacheProvider>
{
}
public interface IResourceInfo<TStorageProvider> where TStorageProvider : IStorageProvider<ICacheProvider>
{
}
public abstract class ResourceInfo<TStorageProvider> : IResourceInfo<TStorageProvider>
where TStorageProvider : IStorageProvider<ICacheProvider>
{
}
public abstract class BaseFileInfo<TStorageProvider> : ResourceInfo<TStorageProvider> where TStorageProvider : IStorageProvider<ICacheProvider>
{
}
public class DerivedFileInfo : BaseFileInfo<DerivedStorageProvider>
{
}

Related

How to define Generic interfaces with multiple inheritance?

I have the following generic interface structure:
public interface IBase {}
public interface IBase<TBase> : IBase
where TBase : class, IBase
{
IEnumerable<IA<TBase>> Derived { get; }
}
public interface IA {}
public interface IA<TBase> : IA
where TBase : class, IBase
{
IEnumerable<IB<TBase, IA<TBase>>> DerivedOfA { get; }
}
public interface IB {}
public interface IB<TBase, TA> : IB
where TBase : class, IBase
where TA : class, IA<TBase>
{
IEnumerable<IC<TBase, TA, IB<TBase, TA>>> DerivedOfB { get; }
}
public interface IC {}
public interface IC<TBase, TA, TB> : IC
where TBase : class, IBase
where TA : class, IA<TBase>
where TB : class, IB<TBase, TA>
{
}
The purpose of this interface structure is to define a contract of type objects in the heirarcy structure.
When I try to define the following:
public class Base : IBase
{
public IEnumerable<IA<Base>> Derived { get; }
public class DerivedA : IA<Base>
{
public IEnumerable<IB<Base , IA<Base>>> DerivedOfA { get; }
public class DerivedB : IB<Base, DerivedA>
{
public IEnumerable<IC<Base, IA<Base>, IB<Base, IA<Base>>>> DerivedOfB { get; }
}
}
}
Everything compiles.
But when I define the 3rd nesting level (IC) compilation fails.
public class Base : IBase
{
public IEnumerable<IA<Base>> Derived { get; }
public class DerivedA : IA<Base>
{
public IEnumerable<IB<Base, IA<Base>>> DerivedOfA { get; }
public class DerivedB : IB<Base, DerivedA>
{
public IEnumerable<IC<Base, IA<Base>, IB<Base, IA<Base>>>> DerivedOfB { get; }
public class DerviedC: IC<Base, DerivedA, DerivedB>
{
}
}
}
(this creates a strict inheritance between the objects with the fact that the classes are nested).
When I write the code, the compiler complains on the following line
public class DerviedC: IC<Base, DerivedA, DerivedB> <-----
with the following error message
Error CS0311 The type 'Base.DerivedA.DerivedB' cannot be used as type parameter 'TB' in the
generic type or method 'IC<TBase, TA, TB>'. There is no implicit reference conversion from
'Base.DerivedA.DerivedB' to 'IB<Base, Base.DerivedA>'.
It seems the 2nd inheritance level of the interfaces is not supported. Why?
As Base.DerivedB inherits IB<Base, DerivedA> I would assume the compiler will no how to up-cast the object to its reference.
How can I resolve this?

Assign a sub class to an interface, with generics and covariant involved

I defined 3 interfaces:
public interface IManufacturerInput
{
}
public interface IManufacturerOutput
{
}
public interface IManufacturerApi<in S, out T>
where S : IManufacturerInput
where T : IManufacturerOutput
{
T Calculate(S);
}
And I defined a specific Manufacturer:
public class ManufacturerAInput : IManufacturerInput
{
}
public class ManufacturerAOutput : IManufacturerOutput
{
}
public class ManufacturerAApi : IManufacturerApi<ManufacturerAInput, ManufacturerAOutput>
{
public ManufacturerAOutput Calculate(ManufacturerAInput)
{
return null;
}
}
And In Main() I created a ManufacturerAApi, and try assign it to IManufacturerApi.
IManufacturerApi<IManufacturerInput, IManufacturerOutput> api = new ManufacturerAApi();
But it failed. The error message said (just abstract meaning):
Can't convert from ManufacturerAApi to IManufacturerApi<IManufacturerInput, IManufacturerOutput>
So is there any way I can make the assignment work? Thanks in advance.
What you are proposing isn't type safe. Let's change the names of your types to make the issue clearer:
public interface IPetFood { }
public interface IPetSound { }
public interface IPetCage<in S, out T>
where S : IPetFood
where T : IPetSound
{
T Feed(S s);
}
public class DogFood : IPetFood { }
public class CatFood : IPetFood { }
public class Bark : IPetSound { }
public class DogCage : IPetCage<DogFood, Bark>
{
public Bark Feed(DogFood input)
{
return new Bark();
}
}
And now suppose this is legal:
IPetCage<IPetFood, IPetSound> api = new DogCage();
Then we could do the following:
api.Feed(new CatFood()); //oops we've just given the dog some catfood.
The assignment will not work because S is contravariant, which means that any possible IPetFood passed into api.Feed would need to be a subtype of DogFood and you have the opposite; IPetFood is a superset of DogFood.

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'
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 { }

Conversion of generic types

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;
}

Implicit conversion en C# generic classes

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

Categories

Resources