在工厂中使用通用接口时推断类型

时间:2011-11-13 03:07:10

标签: c# generics design-patterns factory-pattern

我的同事和我正在为在线店面整理一个小型报告框架。我们在存储库模式之后构建了一个库,使用“reports”作为存储库,并使用一个非常轻的服务层来与所述报告进行交互。我们的代码非常好用,并且非常易于使用。但是,有一件事情困扰着我个人:在服务工厂层面,我们需要两次声明我们的返回类型(它没有被推断)。这是我们项目的基础:

报告界面

这就是我们的“存储库”。它们接受数据访问对象,例如SQL / Oracle连接的包装类,或者我们的Storefront API。

internal interface IReport<T>
{
    T GetReportData(dynamic options);
}

存储库工厂

这提供了一种通过了解类型生成这些报告的简便方法。

internal interface IReportFactory
{
    TR GenerateNewReport<T, TR>() where TR : IReport<T>;
}

internal class ReportFactory : IReportFactory
{
    public ReportFactory()
    {
        // some initialization stuff
    }

    public TR GenerateNewReport<T, TR>() where TR : IReport<T>
    {
        try
        {
            return (TR)Activator.CreateInstance(typeof(TR));
        }
        catch(Exception ex)
        {
            // Logging
        }
    }
}

示例报告(存储库)

以下是报告的样子。请注意,它返回一个DataTable,并在通用接口中声明它(这将很快出现)。

internal class ItemReport : IReport<DataTable>
{
    public DataTable GetReportData(dynamic options)
    {
        return new DataTable();
    }
}

报告服务

一种轻量级服务,它接收报告(存储库)并使用它。轻巧,但它允许简单的单元测试等,如果我们想要在检索报告后添加额外的处理,那么这样做很容易。

public interface IReportService<T>
{
    T GetReportData(dynamic options);
}

public class ReportService<T> : IReportService<T>
{
    private readonly IReport<T> _report;

    public ReportService(IReport<T> report)
    {
        _report = report;
    }

    public T GetReportData(dynamic options)
    {
        return _report.GetReportData(options);
    }
}

服务工厂

我们将服务工厂设置为抽象类(因为所有服务工厂都需要创建报告工厂),强制使用默认构造函数:

public abstract class ReportServiceFactory
{
    protected IReportFactory ReportFactory;

    protected ReportServiceFactory(connection strings and other stuff)
    {
        ReportFactory = new ReportFactory(connection strings and other stuff);
    }
}

然后我们可以根据功能创建单独的服务工厂。例如,我们有一个“标准报告”服务工厂,以及客户特定的服务工厂。这里的实现是我的问题所在。

public class SpecificUserServiceFactory : ReportServiceFactory
{
    public SpecificUserServiceFactory(connection strings and other stuff) : base(connection strings and other stuff){}

    public IReport<DataTable> GetItemReport()
    {
        return new ReportService<DataTable>(ReportFactory.GenerateNewReport<DataTable, ItemReport>());
    }
}

为什么创建服务工厂时我必须如此冗长?我两次声明返回类型。为什么我不能这样做:

return new ReportService(ReportFactory.GenerateNewReport<ItemReport>());

请注意,我没有在此声明DataTable;我认为应该从ItemReport是IReport的事实推断出来。任何关于如何以这种方式工作的建议都将不胜感激。

很抱歉这么长的解释要问这么简单的问题,但我认为所有支持代码都有助于提出解决方案。再次感谢!

2 个答案:

答案 0 :(得分:1)

我认为编译器无法从DataTable推断ItemReport。但是,您可以通过在非泛型类中使用静态泛型方法来避免两次指定DataTable

ReportService.Create(reportFactory.GenerateNewReport<DataTable, ItemReport>())
public static class ReportService
{
    public static ReportService<T> Create<T>(IReport<T> report)
    {
        return new ReportService<T>(report);
    }
}

答案 1 :(得分:1)

在调用GenerateNewReport时无法省略DataTable泛型类型的原因是因为它是该函数定义中其他泛型类型的约束。让我们假设(为简单起见)您的IReport&lt; T&gt;实际上是一个带有函数void Something(T输入)的接口。一个类可以实现IReport&lt; int&gt;和IReport&lt; string&gt;。然后,我们构建了一个名为Foo的类:IReport&lt; int&gt ;, IReport&lt; string&gt;。编译器将无法编译bar.GenerateNewReport&lt; Foo&gt;因为它不知道它是否绑定到IReport&lt; int&gt;或IReport&lt; string&gt;类型,因此无法确定该调用的适当返回类型。