51

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?

Eamon Nerbonne
  • 47,023
  • 20
  • 101
  • 166
  • 1
    +1 also using protected static fields in inheritor's static constructor should be prohibited – Sergey Mirvoda Feb 17 '10 at 15:14
  • 1
    Can you give an example of the problems with accessing inherited protected static fields in static constructors? – Eamon Nerbonne Feb 17 '10 at 16:07
  • 5
    I wish with everything in me right now that there was true static inheritance, and that I could specify `public static abstract string Method();`, for example, requiring subclasses to implement this static member. It would make me so, so happy! – ErikE Mar 16 '16 at 02:14
  • @ErikE: an (ugly) workaround in some cases is to require a type param `TStatic` with the constraint `where TStatic : struct, IMyMethod` and then in the consuming code to write `default(TStatic).Method()`. That way you can at least statically require the availability of an algorithm, even if the syntax is far from obvious. – Eamon Nerbonne Mar 16 '16 at 11:56
  • @EamonNerbonne I'll have to think about that! The way I've been doing it is writing a unit test that finds all concrete subclasses of the base class and ensures they have the requisite methods/properties that take the correct parameters/types and return the correct type. I'm sure getting practice at reflection. By writing a typeprovider class that provides the parameters for the real unit test, I get one unit test per subtype. – ErikE Mar 16 '16 at 16:44
  • 1
    This post gives a good alternative to static method hiding: [What's the correct alternative to static method inheritance?](https://stackoverflow.com/questions/1380087/whats-the-correct-alternative-to-static-method-inheritance) – Hans Vonn Nov 18 '19 at 16:16
  • I have to agree, this makes no sense. Static members cannot be inherited, so why is the namespace being polluted with names that can only be accessed internally? This is particularly onerous for public static members. Forcing us to use `new` *sometimes* to hide/shadow an inherited member that cannot really be inherited is annoying as heck. – RBarryYoung Dec 22 '20 at 15:30

7 Answers7

29

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.

Daniel Earwicker
  • 114,894
  • 38
  • 205
  • 284
  • 1
    There are two different issues here - do you allow plain `M();` from somewhere within B, and, secondly, what happens when you explicitly say `B.M();` I find the argument for the allowing those methods without class name qualification (kind of like extension methods) to be reasonable - but I don't see the point of reinterpreting *explicit* namespace qualifications, however. – Eamon Nerbonne Feb 18 '10 at 16:13
  • 1
    I agree on that too, not just because its pointless but may actually be very misleading, especially if the static methods are "factory"-like - see update. – Daniel Earwicker Feb 19 '10 at 12:11
  • what about `public static T Make() where T : Thing, new() { return new T(); }`? – Nick Strupat Mar 04 '14 at 22:55
  • @NickStrupat - Why would you use that rather than the `new` operator directly? – Daniel Earwicker Mar 05 '14 at 21:05
  • I'm not sure. I guess if you're using the factory pattern for some reason. I was just thinking within the context of your answer. – Nick Strupat Mar 05 '14 at 21:58
  • 6
    "One guy's namespace pollution is another guy's added spicy flavoring". I think this might be my new favorite quote of all time. – Asad Saeeduddin Aug 31 '14 at 17:18
11

I rather have access to all my based static members in derived classes. Otherwise i would need to know exactly where the static member was defined and call it explicitly.

When using Intellisense you can automatically know every static member available to that kind of class.

Of course, they are not inherited, it's just a shortcut

Luis Filipe
  • 8,488
  • 7
  • 48
  • 76
  • This makes sense - it's a shorthand helper for intellisense. 'Course, intellisense works best if you only have useful things in scope, so it's not a pure win. – Eamon Nerbonne Feb 18 '10 at 14:35
  • I'd love see intellisense providing filters, such as, let me see only events, only extensions, etc.. Maybe VS2010 RC1 has it... dind't check it yet – Luis Filipe Feb 18 '10 at 16:53
  • 1
    I recently learned that R# warns you when referencing a static member from an inherited class – Luis Filipe Aug 07 '13 at 08:02
9

That's how it works, would probably just be a stupid answer in most cases. But in this case, it is how it works; since you derive from A you say that you are A + the extra features you add.

Therefore you need to be able to access the same variables that you would through an instance of A.

However, inheriting a static class makes no sense while access to the static members / fields / methods does.

An example of this is the following:

internal class BaseUser
{
    public static string DefaultUserPool { get; set; }
}
internal class User : BaseUser
{
    public int Id { get; set; }
    public string Name { get; set; }
    public User Parent { get; set; }
}

Where the test looks like this:

User.DefaultUserPool = "Test";
BaseUser.DefaultUserPool = "Second Test";

Console.WriteLine(User.DefaultUserPool);
Console.WriteLine(BaseUser.DefaultUserPool);

Both of the WriteLines outputs "Second Test", this is because both BaseUser and User should use DefaultUserPool, by design. And overriding static implemented methods wouldn't make mucn sense since it's just an accessor in the child-class.

There can be only one. Overriding it would mean that there's a new implementation for that sub-class, which would kill the term "static".

Filip Ekberg
  • 36,033
  • 20
  • 126
  • 183
  • 3
    The question is why User.DefaultUserPool compiles at all. I far as I can tell, it's merely confusing; it's for instance, as your example points out, not obvious at first sight whether those two distinct static references refer to the same underlying variable. If User had it's own DefaultUserPool (either in another assembly, or with the new keyword) this code would also compile without warning yet result in different output - in short, you *can* effectively override a static member (even if, for the static case, overriding and hiding is equivalent). – Eamon Nerbonne Feb 17 '10 at 15:59
  • Put another way, how does being able to say `User.DefaultUserPool` add anything to being forced to write `BaseUser.DefaultUserPool`? After all, there is *no* member or property `DefaultUserPool` on the class `User`, it "inherits" this from `BaseUser`. – Eamon Nerbonne Feb 17 '10 at 16:05
  • In a more abstract manner this makes sense, in a more concrete / implementation manner it might not make as much sense.. But it depends on how you look at it, User Extends BaseUser and therefore you have access to all Public things from BaseUser. There is really no difference in having a non-static and static in this matter, the only difference is that you can Access it by using both BaseUser and User, both of them will reference the same object, since thats what "static" is all about, a chunk of data stored during the entire life cycle of the application.. – Filip Ekberg Feb 17 '10 at 17:03
  • If that's the intuition, why can't I access it from generic code? I can guarantee compile-time that all subtypes of A will have *some* static method M - so why can't I call it? And, this intuition is different from the intuition used for static members of concrete versions of generic types - i.e. if `MyType` has a static member, this member is different for `MyType` and `MyType` (of course generics aren't exactly subclasses - we're talking design, here). And, worse, what if `MyType : A` - then `MyType.M()` does refer to the same method as `MyType.M()`. – Eamon Nerbonne Feb 18 '10 at 12:31
  • What I'm trying to get at with my previous comment is not that your idea doesn't make sense - it does - it's just that it's not fully realized (no use for generics, for instance), and I don't see the purpose of doing it that way - the *why*. – Eamon Nerbonne Feb 18 '10 at 12:44
  • MyType.M() and MyType.M() would both refer to the same static method that's because it doesn't matter what the Generic type really is the T only defines the type used Inside the object, not What the objects is. Therefore when MyType derives from A and A has a static member / method, you will always be able to access M() through MyType, dispite what T will ever be. – Filip Ekberg Feb 18 '10 at 12:51
  • That's my point - `MyType.Q === MyType.Q` sometimes, depending on whether A is defined in `MyType<>` or in a base type. By contrast, if static members were not inherited this would never be possible. – Eamon Nerbonne Feb 18 '10 at 14:38
  • Don't forget that both MyType and MyType are still both MyType and if MyType inherits from A and there are static functionallity, they will be "inherited"/"referenceable". – Filip Ekberg Feb 18 '10 at 14:49
6

Actually, as I understand it, this is just a shortcut provided by the compiler. Syntax sugar. B.M() will just compile to A.M() since B does not have a static M() and A does. It's for easier writing, nothing else. There is no "static inheritance".

Added: And the requirement for new when "redefining" is just so that you don't accidentally shoot yourself in the foot.

Vilx-
  • 104,512
  • 87
  • 279
  • 422
  • But why? You *could* design a language where if a symbol isn't identified, it picks the symbol in scope that's nearest in name by levenshtein distance, resolving ties lexicographically. It's possible to have aliases which refer to the same entity, but in this case, why do so? – Eamon Nerbonne Feb 18 '10 at 14:32
0

I think it's for accessing protected static members of the base class.

class Base
{
    protected static void Helper(string s)
    {
       Console.WriteLine(s);
    }
}

class Subclass : Base
{
   public void Run()
    {
       Helper("From the subclass");
    }
}
TrueWill
  • 25,132
  • 10
  • 101
  • 150
  • 1
    This isn't questionable behavior - within Subclass, it's clearly consistent for `Helper` and `Base.Helper` (and even `base.Helper`) to resolve to class `Base`'s method `Helper`. However, it's less clear why `Subclass.Helper` works. – Eamon Nerbonne Feb 18 '10 at 12:46
0

So... What's the alternative?

The question mentions...

why doesn't the compiler report an error? After all, there's no "M" method in "B"...

But there is a derived "M" method in "B" class.

If the compiler did not present the programmer a unified virtual table for base cases, then the programmer would have to go hunting through base types to find static methods. This would break polymorphism.

Wikipedia...

Subtype polymorphism, almost universally called just polymorphism in the context of object-oriented programming, is the ability of one type, A, to appear as and be used like another type, B....

In strongly typed languages, polymorphism usually means that type A somehow derives from type B, or type C implements an interface that represents type B.

Community
  • 1
  • 1
kervin
  • 11,672
  • 5
  • 42
  • 59
  • 2
    That argument holds for non-static members; only wrt *instances* are types polymorphic in the first place (the wiki summary is misleading). Were static members "polymorphic", then I'd expect (for instance) `void CallM() where T:A {T.M();}` to work. In any case, you can't actually call/use a static member via a instance anyhow, so not having static inheritance isn't violating polymorphism (i.e. you can't do `new A().M();`), and there's no general rule that you must be able to use a sub*type* whereever you could use the base class - parameters are contravariant, for instance. – Eamon Nerbonne Feb 18 '10 at 14:29
  • 1
    The alternative would be to allow `A.M();` and `M();` (just as namespaces would) and to give a compile error on line `B.M();` – Eamon Nerbonne Feb 18 '10 at 16:11
  • @EamonNerbonne: It would be helpful if methods could specify if and how they should be seen by inherited classes. If `Foo` is a widely-used class and it turns out to be necessary to have a type `FooBase` from which a type `Bar` descends (without `Bar` having to descend from `Foo`) being able to move the static members to `FooBase` without breaking code that calls them from `Foo` is useful, but there are times when methods really should only be invokable using the base class. – supercat Jul 14 '15 at 17:35
-1

I always see it a means of preventing any form of polymorphism by the inheriting class on those items that you wish to retain the same function for all child classes.

ignore the above for some reason I was thinking of sealed instead of static

I suppose that you'd use static member variables and functions in order to ensure that any data or functionallity is not dependent on the a class instance as it would be instantiated only the once.

An example of use would be say a counter value that would keep a live count of all instances of a superclass's subclasses (each subclass increments the static count value on construction). This count value would be available and equal for all instances of the subclass.

ChrisBD
  • 9,104
  • 3
  • 22
  • 35
  • The "sealed" keyword does that. This doesn't really; an inheriting class _can_ actually replace the base classes implementation - which we'd normally call overriding. – Eamon Nerbonne Feb 17 '10 at 16:01
  • @Eamon - you are quite correct. I had a bit of a brainstorm there. – ChrisBD Feb 17 '10 at 18:37