Why Static Isn’t Real Sharing
The static keyword has origins as far back as C, and has carried
across to C++, Java and C#, among others—
And this is all well and good for sharing within a class, but the problem is that it doesn’t play well with inheritance, because static members cannot be overridden. And inheritance is another common method of achieving code sharing. Consider the following snippet, which shows how one might hope to override static members in C#.
public class Base { public static virtual string Value => "Base"; public static virtual void Display() => Console.WriteLine(Value); } public class Sub1 : Base { public static override string Value => "Sub1"; } public class Sub2 : Base { public static override void Display() => Console.WriteLine($">>> {Value}"); } new Sub1().Display(); // Sub1 new Sub2().Display(); // >>> Base
We would like to be able to override one or both of the
Value
and Display
members, but
unfortunately this won’t compile because static members can’t be
virtual. Python, on the other hand, got this exactly right—classmethod
decorator.
class Base: value = 'base' @classmethod def display(cls): print(cls.vlaue) class Sub1(Base): value = 'Sub1' class Sub2(Base): @classmethod def display(cls): print('>>>', cls.value) Sub1().display() # Sub1 Sub2().display() # >>> Base
So how come Python can do this but C# can’t? Notice that we used
the classmethod
decorator, not the
staticmethod
decorator. The classmethod
decorator benefits from the fact that it is passed a reference to
the class, whereas in the case of the staticmethod
decorator you
need to hard-code it into source. This distinction really
highlights the real meaning of the word static—
The reason why static members can’t be overridden is because doing so requires knowing at run-time which override to use, which goes against the definition of the word static. Even in a language like Python without compilation, the concept of static still exists and still has the same restrictions.
So if we want to get truly shareable members, both horizontally across members of a class and vertically across a type hierarchy, we are going to need another solution.