views:

295

answers:

5

Edit: I have asked question to understand why C# designers chose it to behave in particular fashion?

Similar question has been asked, but this is little different.

Should following code give warning?

class Foo { public void Do() { /*...*/ } /*...*/ }
class Bar : Foo { public static void Do()  { /*...*/ } /*...*/ }

It gives: "warning CS0108: 'Bar.Do()' hides inherited member 'Foo.Do()'. Use the new keyword if hiding was intended."

Let me make a change in code.

class Foo { public static void Do() { /*...*/ } /*...*/ }
class Bar : Foo { public void Do()  { /*...*/ } /*...*/ }

Same warning.

If you do following, warning goes away.

class Foo { public void Do() { /*...*/ } /*...*/ }
class Bar : Foo { new public static void Do() { /*...*/ } /*...*/ }

Let me make further change.

class Foo { public void Do() { /*...*/ } /*...*/ }
class Bar : Foo { 
    new public static void Do() 
    { new Bar().Do();/*...*/ } /*...*/ 
}

This does not compile. error CS0176: Member 'Bar.Do()' cannot be accessed with an instance reference; qualify it with a type name instead.

So, I lose access to inherited method via instance reference from static method!

What would be logic behind it? Or I made a typo somewhere?

Btw, I come across this when I was trying to define static method 'Show' for my form derived from 'Form'.

+2  A: 

No, that makes perfect sense. This works as expected:

using System;
using System.Collections.Generic;

class Foo { public void Do() { /*...*/ } /*...*/ }
class Bar : Foo { 
    new public static void Do() 
    { ((Foo)new Bar()).Do();/*...*/ } /*...*/ 
}

That's because the compiler assumes that you have a Bar type, and then finds the static member. By casting it to Foo (which comes for free btw.) you make it look in the metdadata for Foo() and all is fine.

Lucero
When I say "new Bar().Do();" I am surely calling instance method "Do", which is available in Bar via Foo. So, why do we have to type cast?
isntn
Because the compiler does not look at the instance, but at the type metadata. And here it finds only one method with the correct signature (name and empty argument list), and that's the static one.
Lucero
Think of it in terms of virtual fuction tables; the Bar::Do() mapping hides the mapping of Foo:Do().
McWafflestix
Hi Lucero, If compiler looks at type metadata, is type metadata available during compilation? Then Who generates metadata in first place from code? Are there different passes of compilation?McWafflesstix, If you have time, can you pls elaborate this further? Thanks in advance.
isntn
The metadata is generated by the compiler, and yes, I assume that internally the compiler does at least 2 passes (since the order of declarations does not matter). My guess is: lexical analysis, tokenizing, metadata/structure creation (all 3 can be in one pass), code compilation (second pass)
Lucero
Each type gets a vtable in it's internal in memory CORE_INFO_TYPE_STRUCT object. This vTable consists of all the methods accessible through the type. Each one is defined as either static or instance. The Do() in Bar's vTable is defined as static. Therefore calling it requires "static" syntax...
Charles Bretana
The Do() in Foo is NOT accessible via a Bar object reference variable, because it's not in the Bar Type's vTable... it has been replaced by the new static Do().
Charles Bretana
The Do() in Foo is NOT accessible via a Bar object reference variable, because it's not in the Bar Type's vTable... it has been replaced by the new static Do(). Casting the object to a Foo tells the compiler to look in Foo's vTable. For (non-Virtual) methods the declared variable type controls this
Charles Bretana
Hi Charles, thanks for answer. According to it, at v-table level there is no difference between static and instance methods, whereas I think that there should be difference. why not? why v-table is done this way?
isntn
@isntn: I'm not sure that the term "vTable" is technically correct, as it usually refers to the runtime table of virtual method pointers. However, the compiler just uses a list of method signatures, and the signature consists of name, if present number of generic arguments, and the parameters.
Lucero
+2  A: 

Try this:

        new public static void Do()
        { 
            ((Foo)new Bar()).Do(); 
        }
RossFabricant
A: 

In your final code sample, the new keyword on the declaration of Bar.Do() means that you intended to hide Foo.Do().

Don Kirkby
A: 

Out of curiosity, why do you want to do this? It seems like you are probably going about your solution the wrong way.

The above code appears to work as it should, and the compiler messages tell you what the problems are.

JoshJordan
Hi Josh, I do not "want" to do it. It is just that I come across this, and want to understand this behavior chosen by C# designers. "new Bar().Do()" tells obviously that it is instance method being called. So, why C# is designed to make this even more explicit? bit of educative question.
isntn
I see. Well, in that case, you have to realize that in Bar, there is *no* instance method named Do(), just a static method. By giving it the same name, you have blocked inheritance. So no, it is not obvious that the instance method is being called, to me or the compiler =p
JoshJordan
I think, there is instance method in Bar which is derived from Foo.
isntn
That's my point - this isn't true. Say you had them both non-static. Then, Bar.Do() would hide Foo.Do(). That is, Foo.Do() is not inherited. It doesn't matter that it is static, or an instance method, Foo.Do() is not derived into Bar if Bar.Do() exists.
JoshJordan
:)I understand u,now. Thanks. In same terms, I am looking for logical reason why it doesn't matter. Was it optional for C# (or CLR) designers to differentiate between static and instance method and they chose not to? If no, why? If yes, why they chose not to? (Though I agree that it is no big deal)
isntn
+3  A: 

Where do you think the bug is? The fact that there is a warning is absolutely right. From the C# 3.0 spec, section 10.3.4:

A class-member-declaration is permitted to declare a member with the same name or signature as an inherited member. When this occurs, the derived class member is said to hide the base class member. Hiding an inherited member is not considered an error, but it does cause the compiler to issue a warning. To suppress the warning, the declaration of the derived class member can include a new modifier to indicate that the derived member is intended to hide the base member.

The fact that your method invocation fails is subtler, but it's basically because the member lookup algorithm picks the static method, and then this part of section 7.5.5.1 is used:

Final validation of the chosen best method is performed:

The method is validated in the context of the method group: If the best method is a static method, the method group must have resulted from a simple-name or a member-access through a type. If the best method is an instance method, the method group must have resulted from a simple-name, a member-access through a variable or value, or a base-access. If neither of these requirements is true, a compile-time error occurs.

Jon Skeet
Hi Jon,+1 for answer.I think, from language user perspective, bug is in the way of choosing best method to call :-).To me, "new Bar().Do()" clearly tells which method is being called at compile time and there is no need to produce error!
isntn
I suspect the alternative is to make the language much more complicated though. There are quite often edge cases which could be improved if the language were smarter - but it would then be harder to "know" the language well, and also to correctly implement it in a compiler. It's all balance...
Jon Skeet
Downvoters: please add a comment, otherwise it's just pointless...
Jon Skeet