【问题标题】:C# function to return the implemented interface of a typeC#函数返回一个类型的实现接口
【发布时间】:2013-07-18 23:05:30
【问题描述】:

我有这个单例工厂类示例,我在其中使用字典并在运行时确定相关的报表类。因为根据每个报表服务还必须执行的操作,最多有 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...这样我就可以访问下面的@ 987654326@ 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);
}

【问题讨论】:

标签: c# .net generics reflection factory


【解决方案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);

不确定是否有帮助

【讨论】:

  • 谢谢,所以基本上我的调用代码仍然需要确定调用哪个函数,即 GetFoo、GetBar.. 或应用于泛型函数的类型,或者只是我想要执行的转换 :( 看起来好像我运气不好
【解决方案2】:

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

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 确实实现的接口。即,

IFoo myOtherVariable = SomeMethod() as IFoo;

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

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

【讨论】:

  • 您的示例依赖于SomeMethod() 返回MyClass。这与我的Get() 方法被定义为返回IServiceFooReports 相同,但事实并非如此。我想让我的调用代码与服务工厂上的相同 Get() 方法交互,但返回类型更具体,所以我不必在调用代码中强制转换或设置类型:(
  • SomeMethod 不需要返回 MyClass。它也可以返回 'IBar' 或 'IFoo' 并且它仍然可以工作。对于您的问题,您可以指示您的方法返回类型“T”,其中 T 是由在运行时使用泛型注入的接口定义的类型。如果 T 可以从方法的内部工作产生的特定类型中转换,那么您需要在方法中做的就是在方法的末尾包含一行,例如“return myResult as T”。您所做的只是在您的方法中移动投射发生的位置。
  • 但是编译器需要能够推断出 T 是什么,所以我的调用代码需要是这样的:.Get&lt;IServiceFooReports&gt;(...) 这与刚刚执行 .Get(...) as IServiceFooReports 几乎相同。试图在我的调用代码中避免这种情况,因为我需要在很多地方使用逻辑来确定要使用的类型。我也无法使接口IServiceReports 通用,因为我想将不同的报告服务存储在我的工厂内的同一个静态字典中。
  • 现在考虑一下,将IServiceReports 更改为IServiceReports&lt;T&gt; 也会迫使IGetReportServices 必须更改才能知道T 是什么。由于工厂是单例的,因此我只能为其字典中的所有服务设置一种类型。到目前为止的结论是我需要重构我“继承”的这个项目,以便调用代码在更少的地方。感谢您的帮助
猜你喜欢
  • 2013-07-22
  • 1970-01-01
  • 1970-01-01
  • 2019-12-19
  • 1970-01-01
  • 2012-10-06
  • 2011-05-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多