如何编写实现具有定义约束的泛型方法的接口的泛型类

时间:2009-10-15 13:36:27

标签: .net generics c#-3.0

我是Generics实施的新手,需要对我面临的两个问题提出意见:

我有一个接口ICommand定义为:

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

}

故意我把它作为非泛型的,因为我必须添加一组不同的命令。

这个接口我希望在一个名为PersistCommand的泛型类中实现为:

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

拥有泛型类的目的是将这些约束从类传递给这些泛型方法,遗憾的是不会发生这种情况(不确定为什么?编译时,我收到警告:类型参数'T'具有相同的名称作为外部类型'PersistCommand'的类型参数这是第一个问题。

第二个问题是: 我有不同的命令集,InsertCommand,DeleteCommand和UpdateCommand继承自PersistCommand,并且在单独调用Execute()方法时工作正常。

我有一个CommandManager类,用于执行多个命令,如下所示:

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

    }

只要传递给Commands的类型“T”和CommandManager.Perform方法相同,它就可以工作。 但我有一个场景,其中我们有两个具有不同“T”类型的域对象:

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

当我调用CommandManager.Perform(commandList)时,它会在

处抛出异常

GetDO方法显示消息:对象引用未设置为对象的实例“

任何有助于解决此问题的帮助或想法都将受到赞赏。

1 个答案:

答案 0 :(得分:2)

第一个问题源于您在方法定义中声明泛型类型T而不是整体接口定义。

因此要进行编译,您必须将接口和类定义更改为:

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

您还可以将实现类中的方法声明为使用名称不是T的泛型类型,但这只会增加更多的复杂性。

然而,我认为这一切都被更广泛的问题所黯然失色 - 你似乎试图使用像接口这样的泛型。

从提供的代码示例来看,您看起来并不一定需要像使用泛型一样使用泛型,而且只会使问题复杂化

我会尝试简化它:

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

}

这样你就可以对待所有相同的东西(即通过界面),这就是你想要做的事情。您使用泛型的唯一方法是在PersistCommand中指定域对象的特定类型,我认为您可能需要内部使用或子类。