以下程序打印
A:C(A,B)
B:C(A,B)
(应该如此)
public interface I
{
string A();
}
public class C : I
{
public string A()
{
return "A";
}
public string B()
{
return "B";
}
}
public class A
{
public virtual void Print(C c)
{
Console.WriteLine("A:C(" + c.A() + "," + c.B() + ")");
}
}
public class B : A
{
public new void Print(C c)
{
Console.WriteLine("B:C(" + c.A() + "," + c.B() + ")");
}
public void Print(I i)
{
Console.WriteLine("B:I(" + i.A() + ")");
}
}
class Program
{
public static void Main(string[] args)
{
A a = new A();
B b = new B();
C c = new C();
a.Print(c);
b.Print(c);
}
}
但是,如果我在B类中将关键字'new'更改为'override',就像这样:
public override void Print(C c)
突然程序开始打印:
A:C(A,B)
B:I(A)
为什么?
答案 0 :(得分:8)
这与如何解决重载方法有关。
有效地(有些简化),编译器首先查看表达式(B)的声明类型,并查找首先在该类型中声明的候选方法。如果有任何适当的方法(即所有参数都可以转换为方法的参数类型),那么它不会查看任何父类型。这意味着如果在派生类型中存在任何“新近声明的”适当方法,那么初始声明在父类型中的重写方法不会获得查找。
这是一个稍微简单的例子:
using System;
class Base
{
public virtual void Foo(int x)
{
Console.WriteLine("Base.Foo(int)");
}
}
class Derived : Base
{
public override void Foo(int x)
{
Console.WriteLine("Derived.Foo(int)");
}
public void Foo(double d)
{
Console.WriteLine("Derived.Foo(double)");
}
}
class Test
{
static void Main()
{
Derived d = new Derived();
d.Foo(10);
}
}
这打印Derived.Foo(double)
- 即使编译器知道匹配方法的参数类型为int
,参数类型为int
,并且转换为{{1} }} int
比从int
到int
的转换“更好”,事实上只有double
方法最初声明 Foo(double)
表示编译器忽略Derived
。
这是非常令人惊讶的IMO。我可以看到为什么Foo(int)
没有覆盖Derived
的情况 - 否则在基类中引入一个新的,更具体的方法可能会意外地改变行为 - 但显然{{1}这里知道关于Foo
,因为它正在覆盖它。这是我认为C#设计师做出错误决定的(相对较少)点之一。
答案 1 :(得分:1)
好的,所以
public new void Print(C c)
{
Console.WriteLine("B:C(" + c.A() + "," + c.B() + ")");
}
public void Print(I i)
{
Console.WriteLine("B:I(" + i.A() + ")");
}
这声明了一种新的打印方法。现在因为B继承自A,你只需要两次调用新方法。当您覆盖该方法时,这会在您调用A时更改方法签名,但是当您调用B签名时,它会有自己的方法签名。
我不确定我是否在解释清楚但好的问题。
使用new:
A和B获得相同的Print方法实现。
使用覆盖:
A与B具有不同的方法签名,因为您没有仅在A中更改B中的方法签名。
使用新的它基本上忽略了这个:
public void Print(I i)
{
Console.WriteLine("B:I(" + i.A() + ")");
}
答案 2 :(得分:1)
这是一个很好的问题 所有答案都可以在这里找到: http://msdn.microsoft.com/en-us/library/6fawty39(VS.80).aspx
它的主旨是:
... C#编译器将首先尝试制作 该调用与版本兼容 最初声明的[functionName] [派生类]。覆盖方法不是 被视为在课堂上宣布, 它们是a的新实现 在基类上声明的方法。只要 如果C#编译器无法匹配 方法调用原始方法 [派生类]会尝试匹配通话 一个被覆盖的方法与相同 名称和兼容参数。
因为你在派生类上有一个新方法Print(I i),它匹配参数“c”,(因为c实现了我),该方法优先于“override”方法。
当您将方法标记为“new”时,它们都被认为是在派生类上实现的,而Print(C c)方法更接近地匹配参数“c”,因此它优先。
答案 3 :(得分:0)
这至少是关于方法重载如何在C#中工作的问题。我想你在这里突出了一个有趣的情况......
在第一种情况下(在方法上使用new
关键字),编译器决定将Print
方法重载与类型C的参数一起使用,因为它的类型完全等同于传递的参数(即不需要隐式转换),如果编译器选择采用类型I的参数的Print
方法,则需要隐式转换到接口I - 换句话说,它选择了更多“明显的”方法超载。
在第二种情况下(在方法上使用override
关键字),编译器决定使用Print
的重载和类型I的参数,因为尽管你重写了Print(C c)
在类B中的方法重载,它在父类A中有效定义,使Print(I i)
方法实际上重载最高级别的重载,因此是最直接的重载,即编译器找到的第一个重载。“ p>
希望这有助于您理解。如果我需要进一步克服任何问题,请告诉我......
注意:如果我说编译器做了这些事情我错了,那么请纠正我,虽然它对于参数来说没什么区别,无论是编译器还是CLR / JIT,它看起来都是。