以下程序的指纹

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,并从intint所述转换是从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,它似乎着想差别不大

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top