1

我有这个单例工厂类示例,我在其中使用字典并在运行时确定相关的报表类。因为根据每个报表服务还必须执行的操作,最多有 40 种组合。

public sealed class ReportServiceFactory : IGetReportServices
{
    private static readonly ReportServiceFactory instance = new ReportServiceFactory();

    private static readonly Dictionary<Tuple<ReportTypes, ReportPeriodTypes>, IServiceReports> reportEntityServices =
        new Dictionary<Tuple<ReportTypes, ReportPeriodTypes>, IServiceReports> {
            { Tuple.Create(ReportTypes.Foo, ReportPeriodTypes.Week), new FooService<FooWeekEntity>() },
            { Tuple.Create(ReportTypes.Foo, ReportPeriodTypes.Month), new FooService<FooMonthEntity>() },

            { Tuple.Create(ReportTypes.Bar, ReportPeriodTypes.Week), new BarService<BarWeekEntity>() },
            { Tuple.Create(ReportTypes.Bar, ReportPeriodTypes.Month), new BarService<BarMonthEntity>() }
        };

    // Explicit static constructor to tell C# compiler not to mark type as beforefieldinit
    static ReportServiceFactory()
    {
    }

    private ReportServiceFactory()
    {
    }

    public static ReportServiceFactory Instance
    {
        get { return instance; }
    }

    public IServiceReports Get(ReportTypes reportType, ReportPeriodTypes periodType)
    {
        if (!Enum.IsDefined(typeof(ReportTypes), reportType))
            throw new ArgumentOutOfRangeException("reportType");
        if (!Enum.IsDefined(typeof(ReportPeriodTypes), periodType))
            throw new ArgumentOutOfRangeException("periodType");

        var reportTuple = Tuple.Create(reportType, periodType);

        if (!reportEntityServices.ContainsKey(reportTuple))
            return null;

        return reportEntityServices[reportTuple];
    }
}

我希望能够为Get()工厂上的函数返回一个更具体的接口,例如在执行时将其更改IServiceFooReports为......这样我就可以访问下面的FooViewModel Get()函数,而无需将结果转换为其他调用代码。

public interface IServiceFooReports : IServiceReports
{
    IEnumerable<FooViewModel> Get();
}

public class FooService<T> : IServiceFooReports
{
      ... use EF model context and call .CreateObjectContext<T>()
      do filtering on the IQueryable result and return the result materialised as the relevant view models
}

这可能吗?这是IGetReportServices界面,以防也需要更改。IServiceReports是空的,目前仅用作将这些报告服务存储在同一个字典中的一种方式……标记接口?

public interface IGetReportServices
{
    IServiceReports Get(ReportTypes reportType, ReportPeriodTypes reportPeriodType);
}
4

2 回答 2

1

如果您希望转换为 IGetReportServices 接口的对象在不进行转换的情况下返回 IServiceFooReports,那么您可以在接口上添加一个为您进行转换的方法。如果将服务实例强制转换为您的接口,则定义“新”方法以更改返回类型的任何技巧都不会返回更具体的类型。

IServiceReports GetFoo(..., ...);

有一个像这样的实现

public IServiceFooReports GetFoo(ReportTypes reportType, ReportPeriodTypes reportPeriodType){
    return this.Get(reportType, reportPeriodType) As IServiceFooReports;
}

或者,您可以使用通用方法,例如

T Get<T>(ReportTypes reportType, ReportPeriodTypes reportPeriodType);

但是,您需要知道类类型才能调用该方法,所以这实际上并没有比事后强制转换结果更好。另一方面,如果您可以将报表逻辑设计为使用报表类型而不是 ReportTypes 枚举,则可以利用隐式转换。

List<T> Get<T>(T reportTemplate, ReportPeriodTypes reportPeriodType);

然后你可以像这样调用方法:

var template = new FooViewModel();
List<FooViewModel> results = Get(template, reportPeriodType);

不确定这是否有帮助

于 2013-07-18T23:40:37.840 回答
1

当然。接口可以继承其他接口。当它们这样做时,您可以使用多态性在它们所代表的类型之间进行转换,就像使用类继承时一样。举个简单的例子,如果你有以下接口:

public interface IFoo
{
    string Get();
}

public interface IBar : IFoo
{
    new string Get();
}

和以下课程:

public class MyClass : IBar
{
    public string Get()
    {
        return @"some data";
    }
}

那么您可以实现以下方法:

public MyClass SomeMethod()
{
    MyClass myVariable = new MyClass();
    return myVariable;
}

并调用该方法来填充由未由“MyClass”显式实现的接口定义的类型的变量,而是由 MyClass确实实现的接口。IE,

IFoo myOtherVariable = SomeMethod() as IFoo;

注意:重点是 MyClass 没有明确实现“IFoo”。它只实现了'IBar',而后者又继承了'IFoo'。

因此,将上述信息应用于您的问题,如果您的接口 IServiceFooReports实现了 IServiceReports,并且如果您的方法的内部工作创建了一个“IServiceFooReports”类型的对象,您可以通过“IServiceReports”类型返回该对象。在您的方法之外,您应该能够使用“as”关键字安全地将输出转换为更具体的类型“IServiceFooReports”。

于 2013-07-18T23:55:48.387 回答