Pergunta

O programa a seguir imprime

A:C(A,B)
B:C(A,B)

(como deveria)

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);
    }
}

No entanto, se eu mudar palavra-chave 'nova' para 'override' na classe B assim:

    public override void Print(C c)

todos de um programa de repente começa a imprimir:

A:C(A,B)
B:I(A)

Por quê?

Foi útil?

Solução

Esta é a ver com como métodos sobrecarregados são resolvidos.

Efetivamente (simplificado um pouco), o compilador primeiro olha para o tipo declarado da expressão (B) neste caso e procura por métodos candidatos que são primeiramente declarados nesse tipo . Se houver quaisquer métodos que são apropriadas (isto é, onde todos os argumentos podem ser convertidos para tipos de parâmetros do método), então ele não olhada quaisquer tipos de pais. Isto significa que métodos substituídos, em que a declaração inicial é em um tipo pai, não conseguir um look-nos se há algum "recém-declarados" métodos adequados no tipo derivado.

Aqui está um exemplo um pouco mais simples:

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);
    }
}

Isto imprime Derived.Foo(double) - mesmo que o compilador sabe que há um método de correspondência com um parâmetro do tipo int, eo argumento é do tipo int, e a conversão de int para int é "melhor" do que a conversão de int para double, o fato de que apenas o método Foo(double) é originalmente declarou em meio Derived o compilador ignora Foo(int).

Este é altamente surpreendendo IMO. Eu posso ver por que seria o caso se Derived não substituir Foo - caso contrário, introduzindo um novo, mais específico, método na classe base poderia mudar o comportamento de forma inesperada - mas claramente Derived aqui sabe sobre Base.Foo(int) como é ignorá-los. Este é um dos (relativamente poucos) pontos onde eu acredito que o C # projetistas tomou a decisão errada.

Outras dicas

Ok, então

    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() + ")");
    }

Este declara um novo método para a impressão. Agora, porque herda B de A, você está simly chamar o novo método duas vezes. Quando você overide o método, este, em seguida, muda a assinatura do método quando você ligar para A, mas quando você chamar a assinatura B, então ele tem a sua própria assinatura do método.

Eu não tenho certeza se eu estou explicando pergunta clara, mas bom.

usando novo:

A e B obter a mesma implementação do Método de impressão.

usando override:

A tem uma assinatura método diferente para B como, você não mudou a assinatura do método no B apenas em A.

usando o novo basicamente ignora esta:

    public void Print(I i)
    {
        Console.WriteLine("B:I(" + i.A() + ")");
    }

Esta foi uma grande questão.
Todas as respostas podem ser encontradas aqui: http://msdn.microsoft.com/en-us /library/6fawty39(VS.80).aspx

A essência do que é o seguinte:

... o compilador C # vai primeiro tentar fazer a chamada compatível com as versões de [functionName] declarou originalmente no [A classe derivada]. métodos de substituição não são considerados como declarados em uma classe, são novas implementações de um método declarado em uma classe base. Somente se o compilador C # não pode coincidir com o chamada de método para um método original em [A classe derivada] ele vai tentar igualar a chamada a um método substituído com a mesma nome e compatíveis parâmetros.

Então, porque você tem um novo método de impressão (I i) na classe derivada que combina o argumento de "c", (porque c implementos I), esse método tem precedência sobre o método de "override".

Quando você marcar o método como "novo", ambos são considerados a ser implementado na classe derivada, ea impressão (C c) Método corresponda melhor ao parâmetro "c", por isso tem precedência.

Esta é pelo menos tanto uma pergunta sobre como método sobrecarga trabalha em C #. Eu acho que você destacou uma situação interessante aqui ...

No primeiro caso (usando a palavra chave new no método), o compilador decide usar a sobrecarga método Print com o parâmetro do tipo C, pois de tipo é completamente equivalente à do parâmetro passado (isto é, nenhuma conversão implícita é necessário), enquanto que uma conversão implícita para a interface I seria necessária se o compilador foram para escolher o método Print que leva um argumento do tipo I -. em outras palavras, ele escolhe o mais "óbvio" sobrecarga de método

No segundo caso (usando a palavra-chave override no método), o compilador decide usar a sobrecarga de Print com parâmetro do tipo I porque embora você está substituindo a sobrecarga de método Print(C c) na classe B, que é efetivamente definido no a classe principal a, tornando a sobrecarga método Print(I i) de facto a sobrecarga de nível mais elevado e, portanto, o mais directo, ou seja, o primeiro dos achados compilador.

Esperemos que isto irá ajudá-lo a compreender. Deixe-me saber se eu preciso clairfy quaisquer pontos mais ...

Nota: Se eu estou errado em dizer que o compilador faz essas coisas, então por favor me correto, embora ele faz pouca diferença para o bem do argumento se é o compilador ou CLR / JIT, pareceria

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top