I know, I know. You can't override static methods. The title was just a trick to provoke your interest :-) In this post, I'll first try to explain why it is impossible to override static methods and then provide two common ways to do it.
Or rather - two ways to achieve the same effect.
So, what's the problem?
There are situations where you wish you could substitute or extend functionality of existing static members - for example, provide different implementations for it and be able to switch implementations at runtime.
For example, let's consider a static class Log with two static methods:public static class Log
{
public static void Message(string message){ ... }
public static void Error(Exception exception){ ... }
}
Let's say your code calls Log.Message and Log.Error all over the place and you would like to have different logging behaviors - logging to console and to the Debug/Trace listeners. Moreover, you would like to switch logging at runtime based on selected options.
Why can't we override static members?
Really, why? If you think about it, this is just common sense. Overriding usual (instance) members uses the virtual dispatch mechanism to separate the contract from the implementation. The contract is known at compile time (instance member signature), but the implementation is only known at runtime (concrete type of object provides a concrete implementation). You don't know the concrete type of the implementation at compile time.
Really, why? If you think about it, this is just common sense. Overriding usual (instance) members uses the virtual dispatch mechanism to separate the contract from the implementation. The contract is known at compile time (instance member signature), but the implementation is only known at runtime (concrete type of object provides a concrete implementation). You don't know the concrete type of the implementation at compile time.
This is an important thing to understand: when types inherit from other types, they fulfil a common contract, whereas static types are not bound by any contract (from the pure OOP point of view). There's no technical way in the language to tie two static types together with an "inheritance" contract. If you would "override" the Log method in two different places, how do we know which one we are calling here: Log.Message("what is the implementation?")
With static members, you call them by explicitly specifying the type on which they are defined. Which means, you directly call the implementation, which, again, is not bound to any contract.
By the way, that's why static members can't implement interfaces. And that's why virtual dispatch is useless here - all clients directly call the implementation, without any contract.
Let's get back to our problem
Solution 1: Strategy + Singleton
Yes, that's easy. Define a contract to use and it will be automatically separated from the implementation. (Unless you make it sealed. By making a type or a member sealed, you guarantee that no one else can implement this contract.)
OK, so here's our contract:
public abstract class Logger
{
public abstract void Message(string message);
public abstract void Error(Exception exception);
}
You could make it an interface as well, but with an interface you wouldn't be able to change the contract later without breaking existing clients.
You'll only need one instance of a logging behavior, so let's create a singleton:public static class
Log
{
public static Logger Instance { get; set; } ...
}
Correct Singleton implementation is not part of this discussion - there is a whole science of how to correctly implement Singleton in .NET - just use your favorite search engine if you're curious.
Now we just redirect the static methods to instance methods of the Logger instance and presto:
public static void Message(string message)
{
Instance.Message(message);
}
And that's it! All of your code continues to use Log.Message and Log.Error, and if you'd like to change the behavior, just say Log.Instance = new DebugWriteLineLogger();
No comments:
Post a Comment