Compartir a través de


Saber cuándo usar invalidaciones y nuevas palabras clave (Guía de programación de C#)

En C#, un método de una clase derivada puede tener el mismo nombre que un método en la clase base. Puede especificar cómo interactúan los métodos mediante las palabras clave new e override . El override modificador extiende el método de clase virtual base y el new modificador oculta un método de clase base accesible. La diferencia se ilustra en los ejemplos de este tema.

En una aplicación de consola, declare las dos clases siguientes y BaseClassDerivedClass. DerivedClass hereda de BaseClass.

class BaseClass  
{  
    public void Method1()  
    {  
        Console.WriteLine("Base - Method1");  
    }  
}  
  
class DerivedClass : BaseClass  
{  
    public void Method2()  
    {  
        Console.WriteLine("Derived - Method2");  
    }  
}  

En el Main método, declarar variables bc, dc y bcdc.

  • bc es de tipo BaseClassy su valor es de tipo BaseClass.

  • dc es de tipo DerivedClassy su valor es de tipo DerivedClass.

  • bcdc es de tipo BaseClassy su valor es de tipo DerivedClass. Esta es la variable a la que prestar atención.

Dado que bc y bcdc tienen el tipo BaseClass, solo pueden tener acceso directo a Method1, a menos que se use la conversión. La variable dc puede acceder tanto a Method1 como a Method2. Estas relaciones se muestran en el código siguiente.

class Program  
{  
    static void Main(string[] args)  
    {  
        BaseClass bc = new BaseClass();  
        DerivedClass dc = new DerivedClass();  
        BaseClass bcdc = new DerivedClass();  
  
        bc.Method1();  
        dc.Method1();  
        dc.Method2();  
        bcdc.Method1();  
    }  
    // Output:  
    // Base - Method1  
    // Base - Method1  
    // Derived - Method2  
    // Base - Method1  
}  

A continuación, agregue el método siguiente Method2 a BaseClass. La firma de este método coincide con la firma del Method2 método en DerivedClass.

public void Method2()  
{  
    Console.WriteLine("Base - Method2");  
}  

Dado que BaseClass ahora tiene un Method2 método, se puede agregar una segunda instrucción de llamada para las variables BaseClass, bc y bcdc, como se muestra en el código siguiente.

bc.Method1();  
bc.Method2();  
dc.Method1();  
dc.Method2();  
bcdc.Method1();  
bcdc.Method2();  

Al compilar el proyecto, verá que la adición del Method2 método en BaseClass provoca una advertencia. La advertencia indica que el Method2 método de DerivedClass oculta el Method2 método en BaseClass. Se recomienda usar la new palabra clave en la Method2 definición si piensa provocar ese resultado. Como alternativa, podría cambiar el nombre de uno de los Method2 métodos para resolver la advertencia, pero eso no siempre es práctico.

Antes de agregar new, ejecute el programa para ver la salida generada por las instrucciones de llamada adicionales. Se muestran los siguientes resultados.

// Output:  
// Base - Method1  
// Base - Method2  
// Base - Method1  
// Derived - Method2  
// Base - Method1  
// Base - Method2  

La new palabra clave conserva las relaciones que generan esa salida, pero suprime la advertencia. Las variables de tipo BaseClass siguen teniendo acceso a los miembros de BaseClass y la variable de tipo DerivedClass sigue teniendo acceso a los miembros de DerivedClass en primer lugar y, después, tiene en cuenta los miembros heredados de BaseClass.

Para suprimir la advertencia, agregue el new modificador a la definición de Method2 en DerivedClass, como se muestra en el código siguiente. El modificador se puede agregar antes o después de public.

public new void Method2()  
{  
    Console.WriteLine("Derived - Method2");  
}  

Vuelva a ejecutar el programa para comprobar que la salida no ha cambiado. Compruebe también que la advertencia ya no aparece. Mediante el uso de new, afirma que es consciente de que el miembro que modifica oculta un miembro heredado de la clase base. Para obtener más información sobre la ocultación de nombres a través de la herencia, vea nuevo Modificador.

Para comparar este comportamiento con los efectos de usar override, agregue el método siguiente a DerivedClass. El modificador override se puede agregar antes o después de public.

public override void Method1()  
{  
    Console.WriteLine("Derived - Method1");  
}  

Agregue el virtual modificador a la definición de Method1 en BaseClass. El modificador virtual se puede agregar antes o después de public.

public virtual void Method1()  
{  
    Console.WriteLine("Base - Method1");  
}  

Vuelva a ejecutar el proyecto. Observe especialmente las dos últimas líneas del resultado siguiente.

// Output:  
// Base - Method1  
// Base - Method2  
// Derived - Method1  
// Derived - Method2  
// Derived - Method1  
// Base - Method2  

El uso del override modificador permite bcdc tener acceso al Method1 método definido en DerivedClass. Normalmente, es el comportamiento deseado en las jerarquías de herencia. Quiere que los objetos que tengan valores creados a partir de la clase derivada usen los métodos definidos en la clase derivada. Este comportamiento se logra mediante override para ampliar el método de clase base.

El código siguiente contiene el ejemplo completo.

using System;  
using System.Text;  
  
namespace OverrideAndNew  
{  
    class Program  
    {  
        static void Main(string[] args)  
        {  
            BaseClass bc = new BaseClass();  
            DerivedClass dc = new DerivedClass();  
            BaseClass bcdc = new DerivedClass();  
  
            // The following two calls do what you would expect. They call  
            // the methods that are defined in BaseClass.  
            bc.Method1();  
            bc.Method2();  
            // Output:  
            // Base - Method1  
            // Base - Method2  
  
            // The following two calls do what you would expect. They call  
            // the methods that are defined in DerivedClass.  
            dc.Method1();  
            dc.Method2();  
            // Output:  
            // Derived - Method1  
            // Derived - Method2  
  
            // The following two calls produce different results, depending
            // on whether override (Method1) or new (Method2) is used.  
            bcdc.Method1();  
            bcdc.Method2();  
            // Output:  
            // Derived - Method1  
            // Base - Method2  
        }  
    }  
  
    class BaseClass  
    {  
        public virtual void Method1()  
        {  
            Console.WriteLine("Base - Method1");  
        }  
  
        public virtual void Method2()  
        {  
            Console.WriteLine("Base - Method2");  
        }  
    }  
  
    class DerivedClass : BaseClass  
    {  
        public override void Method1()  
        {  
            Console.WriteLine("Derived - Method1");  
        }  
  
        public new void Method2()  
        {  
            Console.WriteLine("Derived - Method2");  
        }  
    }  
}  

En el ejemplo siguiente se muestra un comportamiento similar en un contexto diferente. En el ejemplo se definen tres clases: una clase base denominada Car y dos clases derivadas de ella y ConvertibleCarMinivan. La clase base contiene un DescribeCar método . El método muestra una descripción básica de un automóvil y, a continuación, llama ShowDetails a para proporcionar información adicional. Cada una de las tres clases define un ShowDetails método . El new modificador se usa para definir ShowDetails en la ConvertibleCar clase . El override modificador se usa para definir ShowDetails en la Minivan clase .

// Define the base class, Car. The class defines two methods,  
// DescribeCar and ShowDetails. DescribeCar calls ShowDetails, and each derived  
// class also defines a ShowDetails method. The example tests which version of  
// ShowDetails is selected, the base class method or the derived class method.  
class Car  
{  
    public void DescribeCar()  
    {  
        System.Console.WriteLine("Four wheels and an engine.");  
        ShowDetails();  
    }  
  
    public virtual void ShowDetails()  
    {  
        System.Console.WriteLine("Standard transportation.");  
    }  
}  
  
// Define the derived classes.  
  
// Class ConvertibleCar uses the new modifier to acknowledge that ShowDetails  
// hides the base class method.  
class ConvertibleCar : Car  
{  
    public new void ShowDetails()  
    {  
        System.Console.WriteLine("A roof that opens up.");  
    }  
}  
  
// Class Minivan uses the override modifier to specify that ShowDetails  
// extends the base class method.  
class Minivan : Car  
{  
    public override void ShowDetails()  
    {  
        System.Console.WriteLine("Carries seven people.");  
    }  
}  

En el ejemplo se comprueba qué versión de ShowDetails se llama. El método siguiente, TestCars1, declara una instancia de cada clase y, a continuación, llama a DescribeCar en cada instancia.

public static void TestCars1()  
{  
    System.Console.WriteLine("\nTestCars1");  
    System.Console.WriteLine("----------");  
  
    Car car1 = new Car();  
    car1.DescribeCar();  
    System.Console.WriteLine("----------");  
  
    // Notice the output from this test case. The new modifier is  
    // used in the definition of ShowDetails in the ConvertibleCar  
    // class.
  
    ConvertibleCar car2 = new ConvertibleCar();  
    car2.DescribeCar();  
    System.Console.WriteLine("----------");  
  
    Minivan car3 = new Minivan();  
    car3.DescribeCar();  
    System.Console.WriteLine("----------");  
}  

TestCars1 genera la salida siguiente. Observe especialmente los resultados de car2, que probablemente no sean los esperados. El tipo del objeto es ConvertibleCar, pero DescribeCar no tiene acceso a la versión de ShowDetails que se define en la ConvertibleCar clase porque ese método se declara con el new modificador, no al override modificador . Como resultado, un ConvertibleCar objeto muestra la misma descripción que un Car objeto . Compare los resultados de car3, que es un objeto Minivan. En este caso, el ShowDetails método que se declara en la Minivan clase invalida el ShowDetails método declarado en la Car clase y la descripción que se muestra describe un minivan.

// TestCars1  
// ----------  
// Four wheels and an engine.  
// Standard transportation.  
// ----------  
// Four wheels and an engine.  
// Standard transportation.  
// ----------  
// Four wheels and an engine.  
// Carries seven people.  
// ----------  

TestCars2 crea una lista de objetos que tienen el tipo Car. Los valores de los objetos se instancian a partir de las clases Car, ConvertibleCar y Minivan. Se llama a DescribeCar en cada elemento de la lista. El código siguiente muestra la definición de TestCars2.

public static void TestCars2()  
{  
    System.Console.WriteLine("\nTestCars2");  
    System.Console.WriteLine("----------");  
  
    var cars = new List<Car> { new Car(), new ConvertibleCar(),
        new Minivan() };  
  
    foreach (var car in cars)  
    {  
        car.DescribeCar();  
        System.Console.WriteLine("----------");  
    }  
}  

Se muestra la salida siguiente. Observe que es el mismo que el resultado que muestra TestCars1. No se llama al método ShowDetails de la clase ConvertibleCar, independientemente de si el tipo del objeto es ConvertibleCar, como en TestCars1, o Car, como en TestCars2. Por el contrario, car3 llama al método ShowDetails de la clase Minivan en ambos casos, ya sea del tipo Minivan o del tipo Car.

// TestCars2  
// ----------  
// Four wheels and an engine.  
// Standard transportation.  
// ----------  
// Four wheels and an engine.  
// Standard transportation.  
// ----------  
// Four wheels and an engine.  
// Carries seven people.  
// ----------  

Los métodos TestCars3 y TestCars4 completan el ejemplo. Estos métodos llaman ShowDetails directamente, primero desde los objetos declarados para tener el tipo ConvertibleCar y Minivan (TestCars3), a partir de los objetos declarados para tener el tipo Car (TestCars4). El código siguiente define estos dos métodos.

public static void TestCars3()  
{  
    System.Console.WriteLine("\nTestCars3");  
    System.Console.WriteLine("----------");  
    ConvertibleCar car2 = new ConvertibleCar();  
    Minivan car3 = new Minivan();  
    car2.ShowDetails();  
    car3.ShowDetails();  
}  
  
public static void TestCars4()  
{  
    System.Console.WriteLine("\nTestCars4");  
    System.Console.WriteLine("----------");  
    Car car2 = new ConvertibleCar();  
    Car car3 = new Minivan();  
    car2.ShowDetails();  
    car3.ShowDetails();  
}  

Los métodos generan la siguiente salida, que corresponde a los resultados del primer ejemplo de este tema.

// TestCars3  
// ----------  
// A roof that opens up.  
// Carries seven people.  
  
// TestCars4  
// ----------  
// Standard transportation.  
// Carries seven people.  

El código siguiente muestra el proyecto completo y su salida.

using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
  
namespace OverrideAndNew2  
{  
    class Program  
    {  
        static void Main(string[] args)  
        {  
            // Declare objects of the derived classes and test which version  
            // of ShowDetails is run, base or derived.  
            TestCars1();  
  
            // Declare objects of the base class, instantiated with the  
            // derived classes, and repeat the tests.  
            TestCars2();  
  
            // Declare objects of the derived classes and call ShowDetails  
            // directly.  
            TestCars3();  
  
            // Declare objects of the base class, instantiated with the  
            // derived classes, and repeat the tests.  
            TestCars4();  
        }  
  
        public static void TestCars1()  
        {  
            System.Console.WriteLine("\nTestCars1");  
            System.Console.WriteLine("----------");  
  
            Car car1 = new Car();  
            car1.DescribeCar();  
            System.Console.WriteLine("----------");  
  
            // Notice the output from this test case. The new modifier is  
            // used in the definition of ShowDetails in the ConvertibleCar  
            // class.
            ConvertibleCar car2 = new ConvertibleCar();  
            car2.DescribeCar();  
            System.Console.WriteLine("----------");  
  
            Minivan car3 = new Minivan();  
            car3.DescribeCar();  
            System.Console.WriteLine("----------");  
        }  
        // Output:  
        // TestCars1  
        // ----------  
        // Four wheels and an engine.  
        // Standard transportation.  
        // ----------  
        // Four wheels and an engine.  
        // Standard transportation.  
        // ----------  
        // Four wheels and an engine.  
        // Carries seven people.  
        // ----------  
  
        public static void TestCars2()  
        {  
            System.Console.WriteLine("\nTestCars2");  
            System.Console.WriteLine("----------");  
  
            var cars = new List<Car> { new Car(), new ConvertibleCar(),
                new Minivan() };  
  
            foreach (var car in cars)  
            {  
                car.DescribeCar();  
                System.Console.WriteLine("----------");  
            }  
        }  
        // Output:  
        // TestCars2  
        // ----------  
        // Four wheels and an engine.  
        // Standard transportation.  
        // ----------  
        // Four wheels and an engine.  
        // Standard transportation.  
        // ----------  
        // Four wheels and an engine.  
        // Carries seven people.  
        // ----------  
  
        public static void TestCars3()  
        {  
            System.Console.WriteLine("\nTestCars3");  
            System.Console.WriteLine("----------");  
            ConvertibleCar car2 = new ConvertibleCar();  
            Minivan car3 = new Minivan();  
            car2.ShowDetails();  
            car3.ShowDetails();  
        }  
        // Output:  
        // TestCars3  
        // ----------  
        // A roof that opens up.  
        // Carries seven people.  
  
        public static void TestCars4()  
        {  
            System.Console.WriteLine("\nTestCars4");  
            System.Console.WriteLine("----------");  
            Car car2 = new ConvertibleCar();  
            Car car3 = new Minivan();  
            car2.ShowDetails();  
            car3.ShowDetails();  
        }  
        // Output:  
        // TestCars4  
        // ----------  
        // Standard transportation.  
        // Carries seven people.  
    }  
  
    // Define the base class, Car. The class defines two virtual methods,  
    // DescribeCar and ShowDetails. DescribeCar calls ShowDetails, and each derived  
    // class also defines a ShowDetails method. The example tests which version of  
    // ShowDetails is used, the base class method or the derived class method.  
    class Car  
    {  
        public virtual void DescribeCar()  
        {  
            System.Console.WriteLine("Four wheels and an engine.");  
            ShowDetails();  
        }  
  
        public virtual void ShowDetails()  
        {  
            System.Console.WriteLine("Standard transportation.");  
        }  
    }  
  
    // Define the derived classes.  
  
    // Class ConvertibleCar uses the new modifier to acknowledge that ShowDetails  
    // hides the base class method.  
    class ConvertibleCar : Car  
    {  
        public new void ShowDetails()  
        {  
            System.Console.WriteLine("A roof that opens up.");  
        }  
    }  
  
    // Class Minivan uses the override modifier to specify that ShowDetails  
    // extends the base class method.  
    class Minivan : Car  
    {  
        public override void ShowDetails()  
        {  
            System.Console.WriteLine("Carries seven people.");  
        }  
    }  
  
}  

Consulte también