C# 语言规范是否指定是使用方法覆盖还是使用影子?
Does the C# language specification specify whether method override or shadow gets used?
考虑以下源代码:
public abstract class SomeBaseClass {
public abstract void Foo(object a);
}
public class SomeClass<T> : SomeBaseClass {
public override void Foo(object a) {
Console.WriteLine("Foo() from SomeBaseClass");
}
public void Foo(T a) {
Console.WriteLine("Foo() from SomeClass");
}
}
下面的内容完全没有歧义,因为 Foo
有两个重载:
public class Bar : SomeClass<int> { }
var bar = new Bar();
bar.Foo(null);
bar.Foo(24);
C# 语言规范是否说明了以下以某种方式编译的示例中的预期行为?
public class Baz : SomeClass<object> { }
var baz = new Baz();
baz.Foo(null); // "Foo() from SomeClass"
勾选这个strange behavior explained by Jon Skeet in his blog post Overloading
Inheritance can cause a confusing effect. When the compiler goes looking for instance method overloads, it considers the compile-time class of the "target" of the call, and looks at methods declared there. If it can't find anything suitable, it then looks at the parent class... then the grandparent class, etc. This means that if there are two methods at different levels of the hierarchy, the "deeper" one will be chosen first, even if it isn't a "better function member" for the call. Here's a fairly simple example:
using System;
class Parent
{
public void Foo(int x)
{
Console.WriteLine("Parent.Foo(int x)");
}
}
class Child : Parent
{
public void Foo(double y)
{
Console.WriteLine("Child.Foo(double y)");
}
}
class Test
{
static void Main()
{
Child c = new Child();
c.Foo(10);
}
}
The target of the method call is an expression of type Child, so the compiler first looks at the Child class. There's only one method there, and it's applicable (there's an implicit conversion from int to double) so that's the one that gets picked. The compiler doesn't consider the Parent method at all.
The reason for this is to reduce the risk of the brittle base class problem, where the introduction of a new method to a base class could cause problems for consumers of classes derived from it. Eric Lippert has various posts about the brittle base class problem which I can highly recommend.
There's one aspect of this behaviour which is particularly surprising though. What counts as a method being "declared" in a class? It turns out that if you override a base class method in a child class, that doesn't count as declaring it. Let's tweak our example very slightly:
using System;
class Parent
{
public virtual void Foo(int x)
{
Console.WriteLine("Parent.Foo(int x)");
}
}
class Child : Parent
{
public override void Foo(int x)
{
Console.WriteLine("Child.Foo(int x)");
}
public void Foo(double y)
{
Console.WriteLine("Child.Foo(double y)");
}
}
class Test
{
static void Main()
{
Child c = new Child();
c.Foo(10);
}
}
Now it looks like you're trying to call Child.Foo(int x) in my opinion - but the above code will actually print Child.Foo(double y). The compiler ignores the overriding method in the child.
Given this oddness, my advice would be to avoid overloading across inheritance boundaries...
https://msdn.microsoft.com/en-us/library/aa691331%28VS.71%29.aspx
"First, the set of all accessible (Section 3.5) members named N declared in T and the base types (Section 7.3.1) of T is constructed. Declarations that include an override modifier are excluded from the set. If no members named N exist and are accessible, then the lookup produces no match, and the following steps are not evaluated."
考虑以下源代码:
public abstract class SomeBaseClass {
public abstract void Foo(object a);
}
public class SomeClass<T> : SomeBaseClass {
public override void Foo(object a) {
Console.WriteLine("Foo() from SomeBaseClass");
}
public void Foo(T a) {
Console.WriteLine("Foo() from SomeClass");
}
}
下面的内容完全没有歧义,因为 Foo
有两个重载:
public class Bar : SomeClass<int> { }
var bar = new Bar();
bar.Foo(null);
bar.Foo(24);
C# 语言规范是否说明了以下以某种方式编译的示例中的预期行为?
public class Baz : SomeClass<object> { }
var baz = new Baz();
baz.Foo(null); // "Foo() from SomeClass"
勾选这个strange behavior explained by Jon Skeet in his blog post Overloading
Inheritance can cause a confusing effect. When the compiler goes looking for instance method overloads, it considers the compile-time class of the "target" of the call, and looks at methods declared there. If it can't find anything suitable, it then looks at the parent class... then the grandparent class, etc. This means that if there are two methods at different levels of the hierarchy, the "deeper" one will be chosen first, even if it isn't a "better function member" for the call. Here's a fairly simple example:
using System; class Parent { public void Foo(int x) { Console.WriteLine("Parent.Foo(int x)"); } } class Child : Parent { public void Foo(double y) { Console.WriteLine("Child.Foo(double y)"); } } class Test { static void Main() { Child c = new Child(); c.Foo(10); } }
The target of the method call is an expression of type Child, so the compiler first looks at the Child class. There's only one method there, and it's applicable (there's an implicit conversion from int to double) so that's the one that gets picked. The compiler doesn't consider the Parent method at all.
The reason for this is to reduce the risk of the brittle base class problem, where the introduction of a new method to a base class could cause problems for consumers of classes derived from it. Eric Lippert has various posts about the brittle base class problem which I can highly recommend.
There's one aspect of this behaviour which is particularly surprising though. What counts as a method being "declared" in a class? It turns out that if you override a base class method in a child class, that doesn't count as declaring it. Let's tweak our example very slightly:
using System; class Parent { public virtual void Foo(int x) { Console.WriteLine("Parent.Foo(int x)"); } } class Child : Parent { public override void Foo(int x) { Console.WriteLine("Child.Foo(int x)"); } public void Foo(double y) { Console.WriteLine("Child.Foo(double y)"); } } class Test { static void Main() { Child c = new Child(); c.Foo(10); } }
Now it looks like you're trying to call Child.Foo(int x) in my opinion - but the above code will actually print Child.Foo(double y). The compiler ignores the overriding method in the child.
Given this oddness, my advice would be to avoid overloading across inheritance boundaries...
https://msdn.microsoft.com/en-us/library/aa691331%28VS.71%29.aspx
"First, the set of all accessible (Section 3.5) members named N declared in T and the base types (Section 7.3.1) of T is constructed. Declarations that include an override modifier are excluded from the set. If no members named N exist and are accessible, then the lookup produces no match, and the following steps are not evaluated."