Diferença entre sombreamento e substituição em C #?


100

Qual é a diferença entre sombreamento e substituir um método em C #?

Respostas:


152

Bem, herança ...

suponha que você tenha estas classes:

class A {
   public int Foo(){ return 5;}
   public virtual int Bar(){return 5;}
}
class B : A{
   public new int Foo() { return 1;}     //shadow
   public override int Bar() {return 1;} //override
}

então quando você chama isso de:

A clA = new A();
B clB = new B();

Console.WriteLine(clA.Foo()); // output 5
Console.WriteLine(clA.Bar()); // output 5
Console.WriteLine(clB.Foo()); // output 1
Console.WriteLine(clB.Bar()); // output 1

//now let's cast B to an A class
Console.WriteLine(((A)clB).Foo()); // output 5 <<<-- shadow
Console.WriteLine(((A)clB).Bar()); // output 1

Suponha que você tenha uma classe base e use a classe base em todo o seu código em vez das classes herdadas e use shadow, ela retornará os valores que a classe base retorna em vez de seguir a árvore de herança do tipo real do objeto.

Execute o código aqui

Espero que esteja fazendo sentido :)


16
A substituição fornece polimorfismo, o sombreamento fornece uma implementação diferente naquele nível da hierarquia, mas não polimorficamente.
David Rodríguez - dribeas

@dribeas, Que definição de polimorfismo você usaria para chegar a essa conclusão?
AnthonyWJones

9
@AnthonyWJones, Polimorfismo é a capacidade de um tipo (derivado) ser usado como um tipo (base) diferente, onde a implementação real (comportamento) é aquela do tipo específico real (derivado). Se você substituir, o método derivado será chamado por meio da referência à base, mas se você fizer sombra, isso não é verdade
David Rodríguez - dribeas

2
Parece-me que você tem um erro em seu código de amostra. O casting deveria ser ((A) clB) .Foo (), não deveria?
tendência de

1
Não, porque o bar é virtual, então ele ainda chamará o Bar B
Stormenet

32

Sombreamento é, na verdade, linguagem VB para o que chamaríamos de ocultar em C #.

Freqüentemente, ocultação (sombreamento em VB) e sobreposição são mostrados como resposta de Stormenet .

Um método virtual é mostrado para ser substituído por uma subclasse e chamadas para esse método, mesmo no tipo de superclasse ou de dentro do código da superclasse, chamarão a implementação de substituição da subclasse.

Em seguida, um método concreto é mostrado (um não marcado como virtual ou abstrato) sendo escondido usando o new palavra chave ao definir um método com uma assinatura idêntica na subclasse. Nesse caso, quando o método é chamado no tipo de superclasse, a implementação original é usada, a nova implementação está disponível apenas na subclasse.

No entanto, o que muitas vezes é esquecido é que também é possível ocultar um método virtual.

class A
{
    public virtual void DoStuff() { // original implementation }
}

class B : A
{
    public new void DoStuff() {  //new implementation }
}

B b = new B();
A a = b;

b.DoStuff(); //calls new implementation
a.DoStuff(); //calls original implementation.

Observe no exemplo acima, DoStuff torna-se concreto e não pode ser sobrescrito. No entanto, também é possível usar as palavras virtual- newchave e juntas.

class A
{
    public virtual void DoStuff() { // original implementation }
}

class B : A
{
    public new virtual void DoStuff() {  //new implementation }
}

class C : B
{
    public override void DoStuff() { //replacement implementation }
}

C c = new C();
B b = c;
A a = b;

c.DoStuff(); //calls replacement implementation
b.DoStuff(); //calls replacement implementation
a.DoStuff(); //calls original implementation.

Observe que, apesar de todos os métodos envolvidos serem virtuais, a substituição em C não afeta o método virtual em A porque o uso de newem B oculta a implementação de A.

Edit: Foi observado nos comentários a esta resposta que o texto acima pode ser perigoso ou pelo menos não particularmente útil. Eu diria que sim, pode ser perigoso e estaria lá fora se fosse útil.

Em particular, você pode ter todos os tipos de problemas se também alterar os modificadores de acessibilidade. Por exemplo:-

public class Foo
{
    internal Foo() { }
    protected virtual string Thing() { return "foo"; }
}

public class Bar : Foo
{
 internal new string Thing() { return "bar"; }
}

Para um herdeiro externo de Bar, Fooa implementação de Thing () permanece acessível e substituível. Tudo legal e explicável de acordo com as regras do tipo .NET, a menos que seja pouco intuitivo.

Publiquei esta resposta para aprofundar a compreensão de como as coisas funcionam, não como uma sugestão de técnicas que podem ser usadas livremente.


2
É bom saber disso. Embora seu uso possa ser perigoso :)
Stormenet

2
Todas as ferramentas podem ser perigosas quando não usadas corretamente.
AnthonyWJones

1
Mas, sua usabilidade parece realmente questionável! Existe algum aplicativo de código aberto 'sério' usando isso?
Jox

4
Usabilidade? Você quer dizer utilidade? Algumas coisas que estão à margem podem parecer inúteis até que você precise delas.
AnthonyWJones

Parece que Sombreamento entra em cena SOMENTE quando o virtual e novos jogos. @ Stormenet está explicando apenas a coisa de override porque é o comportamento normal da classe chamar seu próprio método, a menos que seja virtual.
Sumit Kapadia

6

Acho que a principal diferença é que com o sombreamento, você essencialmente reutiliza o nome e simplesmente ignora o uso da superclasse. Com a substituição, você está alterando a implementação, mas não a acessibilidade e a assinatura (por exemplo, tipos de parâmetro e retorno). Consulte http://www.geekinterview.com/question_details/19331 .


5

Shadowing é um conceito VB.NET. Em C #, Shadowing é conhecido como Hiding. Ele oculta o método da classe derivada. Isso é feito usando a palavra-chave 'novo'.

A palavra-chave Override é usada para fornecer uma implementação completamente nova de um método de classe base (que é marcado como 'Virtual') na classe derivada.


Você quer dizer. Ele oculta o método da classe derivada, em vez de usar o método da classe base. ?
shaijut

0

Basicamente, se você tiver algo como abaixo,

Class A
{
}

Class B:A
{
}

A a = new B();

Qualquer método que você chamar no objeto 'a' será feito no tipo de 'a' (aqui o tipo é 'A') Mas se você implementar o mesmo método na classe B que já está presente na classe A, o compilador irá dar-lhe um aviso para usar uma palavra-chave "Novo". Se você usar "Novo", o aviso desaparecerá. Fora isso, não há diferença entre usar "Novo" ou não usá-lo na classe herdada.

Em algumas situações, você pode precisar chamar um método da classe de referência que a instância específica mantém naquele momento, em vez de chamar um método no tipo de objeto. No caso acima, a referência que contém é 'B', mas o tipo é 'A'. Portanto, se você quiser que a chamada do método aconteça em 'B', use Virtual e override para fazer isso.

Espero que isto ajude...

Daniel Sandeep.


0

Se houver um caso em que você não pode alterar o conteúdo de uma classe, digamos A, mas você deseja usar seus alguns métodos junto com você tem um método cujo nome é comum, você pode usar sua própria implementação de método pela newpalavra - chave.

O ponto crucial é usar que tanto a referência quanto o objeto devem ser do mesmo tipo.

class A
{
    public void Test()
    {
        Console.WriteLine("base");
    }
}

class B : A
{
    public new void Test()
    {
        Console.WriteLine("sub");
    }

    public static void Main(string[] args)
    {
        A a = new A();
        B aa = new B();
        a.Test();
        aa.Test();
    }
}
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.