Given:
class BaseClass
{
public virtual void M(int x)
{
}
}
class Derived : BaseClass
{
public override void M(int x)
{
base.M(x);
}
static void M(object x)
{
}
static void Main()
{
var d = new Derived();
d.M(0);
}
}
Error:
Member 'Derived.M(object)' cannot be accessed with an instance reference; qualify it with a type name instead
Looking at the C# 4.0 spec section 7.4 (Member Lookup) the first bullet point reads:
A member lookup of a name N with K type parameters in a type T is processed as follows:
[...] Members that include an override modifier are excluded from the set [of accessible members named N]
From this I conclude that the override Derived.M
is no longer accessible. Instead, the compiler must refer to BaseClass.M
.
However, this does not explain why adding a static Derived.M
suddenly causes a compilation error. The compiler can now only see the static member Derived.M
and concludes that this member is an invalid call. If I remove the static Derived.M
then compilation succeeds.
Why does this happen?
The following steps seem to happen and are together the reason for your compiler error:
M
is removed, leaving the compiler only with the static one. The parameter type of the static one is object
, which is compatible to int
. The method name matches, too.I can't really quote a single sentence of the spec proving this, but neither §7.4 (Member lookup) nor §3.5 (Accessability) talk about static
vs. instance, so I assume this fact is simply not taken into account at all when doing a member lookup.
Relevant parts from §7.4 seem to be:
A member lookup of a name N with K type parameters in a type T is processed as follows:
- First, a set of accessible members named N is determined:
[...]
The set consists of all accessible (§3.5) members named N in T, including inherited members and the accessible members named N in object. If T is a constructed type, the set of members is obtained by substituting type arguments as described in §10.3.2. Members that include an override modifier are excluded from the set.
The way I understand this part it is like explained above: It would return both the instance method and the static one and will then remove the instance one, because it has the override
modifier.
Only the static method is left at this point.
It closes with this:
Finally, [...], the result of the lookup is determined:
if the set contains only methods, then this group of methods is the result of the lookup.
So, the result is the static method.
Obviously, this problem only happens in circumstances where you have a class hierarchy and one of the derived classes declares a static method with the same name and compatible parameters.
Adding such a static method to an existing class is a case where simply adding something to a class is still a breaking change.
Although I am pretty sure you know how to solve the compiler error, I will still state it, to have a complete answer:
Use any of the base classes as the compile time type of your variable. The runtime type still can be the derived type, that's not the problem:
BaseClass d = new Derived();
// ^ ^
// compile time type runtime type
Daniel's answer is correct; a brief summary of the relevant rules is:
This last rule is a bit odd, but it does make some sense. See my comments and nikov's comments on pages 290 and 291 of the hardbound annotated C# 4 specification for an extended discussion of this specific rule.
Also, for an analysis of an interesting corner case where this rule intersects the Color Color
rule, see
http://blogs.msdn.com/b/ericlippert/archive/2009/07/06/color-color.aspx
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With