Question

I know how to make pluggable things in c#. Define an Interface, Activator.CreateInstance(<class>), etc. Or I can have a code path that explicitly creates an instance of the plugged class. Many ways.

But what if the service I want to make pluggable is static (I know I could refactor so that it's not but that's not the point of the question)

Concrete example. I have a class that provides disk I/O abstraction (Read File, List Directory,....). Now I want different implementations of this abstraction that serves up files from , say, a real FS, a database.

Based on Olivier Jacot-Descombes reply, I will have a FileSystem class (that is real) like this

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

    ...
}

To reiterate - I have a large body of code that I dont want to change so it was important to keep the calling signature the same - this solution achieves that

Was it helpful?

Solution

Use your static class as facade for non-static implementations

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

    ...
}

OTHER TIPS

You can't, and that's a limitation of the type system in .NET and most object-oriented systems/languages.

The reason being that "pluggable things" (as you refer to them) require polymorphism in order to be effective.

You define a contract (an interface, in .NET-speak), and then you implement that contract. Your system only ever deals with the contract.

Static members are not polymorphic in .NET, so you can never get them to implement a contract.

For your example of disk I/O abstraction, you wouldn't create a static class for this, you'd create an interface, and implement the interface, passing around the interface.

Of course, the benefits of using an interface are that it's easier to test both sides of the implementation of your contract:

  • Where the contract is a client, you can mock the interface easily and pass the mock to the consumers of the contract (this is where you might start thinking about dependency injection)
  • You can test the implementation separately from the rest of your system.

In the case of your disk I/O abstraction, for everything that calls your contract, you would never have to worry about actually touching the file system, you simply have to make the contract behave (through proper mock setup) as if it were touching the file system.

If you have an existing service that is exposed through static members, and you want the ability to swap that out with another implementation, then you'll have to do the following:

  • Create an abstraction (contract/abstract type) which defines the operations on your service.
  • Create an implementation that forwards the calls from the instance of the implementation of the abstraction to the static methods.
  • Rewire all of the static calls to the service with instances of the implementation. At this point, you really must use dependency injection of some sort; if you don't, you're just creating the concrete implementation at the call site, and that's pretty much the same (you'd have to change every call site again if you wanted to use another implementation).

Static classes are not "pluggable". If you want to be able to swap it out, you need to refactor it to use interfaces that you can inject.

Some classes have a static Current or Default parameter, such as ServiceLocator. You then use ServiceLocator.Current to access the current IServiceLocator in a static context.

You can take this a little further and hide the Current object. While I don't recommend doing this, it will prevent the need to refactor all of your code. However, the maintenance is much higher with this method, as Foo does not implement IFoo.

Suppose you have a static class Foo

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

You could make an interface IFoo

interface IFoo
{
    string GetBar();
}

Now change your class Foo to the following

static class Foo 
{
    private static IFoo _foo;

    public static void SetFoo(IFoo foo) { _foo = foo; }

    public static string GetBar() { return _foo.GetBar(); }
}

Make you static class residenting in host application and being actually a factory: returning implementation provided by a plugin by interface.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top