3

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

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

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

根据 Olivier Jacot-Descombes 的回复,我将有一个FileSystem像这样的课程(这是真实的)

public static class FileSystem
{
    static IFSImplemenation s_imple;
    static FileSystem()
    {
        if(<some system setting>)
            // 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);
    }

    ...
}

重申一下-我有大量代码不想更改,因此保持调用签名相同很重要-此解决方案实现了这一点

4

4 回答 4

5

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

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

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

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

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

当然,使用接口的好处是更容易测试合同实施的双方:

  • 在合约是客户端的情况下,您可以轻松地模拟接口并将模拟传递给合约的消费者(这是您可能开始考虑依赖注入的地方)
  • 您可以将实现与系统的其余部分分开测试。

在你的磁盘 I/O 抽象的情况下,对于调用你的合约的所有东西,你永远不必担心实际接触文件系统,你只需要让合约的行为(通过适当的模拟设置)就好像它正在接触一样文件系统。

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

  • 创建一个定义服务操作的抽象(合同/抽象类型)。
  • 创建一个实现,将调用从抽象实现的实例转发到静态方法。
  • 使用实现实例重新连接对服务的所有静态调用。此时,您确实必须使用某种依赖注入;如果你不这样做,你只是在调用站点创建具体的实现,这几乎是一样的(如果你想使用另一个实现,你必须再次更改每个调用站点)。
于 2012-10-12T18:35:21.127 回答
1

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

有些类有一个静态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(); }
}
于 2012-10-12T18:36:05.200 回答
1

使用静态类作为非静态实现的外观

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);
    }

    ...
}
于 2012-10-12T18:39:03.897 回答
0

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

于 2012-10-12T18:39:03.990 回答