Quais são alguns usos práticos do "novo" modificador em C # com relação à ocultação?

21

Eu e um colega de trabalho estávamos analisando o comportamento da newpalavra - chave em C #, conforme ela se aplica ao conceito de ocultação. A partir da documentação :

Use o novo modificador para ocultar explicitamente um membro herdado de uma classe base. Para ocultar um membro herdado, declare-o na classe derivada usando o mesmo nome e modifique-o com o novo modificador.

Lemos a documentação e entendemos o que basicamente faz e como faz. O que realmente não conseguimos entender é por que você precisaria fazê-lo em primeiro lugar. O modificador existe desde 2003, e nós dois trabalhamos com o .Net por mais tempo do que isso e nunca foi lançado.

Quando esse comportamento seria necessário no sentido prático (por exemplo, quando aplicado a um caso de negócios)? Esse é um recurso que sobreviveu à sua utilidade ou é simplesmente incomum o suficiente no que fazemos (especificamente fazemos formulários da Web e aplicativos MVC e alguns pequenos fatores WinForms e WPF)? Ao experimentar esta palavra-chave e brincar com ela, encontramos alguns comportamentos que ela permite que pareçam um pouco perigosos se mal utilizados.

Isso parece um pouco aberto, mas estamos procurando um caso de uso específico que possa ser aplicado a um aplicativo de negócios que considere útil essa ferramenta específica.

Joel Etherton
fonte
9
Talvez você tenha lido também, mas há um artigo interessante de Eric Lippert (desenvolvedor do compilador C #) sobre por que a ocultação de métodos foi adicionada ao C #: blogs.msdn.com/b/ericlippert/archive/2008/05/21/… . Isso responde parte da sua pergunta, mas não tenho um caso de negócios pronto para você, então coloquei isso em um comentário.
Jalayn
3
@Jalayn: O caso de negócios é discutido por Eric neste post: blogs.msdn.com/b/ericlippert/archive/2004/01/07/…
Brian

Respostas:

22

Você pode usá-lo para imitar a covariância do tipo de retorno. Explicação de Eric Lippert . Eric fornece este exemplo de código:

abstract class Enclosure
{
    protected abstract Animal GetContents();
    public Animal Contents() { return this.GetContents(); }
}
class Aquarium : Enclosure
{
    public new Fish Contents() { ... }
    protected override Animal GetContents() { return this.Contents(); }
}

Esta é uma solução alternativa. public override Fish Contents() { ... }não é legal, apesar de ser seguro.

Em geral, você não deve usar ocultação de método, pois isso é confuso para os consumidores da sua classe (o exemplo específico acima não sofre com esse problema). Apenas dê outro nome ao seu novo método, se você não quiser substituir um método existente.

Uma situação provável no mundo real em que você precisaria ocultar o método é se o provedor de uma classe base adicionou um método genérico que você já havia adicionado a uma classe derivada. Esse programa compila (e avisa) sem a nova palavra-chave, mas a adição newdiz: "Eu sei que minha versão deste método está substituindo a versão da classe base. Isso é horrível e confuso, mas estamos presos a ela". Isso ainda é melhor do que forçar a classe derivada a renomear seu método.

Apenas permitir que o método derivado seja tratado como uma substituição causaria problemas. Ignorando quaisquer preocupações com a implementação do compilador, o novo método é semanticamente diferente do método base, mas o polimorfismo faria com que o novo método fosse chamado quando solicitado a chamar um método com o mesmo nome.

Essa situação é discutida em detalhes neste post por Eric Lippert.

Brian
fonte
1
+1 por newser um marcador de "isso é horrível e confuso".
Avner Shahar-Kashtan
Eu acho que o exemplo de Eric é muito útil, mesmo que eu esteja em uma situação semelhante ... Eu tenho uma classe base implementando um método não trivial que retorna TBase e uma classe derivada que deve retornar um TDerived. Nesse caso, parece um mal necessário.
Kyle Baran
4

Acho que existe, caso você precise fazer algo que os designers de linguagem talvez não tenham pensado. O C # foi de várias maneiras uma reação às versões anteriores do java. E uma coisa que o java fez foi explicitar os desenvolvedores de maneira explícita para eliminar as possibilidades de desenvolvedores se atirarem nos pés. O C # adotou uma abordagem um pouco diferente e deu aos desenvolvedores um pouco mais de poder para permitir que os desenvolvedores tivessem mais oportunidades de se atirar nos pés. Um exemplo é a unsafepalavra - chave Esta newpalavra-chave é outra.

Agora, provavelmente não é tão útil quanto, unsafemas quando você entra em uma especificação de idioma, é difícil sair de uma especificação de idioma.

Wyatt Barnett
fonte
5
Java não possui novidade, pois trata todos os métodos como virtuais. De acordo com Eric Lippert, a motivação para apoiar as novas é resolver o frágil problema da classe base. A existência de novas é necessária para o uso comercial no mundo real, não apenas para o uso em bibliotecas de baixo nível. Java sem novos métodos (e sem métodos não virtuais) significa que, se uma classe base introduzir um novo método que já esteja sendo usado nas classes derivadas, o código existente poderá ser interrompido, apesar do fato de o desenvolvedor da classe base ser permitido alheio ao código que o consome.
21712 Brian As
3

Está dizendo ao leitor que "ocultei deliberadamente a implementação desse método pela classe base", em vez de acidentalmente.

Vegio
fonte
5
Isso me parece implorar a pergunta. O OP sabe o que faz, mas quer saber o porquê.
21712 Brian
0

Você pode querer disponibilizar o membro anterior por outro nome:

class VehicleClass
{
  public int AnyProperty
  {
    get; set;
  }

  public int AnyFunction() { return 0; }
} // VehicleClass

class IntermediateClass : VehicleClass
{
  public int PreviousAnyProperty
  {
    get { return AnyProperty; }
    set { AnyProperty = value  }
  }

  public int PreviousAnyFunction() { return AnyFunction(); }
} // IntermediateClass 

class CarClass : IntermediateClass
{
  public new int AnyProperty
  {
    get ; set ;
  }

  public new int AnyFunction() { return 5; }
} // class CarClass

class ExampleClass
{

  public static void Main()
  {
    using (CarClass MyCar = new CarClass())
    {
      int AnyInt1 = MyCar.PreviousAnyProperty;
      MyCar.PreviousAnyProperty = 7;

      int AnyInt2 = MyCar.PreviousAnyFunction();

      int AnyInt3 = MyCar.AnyProperty;
      MyCar.AnyProperty = 45;

      int AnyInt4 = MyCar.AnyFunction();
    }
  } // static void Main()

} // class CarClass

Felicidades.

umlcat
fonte
Estou familiarizado com o funcionamento, mas minha pergunta é: Por que você gostaria que o membro anterior estivesse disponível por outro nome? Isso quebra todos os tipos de contratos, torna inúteis certas peças genéricas.
Joel Etherton
@Joel Etherton: Como você já deve saber, às vezes os desenvolvedores precisam "escolher" o código de programação de outras pessoas. E podemos não estar autorizados a modificar, talvez estender classes. E pode exigir o uso de ambos, o membro anterior e o novo.
umlcat
0

Eu achei bastante útil ao criar um conjunto de testes para código legado. Ele me permite ocultar dependências externas, como consultar um banco de dados dentro de um método usado por outros métodos. Para poder testar os outros métodos, posso ocultar a lógica original que depende de recursos externos sem precisar adicionar a palavra-chave virtual a esses métodos nas classes de teste.

Spacy
fonte