2009-10-15 9 views
0

Je suis nouveau à la mise en œuvre et Generics ont besoin d'intrants sur 2 questions que je face à:Comment écrire une classe générique qui implémente une interface ayant des méthodes génériques avec des contraintes définies

J'ai une interface ICommand définie comme:

public ICommand 
{ 
    List<T> Execute<T>() where T : IValidationResult; 
    IDomain<T> GetDO<T>() where T : IValidationResult; 

} 

intentionnellement je l'ai comme non-générique car je dois ajouter une collection de commandes différentes.

Cette interface Je souhaite mettre en œuvre une classe générique appelée PersistCommand comme:

public PersistCommand<TDomainObj,T> : ICommand where TDomainObj : IDomain<T> where T : IValidationResult 
{ 
     private TDomainObj _domainObject;//IDomain<T> is an interface 

     public PersistCommand(TDomainObj TDomainObject) 
     { 
      _domainObject = TDomainObject; 

     } 

     public IDomain<T> GetDO<T>() where T : IValidationResult 
     { 
      return _domainObject as IDomain<T>; 
     } 

     public List<T> Execute<T>() where T : IValidationResult 
     { 
      //returns a list<T> 
     } 
} 

L'intention d'avoir une classe générique est de transmettre ces contraintes de la classe à ces méthodes génériques qui ne se produit malheureusement pas (Je ne sais pas pourquoi?) Quand je compile, j'obtiens un avertissement: Le paramètre Type 'T' a le même nom que le paramètre Type du type externe 'PersistCommand' Ceci est le premier numéro

Le deuxième numéro est: ensemble différent de commandes, InsertCommand, DeleteCommand et UpdateCommand qui héritent de PersistCommand et fonctionne correctement lorsque la méthode Execute() est appelée individuellement.

I ont une classe de commandes qui est utilisé pour l'exécution de plusieurs commandes comme indiqué:

public class CommandManager 
    { 

     public virtual IEnumerable<T> Perform<T>(List<ICommand> commandList) 
      where T : IValidationResult 
     { 
      List<T> validationResults = new List<T>(); 

      //fire pre-intent validations 
      foreach (ICommand command in commandList) 
      { 
       validationResults.AddRange(command.GetDomainObject<T>().Validate(command.GetPreIntent())); 
      } 

      //fire intent validations 
      if (validationResults.Count == 0) 
      { 
       foreach (ICommand command in commandList) 
       { 
        validationResults.AddRange(command.Execute<T>()); 
       } 
      } 

      //fire post-intent validations 
      if (validationResults.Count == 0) 
      { 
       foreach (ICommand command in commandList) 
       { 
        validationResults.AddRange(command.GetDomainObject<T>().Validate(command.GetPostIntent())); 
       } 
      } 

      return validationResults; 
     } 

    } 

Tant que le type « T » qui est transmis aux commandes et la méthode de CommandManager.Perform sont les mêmes, Ça marche. Mais j'ai un scénario où nous avons 2 objets de domaine ayant différents types « T » comme:

class Project : IDomain<CustomResult>//CustomResult implements IValidationResult 
class Resource : IDomain<AnotherResult>//AnotherResult implements IValidationResult 

Quand j'appelle CommandManager.Perform (commandList), il jette une exception à

méthode GetDO montrant un message : référence d'objet non définie à une instance de l'objet »

Toute aide ou idées pour résoudre ce serait apprécié

Répondre

2

les premiers problèmes provient de l'unité d'organisation fait déclaré le type générique T i. n les définitions de méthode, plutôt que la définition d'interface globale.

Donc, pour obtenir de vous compiler faudrait modifier l'interface et les définitions de classe à:

public interface ICommand<T> where T : IValidationResult 
{ 
    List<T> Execute(); 
    IDomain<T> GetDO(); 
} 

public class PersistCommand<TDomainObj,T> : ICommand<T> where TDomainObj : IDomain<T> where T : IValidationResult<T> 
{ 
    private TDomainObj _domainObject;//IDomain<T> is an interface 

    public PersistCommand(TDomainObj TDomainObject) 
    { 
     _domainObject = TDomainObject; 
    } 

    public IDomain<T> GetDO() 
    { 
     return _domainObject as IDomain<T>; 
    } 

    public List<T> Execute() 
    { 
     //returns a list<T> 
    } 
} 

Vous pouvez également déclarer les méthodes de la classe mise en œuvre en utilisant un type générique nommé autre chose que T , mais cela ajoute simplement plus de complexité. Cependant, je pense que tout cela est éclipsé par le problème plus large - vous semblez essayer d'utiliser des génériques comme des interfaces.

De l'exemple de code à condition qu'il ne semble pas que vous devez nécessairement utiliser les génériques autant que vous êtes et que cela ne compliquer

Je simplifiez vers le bas:

public interface ICommand 
{ 
    List<IValidationResult> Execute(); 
    IDomain<IValidationResult> GetDO(); 
} 

public class PersistCommand<TDomainObj> : ICommand where TDomainObj : IDomain<IValidationResult> 
{ 
    private TDomainObj _domainObject;// 

    public PersistCommand(TDomainObj TDomainObject) 
    { 
     _domainObject = TDomainObject; 

    } 

    public IDomain<IValidationResult> GetDO() 
    { 
     return _domainObject; 
    } 

    public List<IValidationResult> Execute() 
    { 
     //returns a list<T> 
    } 
} 

public class CommandManager 
{ 

    public virtual IEnumerable<IValidationResult> Perform(List<ICommand> commandList) 
    { 
     List<IValidationResult> validationResults = new List<IValidationResult>(); 

     //fire pre-intent validations 
     foreach (ICommand command in commandList) 
     { 
      validationResults.AddRange(command.GetDO().Validate(command.GetPreIntent())); 
     } 

     //fire intent validations 
     if (validationResults.Count == 0) 
     { 
      foreach (ICommand command in commandList) 
      { 
       validationResults.AddRange(command.Execute()); 
      } 
     } 

     //fire post-intent validations 
     if (validationResults.Count == 0) 
     { 
      foreach (ICommand command in commandList) 
      { 
       validationResults.AddRange(command.GetDO().Validate(command.GetPostIntent())); 
      } 
     } 

     return validationResults; 
    } 

} 

De cette façon, vous traitez tout de même (c'est-à-dire par interface) ce qui ressemble à ce que vous essayez de faire.La seule chose pour laquelle vous utilisez des génériques est de spécifier le type particulier de l'objet de domaine dans PersistCommand, dont je suppose que vous pourriez avoir besoin pour une utilisation interne ou un sous-classement.

+0

Salut Dave, J'ai essayé, mais il ne compile pas et le message d'erreur: PersistCommand ICommand.GetDO 'ne pas mettre en œuvre un membre d'interface'() – Chandrasekhar

+0

je l'ai mis à jour Ma réponse pour couvrir cela, c'est à voir avec la façon dont vous déclarez votre interface. J'ai aussi ajouté des éléments pour simplifier le tout ... HTH –

Questions connexes