¿Cuáles son algunos usos prácticos del "nuevo" modificador en C # con respecto a la ocultación?

21

Un compañero de trabajo y yo estábamos analizando el comportamiento de la newpalabra clave en C # en lo que respecta al concepto de ocultación. De la documentación :

Use el nuevo modificador para ocultar explícitamente un miembro heredado de una clase base. Para ocultar un miembro heredado, declare en la clase derivada con el mismo nombre y modifíquelo con el nuevo modificador.

Hemos leído la documentación y entendemos qué hace básicamente y cómo lo hace. Lo que realmente no pudimos entender es por qué tendría que hacerlo en primer lugar. El modificador ha estado allí desde 2003, y ambos hemos estado trabajando con .Net durante más tiempo que eso y nunca ha aparecido.

¿Cuándo sería necesario este comportamiento en un sentido práctico (p. Ej .: aplicado a un caso de negocios)? ¿Es esta una característica que ha dejado de ser útil o es lo que hace que es bastante poco común en lo que hacemos (específicamente hacemos formularios web y aplicaciones MVC y algunos WinForms y WPF de pequeño factor)? Al probar esta palabra clave y jugar con ella, encontramos algunos comportamientos que permite que parezcan un poco peligrosos si se usan incorrectamente.

Esto suena un poco abierto, pero estamos buscando un caso de uso específico que pueda aplicarse a una aplicación comercial que considere útil esta herramienta en particular.

Joel Etherton
fuente
99
Quizás también lo haya leído, pero hay un interesante artículo de Eric Lippert (desarrollador del compilador de C #) sobre por qué se agregó la ocultación de métodos a C #: blogs.msdn.com/b/ericlippert/archive/2008/05/21/… . Eso responde parte de su pregunta, pero no tengo un caso de negocios listo para usted, así que lo puse en un comentario.
Jalayn
3
@Jalayn: Eric discute el caso de negocios en esta publicación: blogs.msdn.com/b/ericlippert/archive/2004/01/07/…
Brian

Respuestas:

22

Puede usarlo para imitar la covarianza de tipo de retorno. Explicación de Eric Lippert . Eric proporciona este código de ejemplo:

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 es una solución alternativa. public override Fish Contents() { ... }no es legal, a pesar de ser seguro.

En general, se debe no utilizar el método de ocultación, ya que está confundiendo a los consumidores de la clase (el ejemplo específico anterior no sufren de este problema). Simplemente asigne a su nuevo método otro nombre si no desea anular un método existente.

Una situación probable del mundo real en la que necesitaría ocultar métodos es si el proveedor de una clase base agrega un método genérico que ya había agregado a una clase derivada. Dicho programa compilará (y dará advertencias) sin la nueva palabra clave, pero la adición newdice: "Sé que mi versión de este método está reemplazando la versión de la clase base. Esto es horrible y confuso, pero estamos atascados con él". Eso es aún mejor que forzar a la clase derivada a cambiar el nombre de su método.

Solo permitir que el método derivado sea tratado como una anulación podría causar problemas. Ignorando cualquier preocupación con la implementación del compilador, el nuevo método es semánticamente diferente del método base, pero el polimorfismo provocaría que se llamara al nuevo método cuando se le pidiera que llamara a un método con el mismo nombre.

Esta situación es discutida en detalle en esta publicación por Eric Lippert.

Brian
fuente
1
+1 por newser un marcador de "esto es horrible y confuso".
Avner Shahar-Kashtan
Creo que el ejemplo de Eric es muy útil, aunque solo sea porque estoy en una situación similar ... Tengo una clase base que implementa un método no trivial que devuelve TBase, y una clase derivada que debería devolver un TDerived. En este caso, se siente como un mal necesario.
Kyle Baran
4

Creo que está allí en caso de que necesites hacer algo que los diseñadores de idiomas no hayan pensado. C # fue en muchos sentidos una reacción a las primeras versiones de Java. Y una cosa que hizo Java fue explícitamente encasillar a los desarrolladores para eliminar las posibilidades de que los desarrolladores se disparen en los pies. C # adoptó un enfoque ligeramente diferente y les dio a los desarrolladores un poco más de poder para permitirles a los desarrolladores algunas oportunidades más para dispararse en los pies. Un ejemplo es la unsafepalabra clave. Esta newpalabra clave es otra.

Ahora, probablemente no sea tan útil como, unsafepero una vez que entras en una especificación de idioma, es difícil salir de una especificación de idioma.

Wyatt Barnett
fuente
55
Java no tiene novedades porque Java trata todos los métodos como virtuales. Según Eric Lippert, la motivación para apoyar nuevos es resolver el problema de la clase base frágil. La existencia de nuevas es necesaria para el uso comercial en el mundo real, no solo para el uso de bibliotecas de bajo nivel. Java no tener nuevos (y no tener métodos no virtuales) significa que si una clase base introduce un nuevo método que ya está en uso dentro de las clases derivadas, el código existente puede romperse, a pesar del hecho de que el desarrollador de la clase base debería estar permitido ser ajeno al código que lo consume.
Brian
3

Le dice al lector que "deliberadamente oculté la implementación de la clase base de este método", en lugar de accidentalmente.

Vegio
fuente
55
Esto me parece rogar la pregunta. El OP sabe lo que hace pero quiere saber por qué.
Brian
0

Es posible que desee tener el miembro anterior disponible con otro nombre:

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

Aclamaciones.

umlcat
fuente
Estoy familiarizado con cómo funciona, pero mi pregunta es: ¿por qué querría tener al miembro anterior disponible a través de otro nombre? Esto rompe todo tipo de contratos, deja ciertas piezas genéricas inútiles.
Joel Etherton
@Joel Etherton: Como ya sabrá, a veces los desarrolladores tienen que "elegir" el código de programación de otras personas. Y es posible que no estemos autorizados a modificar, tal vez extender las clases. Y puede requerir el uso de ambos, el miembro anterior y el nuevo.
umlcat
0

Lo he encontrado bastante útil al crear un conjunto de pruebas para código heredado. Me permite ocultar dependencias externas, como consultar una base de datos dentro de un método que utilizan otros métodos. Para poder probar los otros métodos, puedo ocultar la lógica original que depende de recursos externos sin necesidad de agregar la palabra clave virtual a esos métodos en las clases de prueba.

Spacy
fuente