I have problem with constraints on generic method. Here is code for all classes:
namespace Sdk.BusinessObjects
{
public interface IBusinessObject
{
}
}
namespace Sdk.BusinessObjects
{
[DataContract]
public class AccountDetails : IBusinessObject
{
[DataMember]
public virtual Guid AccountId { get; set; }
// More properties...
}
}
namespace Sdk.BusinessLogic
{
public interface IManager<T> where T : IBusinessObject
{
T Add(T businessObject);
void Delete(T businessObject);
IList<T> ListAll();
}
}
namespace Sdk.BusinessLogic
{
public interface IAccountManager : IManager<AccountDetails>
{
void ChangeAccountState(Guid accountId, string state);
}
}
namespace Sdk.BusinessLogic
{
public interface IManagerFactory
{
T Create<T>() where T : IManager<IBusinessObject>;
}
public class ManagerFactory : IManagerFactory
{
public T Create<T>() where T : IManager<IBusinessObject>
{
// resolve with Unity and return
}
}
}
So, I have main IBusinessObject interface for all business objects (like AccountDetails) and IManager as generic manager interface for business objects. I wanted to create factory for these managers with constraints. When I try something like this in UnitTest:
IManagerFactory factory = new ManagerFactory();
factory.Create<IAccountManager>();
I get error:
The type 'Sdk.BusinessLogic.IAccountManager' cannot be used as type parameter 'T' in the generic type or method 'Sdk.BusinessLogic.IManagerFactory.Create()'. There is no implicit reference conversion from 'Sdk.BusinessLogic.IAccountManager' to 'Sdk.BusinessLogic.IManager'.
How can this be done?
Basically your problem is that IManager<T> is invariant, and has to be as you've got values coming out of the API and values going into it. So an IAccountManager isn't an IManager<IBusinessObject>, because otherwise you could write:
IAccountManager m1 = new SomeImplementation();
IManager<IBusinessObject> m2 = m1;
m2.Add(new SomeArbitraryBusinessObject());
An account manager is only meant to manage accounts, not just any business object.
One option is to use two generic type parameters instead of one for ManagerFactory.Create:
public TManager Create<TManager,TObjectType>()
where TManager : IManager<TObjectType>
where TObjectType : IBusinessObject
Related
I have the following class relationships
public interface ICapability
{
}
public interface IBaseService<T> where T : ICapability
{
}
public abstract class BaseService<out T> : IBaseService<T> where T : ICapability
{
// modified...
T MapEventToCapability(dynamic eventData, T capability);
}
public class SomeCapability : ICapability
{
}
public partial class Service1 : BaseService<SomeCapability>
{
public Service1()
{
}
}
public class ServiceResolver
{
public void Register(BaseService<ICapability> serviceToRegister)
{
}
}
I try to invoke the Register method, passing in a new service1 as shown:
var b = new ServiceResolver();
var c = new Service1();
b.Register(c);
However I get a compile time error on c in the call to Register as follows;
Cannot convert Service1 to BaseService<ICapability>
I assumed that because Service1 is of type BaseService and that since SomeCapability is of type ICapability that this wouldn't be an issue.
I tried casting to BaseService as well I tried changing the input parameter on Register to be an IBaseService and again casting but then I get a runtime error.
Note the question has been updated since this answer was posted - covariance is no longer an option having added a method which is incompatible
You'll need to do 2 things to make this work
Make Register take IBaseService<ICapability> not BaseService<ICapability>
Make IBaseService covariant by marking the generic type as out - this is the same as the reason you can pass a List<Foo> to a merthod which expects an IEnumerable<Foo> as IEnumerable<T> is covariant in a similar manner.
public class ServiceResolver
{
public void Register(IBaseService<ICapability> serviceToRegister)
{
}
}
and
public interface IBaseService<out T>
where T : ICapability
{
}
Live example: https://dotnetfiddle.net/O0yXa5
I am trying to return an implementation of generic abstract class using a factory, so that the caller doesn't need to know what the concrete type returned. But failed.
The entity classes
public class Car:IMoveable { }
public interface IMoveable { }
The service classes
public abstract class Service<TEntity>
{
public abstract void PerformService(TEntity t);
}
public class VehicleService : Service<IMoveable>
{
public override void PerformService(IMoveable t) { }
}
public class DefaultService : Service<object>
{
public override void PerformService(object t){ }
}
The factory:
public static class ServiceFactory
{
public static Service<TEntity> CreateService<TEntity>(TEntity entity) where TEntity : class
{
if (typeof(IMoveable).IsAssignableFrom(typeof(TEntity)))
{
// run time error here as returns null
return new VehicleService() as Service<TEntity>;
//compiler error
return (Service<TEntity>) new VehicleService();
}
else
{
return new DefaultService() as Service<TEntity>;
}
}
}
The calling code
static void Main(string[] args)
{
var car = new Car();
var service = ServiceFactory.CreateService(car);
}
The problem is the service after createService is always null.
I suspect the problem is TEntity is passed as Car, whereas the VehicleService is implemented as IMovebale. But just can't get my head around how to do it, or is it even possible?
Thanks in advance.
You need to mark TEntity generic type of Service as contravariant via in keyword, and use base interface instead of base abstract class, then cast to generic base type will work:
public interface Service<in TEntity>
{
void PerformService(TEntity t);
}
I have completely re-written this hoping to make my question clearer. I have chosen the concept of services making use of repositories in my example code.
Example code:
class Program
{
interface IEntity
{
int Id { get; set; }
}
// Example entity could be:
class Book : IEntity
{
public int Id { get; set; }
}
class Magazine : IEntity
{
public int Id { get; set; }
}
interface IRepository<TEntity> where TEntity : class, IEntity
{
IEnumerable<TEntity> GetEntities();
}
interface IBooksRepository : IRepository<Book> { }
interface IMagazineRepository : IRepository<Magazine> { }
class DataStore<TEntity> where TEntity: class, IEntity
{
public IEnumerable<TEntity> GetFromStore()
{
throw new NotImplementedException();
}
}
abstract class RepositoryBase<TEntity> : IRepository<TEntity>
where TEntity : class, IEntity
{
DataStore<TEntity> _dataStore;
public RepositoryBase()
{
_dataStore = new DataStore<TEntity>();
}
public IEnumerable<TEntity> GetEntities()
{
return _dataStore.GetFromStore();
}
}
class BookRepository : RepositoryBase<Book>, IBooksRepository { }
class MagazineRepository : RepositoryBase<Magazine>, IMagazineRepository { }
abstract class ServiceBase<IEntityRepository, TEntity>
where IEntityRepository : IRepository<TEntity>
where TEntity : class, IEntity
{
IEntityRepository _repository;
public ServiceBase(IEntityRepository repository)
{
_repository = repository;
}
public IEnumerable<TEntity> GetEntitiesFromRepository()
{
return new List<TEntity>();
}
}
class BookService : ServiceBase<IBooksRepository, Book>
{
public BookService(IBooksRepository bookRepository)
: base(bookRepository)
{ }
}
class MagazineService : ServiceBase<IMagazineRepository, Magazine>
{
public MagazineService(IMagazineRepository magazineRepository)
: base(magazineRepository)
{ }
}
static void Main(string[] args)
{
var aBookService = new BookService(new BookRepository());
var aMagazineService = new MagazineService(new MagazineRepository());
var books = aBookService.GetEntitiesFromRepository();
var magazines = aMagazineService.GetEntitiesFromRepository();
}
}
This all works fine and perhaps it is valid to ask why I want to change this. Mainly I am just curious if I can make this more neat. It is more a point of curiosity that one of functional correctness I suppose.
Both IBookRepository and IMagazineRepository know which concreate type they represent 'Book' and 'Magazine'
When I define my concreate services: BookService and MagazineService I have to specify the type as well as the interface:
class BookService : ServiceBase<IBooksRepository, Book>{}
class MagazineService : ServiceBase<IMagazineRepository, Magazine>{}
I wondered if I could simplify thier signatures as the Interfaces already know The type I am expecting Book or Magazine.
Can I extract the Entity Type from the inteface such that I no longer need to specify the type when creating concreate service types?
As I pondered this, I discovered a deeper issue with my knowledge of C#:
What exactly is the type of 'thing' that the generic system is looking for between those angle brackets: IEnumerable<TThisThing>.
When I look at intellisense is says T is the type of objects to enumerate.
So as an experiment I grabbed the type of MyType:
Type typeOfMyType = instanceOfMyType.GetType();
IEnumerable<typeOfMyType> enumerable = new List<typeOfMyType>(); //crude example.
Now of course this does not work. So what kind of thing is TThisThing that works between the angle brackets?
is there a way of extracting this information so that I can forgo the
inclusion of 'MyType' in the class definition and use the discovered
TMyType in the example method?
Yes, you simply need to define the generic type parameter in the methods name:
public IEnumerable<TMyType> GetMyTypes<TMyType>()
{
// get list of TMyType instances;
return list;
}
If you don't want to use a generic type parameter at all, you'll have to defer to reflection, and you won't be able to use a compile-time generic type such as returning an IEnumerable<T>.
So what kind of thing is TThisThing that works between the angle
brackets?
TThisThing should be a compile-time known type parameter. When you use Type typeOfMyType = instanceOfMyType.GetType();, the type of instanceOfMyType is only known at run-time.
For example:
var obj = new SomeClass<Foo>();
IEnumerable<Bar> bars = obj.GetMyTypes<Bar>();
Where Foo and Bar:
public class Foo { }
public class Bar { }
I'm with problems to convert from the type derived to base type using Generics.
Class to manage the dictionary:
public class ManagerDictionary<TContext>
{
public ManagerDictionary()
{
this.Dictionary = new Dictionary<int, TContext>();
}
public IDictionary<int, TContext> Dictionary { get; private set; }
public void Register<TSubContext>(int context, TSubContext subContext) where TSubContext : TContext
{
this.Dictionary[context] = subContext;
}
}
Interface of the Process context:
public interface IProcessContext : IContext<ProcessViewModel>
{
}
My test class:
public class Foo<TViewModelContext> where TViewModelContext : ViewModeBase
{
public Foo(IProcessContext processContext)
{
// Create de Dictionary Manager.
this.ManagerDictionary = new ManagerDictionary<IContext<TViewModelContext>>();
// Register the process context on dictionary.
// The error is occurring here: The is no implicit reference conversion from 'IProcessContext' to 'IContext<TViewModelContext>'
this.ManagerDictionary.Register((int)ContextType.Process, processContext);
}
protected ManagerDictionary<IContext<TViewModelContext>> ManagerDictionary { get; set; }
}
When I try register the processContext, the problem occurs:
The is no implicit reference conversion from 'IProcessContext' to
IContext<TViewModelContext>
How can I resolve this problem?
Edit:
When I Create a inherited class of the Foo, I can register, but I need register on Foo class too.
public interface IAnotherProcessContext : IContext<ProcessTwoViewModel>
{
}
public class InheritedFoo : Foo<ProcessTwoViewModel>
{
public InheritedFoo(IAnotherProcessContext anotherProcessContext)
{
base.ManagerDictionary.Register((int)ContextType.InheritedProcess, anotherProcessContext);
}
}
You're trying to treat IContext<T> as if it's covariant with respect to T, but that interface isn't defined as being covariant.
Either make the interface be covariant, or alter your program such that you never expect an IContext<Child> to be implicitly convertible to an IContext<Parent>.
Given the following
public class Service<T> : IService<T>
{
Repository<T> _repository = new Repository<T>();
public T Get<T>(int id)
{
return _repository.Get<T>(id);
}
}
public interface IService<T>
{
T Get<T>(int id);
}
I get the following warning
Type parameter 'T' has the same name
as the type parameter from outer type
'Services.IService'
I am not sure what the issue is with this, why does it care if my return type is the same as the type I am telling the class to be. Am I missing something here?
You can leave out the <T> in the declaration of Get methods. You are not introducing a new Type Parameter for the Get method which <T> says. The fact that you return a T is enough.
I think this will work:
public class Service<T> : IService<T>
{
Repository<T> _repository = new Repository<T>();
public T Get(int id)
{
return _repository.Get(id);
}
}
public interface IService<T>
{
T Get(int id);
}
You can create a generic method in both generic and non-generic classes.
public class Foo
{
public T Get<T>(int a)
{
}
}
You could also do this in a generic class, but over a different type.
public class Foo<T>
{
public S Get<S>(int a)
{
}
}
You'd want this instead:
public class Service<T> : IService<T>
{
Repository<T> _repository = new Repository<T>();
public T Get(int id)
{
return _repository.Get<T>(id);
}
}
public interface IService<T>
{
T Get(int id);
}
Basically in you're code you're trying to define Get<T>(). When you put that generic definition, you're saying it's specific to that method, not the whole class.