Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is overriding static method alowed in C#

protected static new void WhyIsThisValidCode()
{
}

Why are you allowed to override static methods? Nothing but bugs can come from it, it doensn't work as you would think.

Take the following classes.

class BaseLogger
{
    protected static string LogName { get { return null; } }

    public static void Log(string message) { Logger.Log(message, LogName); }
}

class SpecificLogger : BaseLogger
{
    protected static string LogName { get { return "Specific"; } }
}

this is alowed, and the code

SpecificLogger.Log("test");

is altso alowed, but it doesn't do what you would think by looking at the code.

it calls Logger.Log with LogName = null.

So why is this allowed?

like image 302
AndreasN Avatar asked Nov 16 '10 17:11

AndreasN


People also ask

Why can static method be overridden?

Static methods are bonded at compile time using static binding. Therefore, we cannot override static methods in Java.

Can we override static method in C?

Can we override a static method? No, we cannot override static methods because method overriding is based on dynamic binding at runtime and the static methods are bonded using static binding at compile time. So, we cannot override static methods.

Why we Cannot override private and static methods?

You cannot override a private or static method in Java. If you create a similar method with same return type and same method arguments in child class then it will hide the super class method; this is known as method hiding. Similarly, you cannot override a private method in sub class because it's not accessible there.

Can static methods be overloaded or overridden?

The static method is resolved at compile time cannot be overridden by a subclass. An instance method is resolved at runtime can be overridden. A static method can be overloaded.


4 Answers

for my surprise following code is allowed and compiles without any error on .net Framework 4.5.1, VS 2013.

class A
{
    public static void Foo()
    {
    }
}

class B : A
{

}

class Program
{
    static void main(string[] args)
    {
        B.Foo();
    }
}
like image 58
Paras Avatar answered Oct 02 '22 14:10

Paras


The new keyword does not override a method. It instead creates a new method of the same name which is independent of the original. It is not possible to override a static method because they are not virtual

like image 25
JaredPar Avatar answered Oct 02 '22 15:10

JaredPar


You're not overriding it, you're hiding it. A normal method would exhibit exactly the same behavior so there is nothing specific to static methods here.

Hiding is only useful in a few cases. The one where I came across most often is making the return type more specific in a derived class. But I never had that occur with static methods.

One area where static functions with a certain name might be useful is if you use reflection and want to get information on each class by returning it from a method. But of course in most cases an attribute fits better.

And it's not likely to create bugs since your code produces a compiler-warning:

Warning 'StaticHiding.SpecificLogger.LogName' hides inherited member 'StaticHiding.BaseLogger.LogName'. Use the new keyword if hiding was intended.

And if you use new you should know what you're doing.

like image 40
CodesInChaos Avatar answered Oct 02 '22 15:10

CodesInChaos


Others have pointed out that this isn't overriding, but that still leaves your original question: why are you able to do it? (But the question is really "why can you hide static methods".)

It's an inevitable feature of supporting the independent versioning of component that contain base classes and components that use those base classes.

For example, imagine that component CompB contains the base class, and some other component CompD contains a derived class. In version 1 of CompB, there might not have been any property called LogName. The author of CompD decides to add a static property called LogName.

The critical thing to understand at this point is that the author of v1 of CompD was not intending to replace or hide any feature of the base class - there was no member called LogName in the base class when they wrote that code.

Now imagine that a new version of the CompB library is released. In this new version, the author added a LogName property. What's supposed to happen in CompD? The options appear to be:

  1. CompD no longer works because the LogName it introduces clashes with the LogName added to CompB
  2. Somehow make the CompD's LogName replace the base CompB LogName. (It's not actually remotely clear how this could work with statics. You could envisage this with non-statics though.)
  3. Treat the two LogName members as being completely different members that happen to have the same name. (In reality, they don't - they're called BaseLogger.LogName and SpecificLogger.LogName. But since in C# we don't always need to qualify the member name with the class, it looks like they're the same name.)

.NET chooses to do 3. (And it does that with both statics and non-statics. If you want behaviour 2 - replacement - with non-statics, then the base has to be virtual and the derived class has to mark the method as override to make it clear that they were deliberately overriding a method in the base class. C# will never make a derived class's method replace a base class's method unless the derived class explicitly stated that this is what they wanted.) This is likely to be safe because the two members are unrelated - the base LogName didn't even exist at the point where the derived one was introduced. And this is preferable to simply breaking because the latest version of the base class introduced a new member.

Without this feature, it would be impossible for new versions of the .NET Framework to add new members to existing base classes without that being a breaking change.

You say that the behaviour isn't what you expect. Actually it's exactly what I'd expect, and what you'd probably want in practice. The BaseLogger has no idea that the SpecificLogger has introduced its own LogName property. (There's no mechanism by which it could because you cannot override static methods.) And when the author of SpecificLogger wrote that LogName property, remember that they were writing against v1 of BaseLogger which didn't have a LogName, so they weren't intending that it should replace the base method either. Since neither class wants replacement, clearly replacement would be the wrong thing.

The only scenario in which you should ever end up in this situation is because the two classes are in different components. (Obviously you can contrive a scenario when they're in the same component, but why would you ever do that? If you own both pieces of code and release them in a single component, it'd be mad ever to do this.) And so BaseLogger should get its own LogName property, which is exactly what happens. You may have written:

SpecificLogger.Log("test");

but the C# compiler sees that SpecificLogger doesn't provide a Log method, so it turns this into:

BaseLogger.Log("test");

because that's where the Log method is defined.

So whenever you define a method in a derived class that isn't attempting to override an existing method, the C# compiler indicates this in the metadata. (There's a "newslot" setting in the method metadata that says, this method is meant to be brand new, unrelated to anything in the base class.)

But this gives you a problem if you want to recompile CompD. Let's say you've got a bug report due to some entirely unrelated bit of code and you need to release a new version of CompD. You compile it against the new verison of CompB. If the code you've written wasn't allowed, you wouldn't actually be able to - old code that's already compiled would work, but you wouldn't be able to compile new versions of that code, which would be a bit mad.

And so, to support this (frankly somewhat obscure) scenario, they allow you to do this. They generate a warning to let you know that you've got a naming clash here. You need to supply the new keyword to get rid of it.

This is an obscure scenario, but if you want to support inheritance across component boundaries, you need this, otherwise the addition of new public or protected members on a base class would invariably be a breaking change. That's why this is here. But it's bad practice ever to rely on it, hence the fact that you get a compiler warning. The use of the new keyword to get rid of the warning should only ever be a stopgap.

The bottom line is this: this feature exists for one reason only, and that's to get you out of a hole in situations where a new version of some base class has added a member that didn't previously exist, and which clashes with a member that's already on your derived class. If that's not the situation you're in, don't use this feature.

(I think they should actually issue an error rather than a warning when you leave out new, to make this more clear.)

like image 43
Ian Griffiths Avatar answered Oct 02 '22 15:10

Ian Griffiths