【问题标题】:injecting an generic interface注入通用接口
【发布时间】:2015-07-07 14:40:20
【问题描述】:

好吧,我对 C# 中的泛型有点迷茫

我有这个通用接口

interface IInvoiceStorage<T>
    where T : class
{
    void Persist(T Invoice);
}

两个类实现接口

public class FacturaStorageForSQLServer:IInvoiceStorage<EVT>
{
      public void Persist(EVT Invoice)
      {
        /*Implementation*/
      }
}



public class FacturaStorageForMySQLServer:IInvoiceStorage<EVTFruit>
{
      public void Persist(EVTFruit Invoice)
      {
         /*Implementation*/
      }
}

当我想在我的服务类中声明这个时问题就来了

public class invoice_service
{
   IInvoiceStorage Storage;
   public invoice_service(IInvoiceStorage storage)
   {
     Storage=_storage;
   }
}

C# 告诉我必须声明接口的 de 类型,但如果我这样做了,那么我的服务类将依赖于实现而不是接口。

建议??

更新 1: 抱歉,如果我声明该类型,则接口将仅依赖于使用该类型的实现,但是如果我有两个使用两种不同类型(例如 EVT 和 EVTFruit)的实现会发生什么。

我正在考虑使用另一个接口来建立 EVT 和 EVTFruit 之间的关系,但它们可能是两个完全不同的对象,所以我不确定这是否是个好主意。

【问题讨论】:

  • 您需要声明接口的类型参数 IInvoiceStorage,而不是该接口实际实现的类型。
  • 你打算在你的invoice_service 类中传递什么样的对象到Storage.Persist()?这些对象的类型与EVTFruitEVT 有什么关系?

标签: c# generics interface decoupling


【解决方案1】:

你可以稍微改变你的班级:

public class invoice_service<T> where T : class
{
   IInvoiceStorage<T> Storage;
   public invoice_service(IInvoiceStorage<T> storage)
   {
     Storage=_storage;
   }
}

这将允许您正确使用界面并保持其通用性。

【讨论】:

  • 使调用者通用听起来不错
【解决方案2】:

根据您的需要,您还可以定义该接口的非通用版本:

public interface IInvoiceStorage
{
  ...
}

并且让类也从这个接口继承。

public class FacturaStorageForSQLServer : IInvoiceStorage, IInvoiceStorage<EVT>
public class FacturaStorageForMySQLServer : IInvoiceStorage, IInvoiceStorage<EVTFruit>

这样您就可以在invoice_service 类中使用非通用版本的接口。

但是,正如我所说,您是否可以使该接口的功能独立于类型(例如,List&lt;T&gt; 还实现了列表功能的IList,没有类型),这取决于您的需求。

【讨论】:

  • 这是个好主意,但正如你所说,我将不得不使该界面的功能
  • 非泛型接口的方法可以是泛型接口的子集。基本上,非泛型和泛型接口都为您提供了所有功能。
  • 好的,但是有个小问题。如果我在 invoice_service 类中使用非通用接口。我只能访问非泛型接口的方法。
  • 是的,这就是为什么我说这取决于您的业务需求,如果这适合您的话。也许你可以在你的非通用接口中展示你所有的方法。如果您确实需要通用接口,那么您应该检查您的设计。其他解决方案是 Ron Beyer 的解决方案,其中您的 invoice_service 也是通用的。
  • 您还可以对您的 T 类型进行更多限制,例如使其实现一些其他接口并在通用约束中使用它。例如“其中 T :类,IPersistor”或类似的东西。然后,您可以使用 IInvoiceStorage 作为 invoice_service 的输入参数。
【解决方案3】:

在尝试了您的建议并阅读了一些博客之后,我所做的就是至少目前正在满足我的要求。当我意识到我不应该将通用存储库模式用作存储库本身而是作为存储库的助手时,问题就解决了!最后,我正在做的是将通用接口包装在另一层非通用接口中。

一个调用 IInvoiceRepositoryService 实现的服务类

public class InvoiceService
{
   private IInvoiceRepositoryService RepositoryService;
   public SQLInvoiceService(IInvoiceRepositoryService _RS)
   {
       RepositoryService=_RS;
   }
}

以及它们各自对 EVT 和 EVTFruit 的实现。

public class EVTRepository:IInvoiceRepositoryService
{
  private IInvoiceStorage<EVT> EVTStorage;

  public EVTInvoiceRepository(IInvoice<EVT> _EVT)
  {
    EVTStorage=_EVT;
  }
}


public class EVTStorageForSQLServer: IInvoiceStorage<EVT>
{
   /*Implementation*/
}

public class EVTStorageForMySQLServer: IInvoiceStorage<EVT>
{
   /*Implementation*/
}



public class EVTFruitRepository:IInvoiceRepositoryService
{
  private IInvoiceStorage<EVT> EVTFruitStorage;

  public EVTFruitInvoiceRepository(IInvoice<EVTFruit> _EVTFruit)
  {
    EVTFruitStorage=_EVTFruit;
  }
}

public class EVTFruitStorageForSQLServer: IInvoiceStorage<EVTFruit>
{
   /*Implementation*/
}

public class EVTFruitStorageForMySQLServer: IInvoiceStorage<EVTFruit>
{
   /*Implementation*/
}

最后,我认为这只是一个设计问题。我将把 Ron Beyer 的回复标记为答案,因为它是有效的,而且非常简单

【讨论】:

    猜你喜欢
    • 2015-09-11
    • 1970-01-01
    • 1970-01-01
    • 2015-04-06
    • 2015-07-05
    • 1970-01-01
    • 2019-04-17
    • 1970-01-01
    • 2021-05-28
    相关资源
    最近更新 更多