Saber cuándo utilizar las palabras clave Override y New (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 de la clase base. Se puede especificar cómo interactúan los métodos mediante las palabras clave new y override. El modificador overrideextiende el método de clase base virtual y el modificador newoculta un método de clase base accesible. En los ejemplos de este tema se ilustra la diferencia.

En una aplicación de consola, declare las dos clases siguientes, BaseClass y DerivedClass. 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 método Main, declare las variables bc, dc y bcdc.

  • bc es de tipo BaseClass, y su valor es de tipo BaseClass.

  • dc es de tipo DerivedClass, y su valor es de tipo DerivedClass.

  • bcdc es de tipo BaseClass, y su valor es de tipo DerivedClass. Esta es la variable a la que hay 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 tener acceso a Method1 y 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  
}  

Después, agregue el método Method2 siguiente a BaseClass. La firma de este método coincide con la firma del método Method2 de DerivedClass.

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

Dado que BaseClass ahora tiene un método Method2, se puede agregar una segunda instrucción de llamada para las variables de BaseClassbc 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 método Method2 de BaseClass genera una advertencia. La advertencia indica que el método Method2 de DerivedClass oculta el método Method2 de BaseClass. Se recomienda usar la palabra clave new en la definición de Method2 si se pretende provocar ese resultado. Como alternativa, se puede cambiar el nombre de uno de los métodos Method2 para resolver la advertencia, pero eso no siempre resulta práctico.

Antes de agregar new, ejecute el programa para ver el resultado producido por las instrucciones adicionales que realizan la llamada. Se muestran los resultados siguientes.

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

La palabra clave new conserva las relaciones que generan ese resultado, pero se 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 modificador new a la definición de Method2 en DerivedClass, como se muestra en el código siguiente. Se puede agregar el modificador antes o después de public.

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

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

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

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

Agregue el modificador virtual a la definición de Method1 en BaseClass. Se puede agregar el modificador virtual 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 modificador override permite que bcdc tenga acceso al método Method1 que se define en DerivedClass. Normalmente, es el comportamiento deseado en jerarquías de herencia. La intención es que los objetos que tienen valores que se crean a partir de la clase derivada usen los métodos que se definen en la clase derivada. Ese comportamiento se consigue mediante el uso de override para extender 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. El ejemplo define tres clases: una clase base denominada Car y dos clases que se derivan de ella, ConvertibleCar y Minivan. La clase base contiene un método DescribeCar. El método muestra una descripción básica de un automóvil y, después, llama a ShowDetails para proporcionar información adicional. Cada una de las tres clases define un método ShowDetails. El modificador new se usa para definir ShowDetails en la clase ConvertibleCar. El modificador override se usa para definir ShowDetails en la clase Minivan.

// 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.");  
    }  
}  

El ejemplo comprueba la versión de ShowDetails que se llama. El siguiente método, TestCars1, declara una instancia de cada clase y, después, 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 el siguiente resultado. Observe especialmente los resultados de car2, que probablemente no son los que se esperaban. El tipo de objeto es ConvertibleCar, pero DescribeCar no tiene acceso a la versión de ShowDetails que se define en la clase ConvertibleCar porque ese método se declara con el modificador new, no con el modificador override. Como resultado, un objeto ConvertibleCar muestra la misma descripción que un objeto Car. Compare los resultados de car3, que es un objeto Minivan. En este caso, el método ShowDetails que se declara en la clase Minivan invalida el método ShowDetails que se declara en la clase Car, y en la descripción que se muestra se describe una furgoneta.

// 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. Se crean instancias de los valores de los objetos desde las clases Car, ConvertibleCar y Minivan. DescribeCar se llama en cada elemento de la lista. En el código siguiente se 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 el siguiente resultado. Observe que es el mismo resultado mostrado por TestCars1. El método ShowDetails de la clase ConvertibleCar no se llama, independientemente de si el tipo de objeto es ConvertibleCar, como en TestCars1, o Car como en TestCars2. Por el contrario, car3 llama al método ShowDetails desde la clase Minivan en ambos casos, independientemente de que tenga el tipo Minivan o 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 directamente a ShowDetails, primero desde los objetos declarados con el tipo ConvertibleCar y Minivan (TestCars3), y después desde los objetos declarados con el tipo Car (TestCars4). En el código siguiente se definen 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 el siguiente resultado, que se 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.  

En el código siguiente se muestra el proyecto completo y sus resultados.

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