Pergunta

Essa pergunta já tem resposta aqui:

Estou um pouco confuso sobre substituir vs.ocultando um método em C#.Os usos práticos de cada um também seriam apreciados, bem como uma explicação para quando um usaria cada um.

Estou confuso sobre a substituição - por que substituímos?O que aprendi até agora é que através da substituição podemos fornecer a implementação desejada para um método de uma classe derivada, sem alterar a assinatura.

Se eu não substituir o método da superclasse e fizer alterações no método da subclasse, isso fará alterações no método da superclasse?

Também estou confuso sobre o seguinte – o que isso demonstra?

class A
{
    virtual m1()
    {
        console.writeline("Bye to all");
    }
}

class B : A
{
    override m1()
    {
        console.writeLine("Hi to all");
    }
}

class C
{
    A a = new A();
    B b = new B();
    a = b; (what is this)
    a.m1(); // what this will print and why?

    b = a; // what happens here?
}
Foi útil?

Solução

Considerar:

public class BaseClass
{
  public void WriteNum()
  {
    Console.WriteLine(12);
  }
  public virtual void WriteStr()
  {
    Console.WriteLine("abc");
  }
}

public class DerivedClass : BaseClass
{
  public new void WriteNum()
  {
    Console.WriteLine(42);
  }
  public override void WriteStr()
  {
    Console.WriteLine("xyz");
  }
}
/* ... */
BaseClass isReallyBase = new BaseClass();
BaseClass isReallyDerived = new DerivedClass();
DerivedClass isClearlyDerived = new DerivedClass();

isReallyBase.WriteNum(); // writes 12
isReallyBase.WriteStr(); // writes abc
isReallyDerived.WriteNum(); // writes 12
isReallyDerived.WriteStr(); // writes xyz
isClearlyDerived.WriteNum(); // writes 42
isClearlyDerived.writeStr(); // writes xyz

A substituição é a maneira clássica de OO pela qual uma classe derivada pode ter um comportamento mais específico do que uma classe base (em alguns idiomas que você não tem escolha a não ser fazê -lo). Quando um método virtual é chamado em um objeto, a versão mais derivada do método é chamada. Portanto, apesar de estarmos lidando com isReallyDerived como um BaseClass então funcionalidade definida em DerivedClass é usado.

Esconder significa que temos um método completamente diferente. Quando ligamos WriteNum() sobre isReallyDerived Então não há como saber que há um diferente WriteNum() sobre DerivedClass Portanto, não é chamado. Só pode ser chamado quando estamos lidando com o objeto Como uma DerivedClass.

Na maioria das vezes, esconder é ruim. Geralmente, você deve ter um método como virtual, se é provável que seja alterado em uma classe derivada e substituí -lo na classe derivada. No entanto, há duas coisas para as quais é útil:

  1. Compatibilidade encaminhada. Se DerivedClass tinha um DoStuff() método, e depois mais tarde BaseClass foi alterado para adicionar um DoStuff() Método (lembre -se de que eles podem ser escritos por pessoas diferentes e existem em diferentes assembléias), então a proibição do esconder DerivedClass Buggy sem mudar. Além disso, se o novo DoStuff() sobre BaseClass foi virtual, então automaticamente fazendo isso em DerivedClass Uma substituição pode levar ao chamado o método pré-existente quando não deveria. Portanto, é bom que o esconder seja o padrão (nós usamos new Para deixar claro, definitivamente queremos nos esconder, mas deixá -lo esconde e emite um aviso na compilação).

  2. Covariância do pobre homem. Considere a Clone() método em BaseClass que retorna um novo BaseClass Essa é uma cópia disso criada. Na substituição DerivedClass Isso criará um DerivedClass Mas devolva -o como um BaseClass, o que não é tão útil. O que poderíamos fazer é ter uma proteção virtual CreateClone() Isso é substituído. Dentro BaseClass nós temos uma Clone() isso retorna o resultado disso - e tudo está bem - em DerivedClass escondemos isso com um novo Clone() que retorna a DerivedClass. Chamando Clone() sobre BaseClass sempre retornará um BaseClass referência, que será um BaseClass valor ou a DerivedClass valor conforme apropriado. Chamando Clone() sobre DerivedClass retornará um DerivedClass valor, que é o que queremos nesse contexto. Existem outras variantes desse princípio, no entanto, deve -se notar que todas são muito raras.

Uma coisa importante a ser observada com o segundo caso é que usamos esconder -se precisamente a remover surpresas para o código de chamada, como a pessoa que usa DerivedClass pode razoavelmente esperar seu Clone() para devolver um DerivedClass. Os resultados de qualquer uma das maneiras pelas quais ele poderia ser chamado são mantidos consistentes entre si. A maioria dos casos de ocultar riscos introduzindo surpresas, e é por isso que elas geralmente são desaprovadas. Este é justificado com precisão porque resolve o próprio problema que o esconderijo geralmente apresenta.

Ao todo, o esconderijo às vezes é necessário, raramente útil, mas geralmente ruim, por isso tenha muito cuidado com isso.

Outras dicas

A substituição ocorre quando você fornece um novo override implementação de um método em uma classe descendente quando esse método é definido na classe base como virtual.

Ocultar é quando você fornece uma nova implementação de um método em uma classe descendente quando esse método é não definido na classe base como virtual, ou quando sua nova implementação não especifica override.

Esconder-se muitas vezes é ruim;geralmente você deve tentar não fazer isso, se puder evitá-lo.Ocultar pode fazer com que coisas inesperadas aconteçam, porque os métodos ocultos são usados ​​apenas quando chamados em uma variável do tipo real que você definiu, e não se estiver usando uma referência de classe base...por outro lado, os métodos virtuais que são substituídos terminarão com a versão adequada do método sendo chamada, mesmo quando chamados usando a referência da classe base em uma classe filha.

Por exemplo, considere estas classes:

public class BaseClass
{
  public virtual void Method1()  //Virtual method
  {
    Console.WriteLine("Running BaseClass Method1");
  }
  public void Method2()  //Not a virtual method
  {
    Console.WriteLine("Running BaseClass Method2");
  }
}
public class InheritedClass : BaseClass
{
  public override void Method1()  //Overriding the base virtual method.
  {
    Console.WriteLine("Running InheritedClass Method1");
  }
  public new void Method2()  //Can't override the base method; must 'new' it.
  {
    Console.WriteLine("Running InheritedClass Method2");
  }
}

Vamos chamá-lo assim, com uma instância de InheritedClass, em uma referência correspondente:

InheritedClass inherited = new InheritedClass();
inherited.Method1();
inherited.Method2();

Isso retorna o que você deveria esperar;ambos os métodos dizem que estão executando as versões InheritedClass.

Executando o método InheritedClass1
Executando Método HerdadoClass2

Este código cria uma instância do mesmo, InheritedClass, mas a armazena em uma referência BaseClass:

BaseClass baseRef = new InheritedClass();
baseRef.Method1();
baseRef.Method2();

Normalmente, sob os princípios OOP, você deve esperar o mesmo resultado do exemplo acima.Mas você não obtém a mesma saída:

Executando o método InheritedClass1
Executando o Método BaseClass2

Quando você escreveu o código InheritedClass, talvez você quisesse que todas as chamadas fossem Method2() para executar o código que você escreveu nele.Normalmente, seria assim que funcionaria - supondo que você esteja trabalhando com um virtual método que você substituiu.Mas porque você está usando um new/hidden , ele chama a versão na referência que você está usando.


Se esse é o comportamento que você realmente quero, então;ai está.Mas eu sugiro fortemente que, se é isso que você deseja, pode haver um problema arquitetônico maior com o código.

A substituição do método é simples, substituindo uma implementação padrão de um método de classe base na classe derivada.

Esconder

Como

class Foo  
{  
  public virtual void foo1()  
  {  

  }  
}  

class Bar:Foo  
{  
  public new virtual void foo1()  
  {   

  }  
}  

Agora, se você fizer outro bar1 de classe que é derivado do bar, poderá substituir o Foo1 que é definido em bar.

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