views:

274

answers:

1

I'm new to Generics implementation and need inputs on 2 issues I face :

I have an interface ICommand defined as :

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

}

intentionally I have it as non-generic as I have to add a collection of different commands.

This interface I wish to implement in a generic class called PersistCommand as :

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

The intention behind having a generic class is to pass these constraints from the class to these generic methods which unfortunately doesn't happen(Not sure why? when compiled ,I get a warning:Type parameter 'T' has the same name as the type parameter from outer type 'PersistCommand' This is the first issue.

The second issue is : I have different set of commands,InsertCommand,DeleteCommand and UpdateCommand that inherit from the PersistCommand and work fine when the Execute() method is called individually.

I have a CommandManager class that is used for execution of multiple commands as shown :

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

    }

As long as the type "T" that is passed to the Commands and the CommandManager.Perform method are same,it works. But I have a scenario wherein we have 2 Domain objects having different "T" types as :

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

When I call CommandManager.Perform(commandList) it throws an exception at

GetDO method showing a message:Object reference not set to an instance of the object"

Any help or ideas to solve this would be appreciated.

+2  A: 

The first problems stems from the fact ou declared the generic type T in the method definitions, rather than the overall interface definition.

So to get it to compile you'd have to change the interface and class definitions to:

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

You could also declare the methods in the implementing class as using a generic type named something other than T, but that just adds more complexity.

However, I think that's all eclipsed by the wider problem - you seem to be trying to use generics like interfaces.

From the code sample provided it doesn't look like you necessarily need to use generics as much as you are and that it's only complicating matters

I'd try simplifying it down:

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

}

That way you're treating everything the same (i.e. by interface) which is what it looks like you're trying to do. The only thing you're using generics for is to specify the particular type of the domain object in PersistCommand, which I presume you may need for internal use or sub-classing.

Dave
Hi Dave,I tried that but it doesn't compile and get the error message:PersistCommand<TDomainObject,TValidationResult>' does not implement interface member 'ICommand.GetDO<TValidationResult>()
Chandrasekhar
I've updated my answer to cover that, it's to do with how you're declaring your interface. I've also added bits about simplifying the whole thing... HTH
Dave