通用接口实现:通用返回类型和通用输入类型

时间:2016-06-15 13:05:09

标签: c# generics

我想创建类系统来概括从某些数据源获取和发布数据。我有两个方法:GetData和PostData。两种方法都应该有某种输入,GetData也应该有返回类型。我试着编写通用接口并在" DatabaseSource"中实现它。类:

public class QueryParameter
{
    public QueryParameter()
    {
        this.Direction = ParameterDirection.Input;
    }

    public string Name { get; set; }
    public object Value { get; set; }
    public ParameterDirection Direction { get; set; }
}

public class InputBase
{
    public InputBase()
    {
        ResultMapping = new Dictionary<string, string>();
        Parameters = new List<QueryParameter>();
    }
    public Dictionary<string, string> ResultMapping { get; set; }
    public List<QueryParameter> Parameters { get; set; }
}
public class DatabaseInput: InputBase
    {
        public string Query { get; set; }
        public DatabaseCommandType CommandType { get; set; }
    }

public interface IDataSource<I> where I: InputBase
{
    IEnumerable<T> GetData<T>(I input);
    void PostData(I Input);
} 

现在我尝试实现这样的界面:

public class DatabaseDataSource: IDataSource<DatabaseInput>
{
    public IEnumerable<T> GetData<T>(DatabaseInput Input)
    {
        //implementation
    }

    public void PostData(DatabaseInput Input)
    {
        //implementation
    }
}

但是当我尝试像这样实例化数据源时,我遇到了问题:

IDataSource<InputBase> dataSource = new DatabaseDataSource();

我不能使用DatabaseInput,因为这段代码是某种工厂方法,我应该可以实例化其他IDataSource实现。

简而言之,我希望将Input和Output作为泛型类型,并使用约束Input来匹配具体的IDataSource实现。

3 个答案:

答案 0 :(得分:3)

如果我正确地理解了这一点(并且我无法100%清楚地了解这一点),那么您的数据源需要定义输入输出。如果你这样做:

public IEnumerable<T> GetData<T>(DatabaseInput Input)

然后,根据T的内容,该方法的实际实现可能会有很大差异。您不希望使用该方法的方案来检查T的类型并相应地分支您的代码。

也许你想要的是这样的:

public interface IDataSource<TInput, TOutput> where TInput: InputBase
{
    IEnumerable<TOutput> GetData(TOutput input);
    void PostData(TInput Input);
} 

但即便如此,你还是有一个界面,它定义了两个看似无关的操作。 (TInput似乎不太可能既可以用作检索数据的查询,也可以用作发布/修改数据的命令。)

所以也许你可以进一步分解它:

public interface IDataSource<TInput, TOutput> where TInput: InputBase
{
    IEnumerable<TOutput> GetData(TOutput input);
}

public interface IDataCommand<TInput> where TInput:InputBase
{
    void PostData(TInput Input);
}

要实例化它,您可以使用抽象工厂:

public interface IDataSourceFactory<TInput, TOutput>
{
    IDataSource<TInput, TOutput> Create();
    void Release(IDataSource<TInput, TOutput> created);
}

之所以这样,是因为它避免了你的类需要调用var dataSource = new [whatever].如果你这样做,那么它在某种程度上违背了实现接口的目的。无论您实现什么接口,只要您明确调用new并创建特定类型,您的类就会耦合到类型,而不是接口。

这改变了原来的问题。抽象工厂的实施是什么?好消息是,依赖工厂的班级并不关心工厂的实施情况。但你仍然需要一个。一种方法是使用DI容器。温莎很有帮助,因为它提供了创建抽象工厂的模式。 This blog post描述了如何更详细地执行此操作。

答案 1 :(得分:1)

答案 2 :(得分:0)

您可以使用where约束,单击here以获取有关约束的更多信息。

where T : class //The type argument must be a reference type; this applies also to any class, interface, delegate, or array type.

where T : <interface name> //The type argument must be or implement the specified interface. Multiple interface constraints can be specified. The constraining interface can also be generic.