2010-10-22 3 views
0

J'ai un problème avec les contraintes sur la méthode générique. Voici le code pour toutes les classes:Méthode générique avec problème de type générique

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

Donc, j'ai principale interface IBusinessObject pour tous les objets métier (comme AccountDetails) et imanager comme interface de gestion générique pour les objets métier. Je voulais créer une usine pour ces managers avec des contraintes. Lorsque je tente quelque chose comme ça dans UnitTest:

IManagerFactory factory = new ManagerFactory(); 
factory.Create<IAccountManager>(); 

J'obtiens l'erreur: Le type « Sdk.BusinessLogic.IAccountManager » ne peut pas être utilisé comme paramètre de type « T » dans le type générique ou méthode « Sdk.BusinessLogic. IManagerFactory.Create() '. Il n'y a pas de conversion de référence implicite de 'Sdk.BusinessLogic.IAccountManager' à 'Sdk.BusinessLogic.IManager'.

Comment cela peut-il être fait?

Répondre

1

Fondamentalement, votre problème est que IManager<T> est invariant, et doit être comme vous avez des valeurs qui sortent de l'API et les valeurs qui s'y trouvent. Ainsi, un IAccountManagern'est pas un IManager<IBusinessObject>, parce que sinon vous pourriez écrire:

IAccountManager m1 = new SomeImplementation(); 
IManager<IBusinessObject> m2 = m1; 
m2.Add(new SomeArbitraryBusinessObject()); 

Un gestionnaire de compte est destiné uniquement à gérer les comptes , pas seulement un objet métier.

Une option consiste à utiliser deux paramètres de type générique au lieu d'un pour ManagerFactory.Create:

public TManager Create<TManager,TObjectType>() 
    where TManager : IManager<TObjectType> 
    where TObjectType : IBusinessObject 
+0

Oui, vous avez raison, mais je n'aime pas la mise en œuvre avec deux paramètres de type générique, car il n'y a pas de sens d'essayer pour résoudre IAccountManager avec une autre implémentation de IBusinessObject. Je pense que la meilleure solution est de supprimer la contrainte pour le paramètre de type générique et de l'appliquer dans l'implémentation: T Create (); – IvanQ

+0

@IvanQ: Cela dépend si vous voulez avoir la sécurité à la compilation, en gros. Une autre alternative consiste à créer une interface non générique 'IManager' dont dérive' IManager '. –

+0

Oui, je pensais à IManager non générique, mais il serait vide donc j'ai rejeté cette idée pour le moment. – IvanQ