简体   繁体   中英

Generic method with generic type problem

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM