Search code examples
c#inheritancestaticlanguage-design

C# static member "inheritance" - why does this exist at all?


In C#, a superclass's static members are "inherited" into the subclasses scope. For instance:

class A { public static int M() { return 1; } }
class B : A {}
class C : A { public new static int M() { return 2; } }
[...]
A.M(); //returns 1
B.M(); //returns 1 - this is equivalent to A.M()
C.M(); //returns 2 - this is not equivalent to A.M()

Now, you can't inherit static classes, and the only place I can imagine that static inheritance might matter ignores it entirely: although you can make a generic constraint that requires a type parameter T to be a subclass of A, you still cannot call T.M() (which probably simplifies things for the VM), let alone write a different M implementation in a subclass and use that.

So, the "inheritance" of static members merely looks like namespace pollution; even if you explicitly qualify the name (i.e. B.M) A's version is still resolved.

Edit compare with namespaces:

namespace N1{  class X();   }
namespace N1.N2 {  class X();   }
namespace N1.N2.N3 { [...] }

Within N1.N2.N3 It makes sense that if I use X without qualification it refers to N1.N2.X. But if I explicitly refer to N1.N2.N3.X - and no such class exists - I don't expect it to find N2's version; and indeed to compiler reports an error if you try this. By contrast, if I explicitly refer to B.M(), why doesn't the compiler report an error? After all, there's no "M" method in "B"...

What purpose does this inheritance have? Can this feature be used constructively somehow?


Solution

  • So, the "inheritance" of static members merely looks like namespace pollution

    That's right, except that one guy's pollution is another guy's added spicy flavouring.

    I think Martin Fowler, in his work on DSLs, has suggested using inheritance in this way to allow convenient access to static methods, allowing those methods to be used without class name qualification. So the calling code has to be in a class that inherits the class in which the methods are defined. (I think it's a rotten idea.)

    In my opinion, static members should not be mixed into a class with a non-static purpose, and the issue you raise here is part of the reason why it's important not to mix them.

    Hiding private static mutable data inside the implementation of an otherwise "instancey" class is particularly horrible. But then there are static methods, which are even worse mixers. Here's a typical use of static methods mixed into a class:

    public class Thing
    {
        // typical per-instance stuff
        int _member1;
        protected virtual void Foo() { ... }
        public void Bar() { ... }
    
        // factory method
        public static Thing Make()
        {
            return new Thing();
        }
    }
    

    It's the static factory method pattern. It's pointless most of the time, but even worse is that now we have this:

    public class AnotherThing : Thing { }
    

    This now has a static Make method which returns a Thing, not a AnotherThing.

    This kind of mismatch strongly implies that anything with static methods should be sealed. Static members fail to integrate well with inheritance. It makes no sense to have them heritable. So I keep static things in separate static classes, and I gripe about redundantly having to declare every member static when I've already said that the class is static.

    But it's just one of those too-late-now things. All real, working languages (and libraries, and products) have a few of them. C# has remarkably few.