如何制作可插入的静态类

我知道如何在c#中制作可插拔的东西。 定义一个接口, Activator.CreateInstance()等。或者我可以有一个显式创建插件类实例的代码路径。 很多种方法。

但是,如果我想要可插拔的服务是静态的(我知道我可以重构,所以它不是,但这不是问题的重点)

具体例子。 我有一个提供磁盘I / O抽象的类(读取文件,列表目录,….)。 现在我想要这种抽象的不同实现,它可以提供来自真实FS,数据库的文件。

基于Olivier Jacot-Descombes的回复,我将有一个像这样的FileSystem类(这是真的)

 public static class FileSystem { static IFSImplemenation s_imple; static FileSystem() { if() // converted to singleton instead of static s_imple = new OldFileSystem() else s_imple = new DbFileSystem(); } public static byte[] ReadFile(string path) { return s_imple.ReadFile(path); } ... } 

重申 – 我有一大堆代码,我不想改变,所以保持调用签名相同很重要 – 这个解决方案实现了

将静态类用作非静态实现的外观

 public static class DB { private static IDbInterface _implementation; public static void SetImplementation(IDbInterface implementation) { _implementation = implementation; } public static Customer GetCustomerByID(int custId) { return _implementation.GetCustomerByID(custId); } ... } 

你不能,这是.NET中类型系统和大多数面向对象系统/语言的限制。

原因是“可插入的东西”(正如你所指的那样)需要多态才能有效。

您定义一个合同(一个接口,在.NET中说),然后您实现该合同。 您的系统只处理合同

静态成员在.NET中不是多态的,因此您永远无法让它们实现合同。

对于您的磁盘I / O抽象示例,您不会为此创建静态类,您将创建一个接口,并实现接口,传递接口。

当然,使用界面的好处是可以更轻松地测试合同执行的双方:

  • 如果合同是客户端,您可以轻松地模拟界面并将模拟传递给合同的消费者(这是您可能开始考虑dependency injection的地方 )
  • 您可以与系统的其余部分分开测试实施。

在磁盘I / O抽象的情况下,对于调用合同的所有内容,您永远不必担心实际触摸文件系统,您只需要使合同行为(通过适当的模拟设置)就好像它触摸文件系统。

如果您有通过静态成员公开的现有服务,并且您希望能够将其与另一个实现交换,那么您将必须执行以下操作:

  • 创建一个抽象(契约/抽象类型),定义服务上的操作。
  • 创建一个实现,将调用从抽象实现的实例转发到静态方法。
  • 使用实现的实例重新连接到服务的所有静态调用。 此时,你真的必须使用某种dependency injection; 如果你不这样做,你只是在呼叫站点创建具体的实现,这几乎是相同的(如果你想使用另一个实现,你必须再次更改每个呼叫站点)。

静态类不是“可插拔的”。 如果您希望能够将其交换出来,则需要重构它以使用可以注入的接口。

某些类具有静态CurrentDefault参数,例如ServiceLocator。 然后使用ServiceLocator.Current在静态上下文中访问当前的IServiceLocator

您可以稍微进一步隐藏Current对象。 虽然我不建议这样做,但它将无需重构所有代码。 但是,这种方法的维护要高得多,因为Foo没有实现IFoo

假设你有一个静态类Foo

 static class Foo { public static string GetBar() { return "Bar"; } } 

你可以创建一个接口IFoo

 interface IFoo { string GetBar(); } 

现在将您的类Foo更改为以下内容

 static class Foo { private static IFoo _foo; public static void SetFoo(IFoo foo) { _foo = foo; } public static string GetBar() { return _foo.GetBar(); } } 

使您在宿主应用程序中驻留静态类并且实际上是工厂:通过接口返回由插件提供的实现。