Uso de palavras-chave virtual + substituição vs. novo


204

Quais são as diferenças entre declarar um método em um tipo base " virtual" e substituí-lo em um tipo filho usando a overridepalavra-chave " " em vez de simplesmente usar a newpalavra-chave " " ao declarar o método correspondente no tipo filho?


3
MSDN digamos "Usando newcria um novo membro com o mesmo nome e faz com que o membro original para ficar escondido, enquanto overrideestende a implementação de um membro herdado"
AminM


Respostas:


181

A palavra-chave "new" não substitui, significa um novo método que não tem nada a ver com o método da classe base.

public class Foo
{
     public bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public new bool DoSomething() { return true; }
}

public class Test
{
    public static void Main ()
    {
        Foo test = new Bar ();
        Console.WriteLine (test.DoSomething ());
    }
}

Isso imprime false, se você usasse substituir, ele seria impresso true.

(Código base retirado de Joseph Daigle)

Então, se você estiver fazendo polimorfismo real, você sempre deve substituir . O único local em que você precisa usar "novo" é quando o método não está relacionado de forma alguma à versão da classe base.


Você deve revisar seu código, eu tornei os métodos estáticos (válidos para o uso da palavra-chave "new"). Mas decidi que era mais claro usar métodos de instância.
Joseph Daigle

1
Obrigado, eu perdi a parte "estática". Deve ter mais atenção no futuro
albertein

1
Observe que a linha Foo test = new Bar () é crucial aqui, a palavra-chave new / override para os métodos determina qual método é chamado quando você coloca uma barra em uma variável Foo.
Thomas N

... então é exatamente o mesmo que simplesmente não ter virtualna base e overridederivar? Por que isso existe? O código ainda funcionará mesmo sem new- então é puramente apenas legibilidade?
Don Cheadle

Sua resposta, querido senhor, é bastante vaga. new e virtual-override fazem a mesma coisa, a única diferença é que new HIDES oculta o método na classe pai e substitui .. bem, substitui-o. Obviamente, ele imprime false porque seu objeto de teste é do tipo Foo, se fosse do tipo Bar, seria true. Exemplo bastante complicado, no entanto.
precisa saber é o seguinte

228

Eu sempre acho coisas assim mais facilmente compreendidas com fotos:

Mais uma vez, usando o código de Joseph Daigle,

public class Foo
{
     public /*virtual*/ bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public /*override or new*/ bool DoSomething() { return true; }
}

Se você chamar o código assim:

Foo a = new Bar();
a.DoSomething();

OBSERVAÇÃO: O importante é que nosso objeto seja realmente um Bar, mas estamos armazenando-o em uma variável do tipoFoo (isso é semelhante a convertê-lo)

Em seguida, o resultado será o seguinte, dependendo de você ter usado virtual/ overrideou newao declarar suas classes.

Imagem de explicação Virtual / Substituir


3
Obrigado .... mas você poderia explicar um pouco sobre a foto acima em relação ao elenco que você disse?
214 odiseh

Ups! Se esqueceu de adicionar estas poucas linhas ao meu prev. comentário: você quer dizer que virtual / overriding e non-vitual / new são usados ​​apenas para o conceito de polimorfismo e quando você simplesmente declara uma variável (sem usar casting), isso não significa? Obrigado novamente.
214 odiseh

esta resposta é exatamente o que eu estava procurando. O único problema que tive é que flickr imagens são bloqueadas no meu trabalho para que eu tinha para acessá-lo através do meu telefone ...
mezoid

7
Esforço real para responder à pergunta.
iMatoria 21/07

Eu votaria se conseguir consertar a imagem? Sua bloqueado atrás do firewall corp ...
Jeremy Thompson

43

Aqui está um código para entender a diferença no comportamento de métodos virtuais e não virtuais:

class A
{
    public void foo()
    {
        Console.WriteLine("A::foo()");
    }
    public virtual void bar()
    {
        Console.WriteLine("A::bar()");
    }
}

class B : A
{
    public new void foo()
    {
        Console.WriteLine("B::foo()");
    }
    public override void bar()
    {
        Console.WriteLine("B::bar()");
    }
}

class Program
{
    static int Main(string[] args)
    {
        B b = new B();
        A a = b;
        a.foo(); // Prints A::foo
        b.foo(); // Prints B::foo
        a.bar(); // Prints B::bar
        b.bar(); // Prints B::bar
        return 0;
    }
}

obrigado por isso - mas por que usar newpara "ocultar" o método base, quando simplesmente não usar a substituição parece fazer o mesmo?
Don Cheadle

1
Eu não acho que foi criado para impedir que a classe base seja substituída. Eu acho que ele foi criado para nome Evite confronto como você não pode substituir um método que não é virtualeo compilador vai reclamar se ele vê o mesmo nome de função em uma classe "linhagem" sem virtualassinatura
MR5

19

o new palavra-chave, na verdade, cria um membro completamente novo que existe apenas nesse tipo específico.

Por exemplo

public class Foo
{
     public bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public new bool DoSomething() { return true; }
}

O método existe nos dois tipos. Quando você usa reflexão e obtém os membros do tipo Bar, na verdade você encontrará 2 métodos chamados DoSomething()que parecem exatamente iguais. Ao usá- newlo, você efetivamente oculta a implementação na classe base, de modo que quando as classes derivam Bar(no meu exemplo) a chamada de método para base.DoSomething()vai Bare não Foo.


9

virtual / override informa ao compilador que os dois métodos estão relacionados e que, em algumas circunstâncias, quando você pensa que está chamando o primeiro método (virtual), é realmente correto chamar o segundo método (substituído). Essa é a base do polimorfismo.

(new SubClass() as BaseClass).VirtualFoo()

Chamará o método VirtualFoo () anulado da subclasse.

new informa ao compilador que você está adicionando um método a uma classe derivada com o mesmo nome que um método na classe base, mas eles não têm relação um com o outro.

(new SubClass() as BaseClass).NewBar()

Chamará o método NewBar () do BaseClass, enquanto que:

(new SubClass()).NewBar()

Irá chamar o método NewBar () da subclasse.


realmente como esta frase "informa o compilador"
Mina Gabriel

"fundação do polimorfismo" é outro elegante dizendo :)
Jeremy Thompson

8

Além dos detalhes técnicos, acho que o uso de substituição / virtual comunica muitas informações semânticas sobre o design. Quando você declara um método virtual, indica que espera que as classes de implementação desejem fornecer suas próprias implementações não padrão. Omitir isso em uma classe base também declara a expectativa de que o método padrão seja suficiente para todas as classes de implementação. Da mesma forma, pode-se usar declarações abstratas para forçar as classes de implementação a fornecer sua própria implementação. Mais uma vez, acho que isso comunica muito sobre como o programador espera que o código seja usado. Se eu estivesse escrevendo as classes base e de implementação e me descobrisse usando novas, repensaria seriamente a decisão de não tornar o método virtual no pai e declararia minha intenção especificamente.


As explicações técnicas de new vs override são sólidas, mas essa resposta parece ajudar mais para orientar os desenvolvedores sobre os quais usar.
Sully

4

A diferença entre a palavra-chave de substituição e a nova palavra-chave é que a primeira substitui o método e a segunda oculta o método.

Confira os links a seguir para obter mais informações ...

MSDN e outros


3
  • newA palavra-chave é para ocultar. - significa que você está ocultando seu método em tempo de execução. A saída será baseada no método da classe base.
  • overridepara substituir. - significa que você está chamando seu método de classe derivada com a referência da classe base. A saída será baseada no método de classe derivada.

1

Minha versão da explicação vem do uso de propriedades para ajudar a entender as diferenças.

overrideé simples o suficiente, certo? O tipo subjacente substitui o pai.

newé talvez o enganador (para mim era). Com propriedades, é mais fácil entender:

public class Foo
{
    public bool GetSomething => false;
}

public class Bar : Foo
{
    public new bool GetSomething => true;
}

public static void Main(string[] args)
{
    Foo foo = new Bar();
    Console.WriteLine(foo.GetSomething);

    Bar bar = new Bar();
    Console.WriteLine(bar.GetSomething);
}

Usando um depurador, você pode perceber que Foo foopossui 2 GetSomething propriedades, pois na verdade possui 2 versões da propriedade, FooeBar ' s, e saber qual usar, C # 'picaretas' a propriedade para o tipo atual.

Se você quisesse usar a versão da barra, você teria usado a substituição ou a Foo foosubstituição.

Bar bartem apenas 1 , pois deseja um comportamento completamente novoGetSomething .


0

Não marcar um método com nada significa: Vincule esse método usando o tipo de compilação do objeto, não o tipo de tempo de execução (ligação estática).

Marcando um método com virtualmeios: Vincule esse método usando o tipo de tempo de execução do objeto, não o tipo de tempo de compilação (ligação dinâmica).

Marcar um virtualmétodo de classe base com overridena classe derivada significa: Este é o método a ser vinculado usando o tipo de tempo de execução do objeto (ligação dinâmica).

Marcar um virtualmétodo de classe base com newna classe derivada significa: Este é um método novo, que não tem relação com o mesmo nome na classe base e deve ser vinculado usando o tipo de tempo de compilação do objeto (ligação estática).

Não marcar um virtualmétodo de classe base na classe derivada significa: Este método está marcado como new(ligação estática).

Marcar um método abstractsignifica: Esse método é virtual, mas não declararei um corpo para ele e sua classe também é abstrata (ligação dinâmica).

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.