怎么方法隐藏在工C#?(二部分)
-
22-08-2019 - |
题
以下程序的指纹
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类,像这样:
public override void Print(C c)
突然间所有程序开始打印:
A:C(A,B)
B:I(A)
为什么?
解决方案
这是与如何重载方法都解决的事情。
有效(稍微简化),编译器首先着眼于声明的类型在这种情况下,表达式(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
,并从int
到int
所述转换是从int
比转换“更好”到double
,一个事实,即仅Foo(double)
方法是最初的声明在Derived
表示编译器将忽略Foo(int)
。
这是非常令人惊奇的IMO。我可以看到为什么它会是如果Derived
没有重载Foo
- 否则将在基类一个新的,更具体的,方法可能会意外更改的行为 - 但显然Derived
这里的知道的有关Base.Foo(int)
因为它的覆盖它。这是我认为C#的设计师做出的错误决定的(比较少)点之一。
其他提示
好吧,所以
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继承了一个,你是simly调用新的方法的两倍。当你在此改变的方法,这就改变方法的签名的时候你的呼吁,但是当你呼叫B签名,然后,它有其自己的方法的签名。
我不知道,如果我解释清楚,但是很好的问题。
使用新:
A和B获得相同执行的打印方法。
使用复盖:
一个具有不同的方法的签名,B,你有没有更改的方法的签名在B仅在A
使用新它基本上忽略这样的:
public void Print(I i)
{
Console.WriteLine("B:I(" + i.A() + ")");
}
这是一个很大的问题。结果 所有的答案都可以在这里找到: http://msdn.microsoft.com/en-us /library/6fawty39(VS.80).aspx
它的要点是:
... C#编译器将首先尝试让 通话与版本兼容 [functionName]宣布本来就 [派生类。覆盖方法不 考虑一类为声明, 他们是一个新的实现 方法上的基类中声明。只要 如果C#编译器所无法比拟的 方法调用上的原始方法 [派生类]将它尝试匹配呼叫 到具有相同的覆盖方法 名称和兼容的参数。
因此,因为你有一个新的打印方法(I I)所导出的类相匹配的参数的“c”,(因为C实现I),该方法优先于“覆盖”方法。
在标记方法为“新的”,它们都被认为是对派生类中实现,并打印(C c)中的方法更紧密地匹配的参数“c”的,所以它的优先级。
这是至少尽可能多的关于如何方法重载在C#工作问题。我想你已经在这里强调了一个有趣的情况...
在第一种情况下(使用的方法中的new
关键字),编译器决定使用Print
方法重载型C的参数,因为它的类型是完全等同于传递的参数(即没有隐式转换所需的),而隐式转换到接口如果编译器选择Print
方法,它有I型的自变量我将需要 - 换句话说,它选择更多的“明显的”方法重载
在第二种情况下(使用的方法中的override
关键字),编译器决定使用Print
的过载与我因为虽然要覆盖在类B的Print(C c)
方法超载,它被有效地在定义的类型的参数父类A,使得实际上Print(I i)
方法过载的最高级别的过载,并因此最直接的一个,即,第一个编译器发现。
希望这将有助于你理解。让我知道如果我需要进一步clairfy任何点......
请注意:如果我错说,编译器这些东西,那么请大家指正,但它使争论无论是编译器或CLR / JIT,它似乎着想差别不大