nueva palabra clave en la firma del método

113

Mientras realizaba una refactorización, terminé creando un método como el siguiente ejemplo. El tipo de datos se ha cambiado por simplicidad.

Anteriormente tenía una declaración de asignación como esta:

MyObject myVar = new MyObject();

Fue refactorizado a esto por accidente:

private static new MyObject CreateSomething()
{
  return new MyObject{"Something New"};
}

Esto fue el resultado de un error de cortar / pegar de mi parte, pero la newpalabra clave in private static newes válida y se compila.

Pregunta : ¿Qué significa la newpalabra clave en la firma de un método? ¿Supongo que es algo introducido en C # 3.0?

¿En qué se diferencia esto de override?

p.campbell
fuente
5
Algunas notas sobre la conveniencia del método de ocultación: blogs.msdn.com/ericlippert/archive/2008/05/21/…
Eric Lippert
2
@Eric .. Gran publicación. Nunca pensé en el método escondido de esa manera, ya que sí, el objeto ES una cosa, pero ahora lo presentamos como otra cosa, así que queremos el comportamiento de la cosa que lo presentamos COMO. Inteligente ...
BFree
1
Una pregunta duplicada en el futuro y he intentado responder con cierto detalle: use una nueva palabra clave en c #
Ken Kin
1
El uso de newcomo un modificador en un método (u otro miembro de tipo) no es "algo introducido en C # 3.0". Ha estado ahí desde la primera versión de C #.
Jeppe Stig Nielsen
1
Hay como 4 duplicados de esta pregunta en SO. En mi opinión, este le dará la mejor comprensión: stackoverflow.com/questions/3117838/… . La contesta @EricLippert.
Raikol Amaro

Respuestas:

101

Nueva referencia de palabras clave de MSDN:

Referencia de MSDN

Aquí hay un ejemplo que encontré en la red de un MVP de Microsoft que tenía sentido: Enlace al original

public class A
{
   public virtual void One();
   public void Two();
}

public class B : A
{
   public override void One();
   public new void Two();
}

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

a.One(); // Calls implementation in B
a.Two(); // Calls implementation in A
b.One(); // Calls implementation in B
b.Two(); // Calls implementation in B

La anulación solo se puede utilizar en casos muy específicos. Desde MSDN:

No puede anular un método estático o no virtual. El método base anulado debe ser virtual, abstracto o anulado.

Por lo tanto, la palabra clave 'nueva' es necesaria para permitirle 'anular' métodos no virtuales y estáticos.

Kelsey
fuente
4
acabo de ejecutar su muestra ... se anulará incluso si no especifica "nuevo", ¿verdad?
Michael Sync
2
@MichaelSync exactamente, entonces, ¿por qué necesitamos mencionar una nueva palabra clave?
ZoomIn
2
"Aunque puede ocultar miembros sin utilizar el nuevo modificador, recibe una advertencia del compilador". según el documento vinculado. Por lo tanto, la palabra clave deja en claro que pretendía ocultarse y no se emite la advertencia.
Jim Counts
1
-1 -> no es necesario en absoluto, el comportamiento será el mismo. Es muy confuso para un novato de qué newse trata, pero creo que literalmente solo está ahí para facilitar la lectura: el compilador simplemente le advierte que cuando llama al método de clase derivada del mismo nombre que base, no obtendrá el método de la clase base que puede creo que ... es extraño ... ¿simplemente por legibilidad?
Don Cheadle
1
En aras de la integridad: si tanto la clase A como la B implementan una interfaz IMyInterface, se llamará a la implementación en la clase Derived. Así IMyInterface c = new B()llamará a la implementación de la clase B. Si solo una clase implementa la interfaz, se llamará al método de la clase que la implementa.
Nullius
61

No, en realidad no es "nuevo" (perdón por el juego de palabras). Básicamente se utiliza para "ocultar" un método. ES DECIR:

public class Base
{
   public virtual void Method(){}
}

public class Derived : Base
{
   public new void Method(){}
}

Si luego haces esto:

Base b = new Derived();
b.Method();

El método en la Base es el que se llamará, NO el del derivado.

Más información: http://www.akadia.com/services/dotnet_polymorphism.html

Re su edición: En el ejemplo que le di, si tuviera que "anular" en lugar de usar "nuevo", cuando llame a b.Method (); El método de la clase derivada se llamaría debido a polimorfismo.

BFree
fuente
clase pública Base {Método vacío virtual público () {Console.WriteLine ("Base"); }} clase pública Derivado: Base {Método público vacío () {Console.WriteLine ("Derivado"); }} Base b = new Derived (); b.Método (); Obtuve "Base" ... Si agrego "nuevo" en la clase derivada, todavía obtengo "Base" ...
Michael Sync
@michael, el método sigue siendo virtual
Rune FS
@MichaelSync: Debería ver una advertencia con ese código; Advertencia Derived.Method () 'oculta el miembro heredado' Base.Method () '. Para que el miembro actual anule esa implementación, agregue la palabra clave anular. De lo contrario, agregue la nueva palabra clave.
Christopher McAtackney
2
@MichaelSync Si se omite la palabra "anular", entonces el comportamiento predeterminado es "nuevo", por ejemplo, ocultación de métodos. Por lo tanto, el hecho de que deje fuera la palabra nuevo no hace ninguna diferencia.
BFree
1
Si. Eso es lo que pienso yo también. No estoy seguro de por qué C # agregó la palabra clave "nueva" ... es solo para hacer que la advertencia desaparezca ... stackoverflow.com/questions/8502661/…
Michael Sync
22

Como explicaron otros, se usa para ocultar un método existente. Es útil para anular un método que no es virtual en la clase principal.

Tenga en cuenta que crear un miembro "nuevo" no es polimórfico. Si lanza el objeto al tipo base, no usará el miembro del tipo derivado.

Si tienes una clase base:

public class BaseClass
{
    public void DoSomething() { }
}

Y luego la clase derivada:

public class DerivedType : BaseClass
{
    public new void DoSomething() {}

}

Si declaras un tipo de DerivedTypey luego lo lanzas, el método DoSomething()no es polimórfico, llamará al método de la clase base, no al derivado.

BaseClass t = new DerivedType();
t.DoSomething();// Calls the "DoSomething()" method of the base class.
Dan Herbert
fuente
1
Llamará al método de la clase base incluso si elimina "nuevo" de DerivedType también ..
Michael Sync
2
Creo que su segundo párrafo clava todo este tema. Cuando se trata de llamadas desde una referencia de clase base , "nuevo" no es polimórfico ... es decir. Obtiene exactamente lo que especifica cuando realiza la llamada. "anular" es polimórfico ... es decir. Obtienes lo que especifica la jerarquía de clases .
Jonathon Reinhart
¿Cómo es esto algo tan vagamente definido? Muy frustrante para un novato. Y no, no tiene nada que ver con polimorfismo, tiene exactamente el mismo comportamiento que simplemente no tener una overridefirma de método
Don Cheadle
¿Qué se esconde de qué? Seguramente no puede ser que newoculte el tipo base del tipo derivado, porque ¿no siempre se puede acceder al tipo base usando la base.<type>notación?
thatWiseGuy
@thatWiseGuy Está "oculto" en el sentido de que no se llamará al método base cuando use la clase derivada en su código. Todavía se puede llamar internamente usando base.<method>, y también se puede llamar externamente si lanza al tipo base en su código. Es técnicamente más exacto pensar en él como "anular" el método base que "ocultarlo".
Dan Herbert
7

De los documentos:

Si el método de la clase derivada está precedido por la nueva palabra clave, el método se define como independiente del método de la clase base.

Lo que esto significa en la práctica:

Si hereda de otra clase y tiene un método que comparte la misma firma, puede definirlo como 'nuevo' para que sea independiente de la clase principal. Esto significa que si tiene una referencia a la clase 'principal', esa implementación se ejecutará, si tiene una referencia a la clase secundaria, esa implementación se ejecutará.

Personalmente, trato de evitar la palabra clave "nueva", ya que normalmente significa que tengo una jerarquía de clases incorrecta, pero hay ocasiones en las que puede ser útil. Un lugar es para el control de versiones y la compatibilidad con versiones anteriores.

Hay mucha información en MSDN para esto .

Jonnii
fuente
El hecho de que ocurra este comportamiento no tiene nada que ver con newestar allí. Es sintáctico / legible.
Don Cheadle
3

Significa que el método reemplaza un método por el mismo nombre heredado por la clase base. En su caso, probablemente no tenga un método con ese nombre en la clase base, lo que significa que la nueva palabra clave es totalmente superflua.

Robin Clowers
fuente
3

En pocas palabras: NO es obligatorio, NO cambia NINGÚN comportamiento, y está PURAMENTE ahí para facilitar la lectura.

Es por eso que en VS verá un poco ondulado, pero su código se compilará y se ejecutará perfectamente bien y como se esperaba.

Uno tiene que preguntarse si realmente valió la pena crear la newpalabra clave cuando todo lo que significa es que el desarrollador reconoce "Sí, sé que estoy ocultando un método base, sí, sé que no estoy haciendo nada relacionado con virtualo overriden(polimorfismo) - Realmente quiero crear su propio método ".

Es un poco extraño para mí, pero tal vez solo porque vengo de un Javaentorno y existe esta diferencia fundamental entre C#herencia y Java: En Java, los métodos son virtuales de forma predeterminada a menos que lo especifique final. En C#, los métodos son finales / concretos por defecto a menos que lo especifique virtual.

Don Cheadle
fuente
1

Desde MSDN :

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

Doug R
fuente
0

Tenga cuidado con este problema.
Tiene un método definido en una interfaz que se implementa en una clase base. Luego, crea una clase derivada que oculta el método de la interfaz, pero no declara específicamente que la clase derivada implementa la interfaz. Si luego llama al método a través de una referencia a la interfaz, se llamará al método de la clase base. Sin embargo, si su clase derivada implementa específicamente la interfaz, entonces su método se llamará independientemente del tipo de referencia que se use.

interface IMethodToHide
{
    string MethodToHide();
}

class BaseWithMethodToHide : IMethodToHide
{
    public string MethodToHide()
    {
        return "BaseWithMethodToHide";
    }
}

class DerivedNotImplementingInterface   : BaseWithMethodToHide
{
    new public string MethodToHide()
    {
        return "DerivedNotImplementingInterface";
    }
}

class DerivedImplementingInterface : BaseWithMethodToHide, IMethodToHide
{
    new public string MethodToHide()
    {
        return "DerivedImplementingInterface";
    }
}

class Program
{
    static void Main()
    {
        var oNoI = new DerivedNotImplementingInterface();
        IMethodToHide ioNoI = new DerivedNotImplementingInterface();

        Console.WriteLine("reference to the object type DerivedNotImplementingInterface calls the method in the class " 
            + oNoI.MethodToHide());
        // calls DerivedNotImplementingInterface.MethodToHide()
        Console.WriteLine("reference to a DerivedNotImplementingInterface object via the interfce IMethodToHide calls the method in the class " 
            + ioNoI.MethodToHide());
        // calls BaseWithMethodToHide.MethodToHide()
        Console.ReadLine();

        var oI = new DerivedImplementingInterface();
        IMethodToHide ioI = new DerivedImplementingInterface();

        Console.WriteLine("reference to the object type DerivedImplementingInterface calls the method in the class " 
            + oI.MethodToHide());
        // calls DerivedImplementingInterface.MethodToHide()
        Console.WriteLine("reference to a DerivedImplementingInterface object via the interfce IMethodToHide calls the method in the class " 
            + ioI.MethodToHide());
        // calls DerivedImplementingInterface.MethodToHide()
        Console.ReadLine();

    }
}
Abrigo de cintura
fuente