How to write a generic class that implements an interface having generic methods with constraints defined
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 t开发者_开发问答he 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.
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.
精彩评论