Sabendo quando usar a substituição e novas palavras-chave (Guia de Programação em C#)

Em C#, um método em uma classe derivada pode ter o mesmo nome que um método na classe base. Você pode especificar como os métodos interagem usando as palavras-chave new e overplace . O modificador estende o método de classe virtual base e o new modificador oculta um método deoverride classe base acessível. A diferença é ilustrada nos exemplos deste tópico.

Em um aplicativo de console, BaseClass declare as duas classes a seguir e DerivedClass. DerivedClass herda de BaseClass.

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

No método, declare as Main variáveis bc, dc, e bcdc.

  • bc é do tipo BaseClass, e seu valor é do tipo BaseClass.

  • dc é do tipo DerivedClass, e seu valor é do tipo DerivedClass.

  • bcdc é do tipo BaseClass, e seu valor é do tipo DerivedClass. Esta é a variável a que se deve prestar atenção.

Porque bc e bcdc tem tipo BaseClass, eles só podem acessar Method1diretamente, a menos que você use casting. Variável dc pode acessar ambos e Method1Method2. Essas relações são mostradas no código a seguir.

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  
}  

Em seguida, adicione o seguinte Method2 método ao BaseClass. A assinatura deste método corresponde à assinatura do Method2 método em DerivedClass.

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

Como BaseClass agora tem um Method2 método, uma segunda instrução de chamada pode ser adicionada para BaseClass variáveis bc e bcdc, conforme mostrado no código a seguir.

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

Quando você cria o projeto, você vê que a Method2 adição do método em BaseClass causa um aviso. O aviso diz que o Method2 método em DerivedClass oculta o Method2 método em BaseClass. Aconselhamo-lo a utilizar a new palavra-chave na Method2 definição se pretender causar esse resultado. Como alternativa, você pode renomear um dos Method2 métodos para resolver o aviso, mas isso nem sempre é prático.

Antes de adicionar new, execute o programa para ver a saída produzida pelas instruções de chamada adicionais. Os resultados a seguir são exibidos.

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

A new palavra-chave preserva as relações que produzem essa saída, mas suprime o aviso. As variáveis que têm type BaseClass continuam a acessar os membros do BaseClass, e a variável que tem type DerivedClass continua a acessar os membros em primeiro lugar e, em DerivedClass seguida, a considerar os membros herdados do BaseClass.

Para suprimir o aviso, adicione o new modificador à definição de Method2 in DerivedClass, conforme mostrado no código a seguir. O modificador pode ser adicionado antes ou depois do public.

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

Execute o programa novamente para verificar se a saída não foi alterada. Verifique também se o aviso não aparece mais. Ao usar newo , você está afirmando que está ciente de que o membro que ele modifica oculta um membro que é herdado da classe base. Para obter mais informações sobre a ocultação de nomes por meio de herança, consulte novo modificador.

Para contrastar esse comportamento com os efeitos do uso overridedo , adicione o seguinte método ao DerivedClass. O override modificador pode ser adicionado antes ou depois do public.

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

Adicione o virtual modificador à definição de Method1 in BaseClass. O virtual modificador pode ser adicionado antes ou depois do public.

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

Execute novamente o projeto. Observe especialmente as duas últimas linhas da saída a seguir.

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

O uso do override modificador permite bcdc acessar o Method1 método definido em DerivedClass. Normalmente, esse é o comportamento desejado nas hierarquias de herança. Você deseja que os objetos que têm valores criados a partir da classe derivada usem os métodos definidos na classe derivada. Você obtém esse comportamento usando override para estender o método de classe base.

O código a seguir contém o exemplo 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");  
        }  
    }  
}  

O exemplo a seguir ilustra um comportamento semelhante em um contexto diferente. O exemplo define três classes: uma classe base nomeada Car e duas classes derivadas dela, ConvertibleCar e Minivan. A classe base contém um DescribeCar método. O método exibe uma descrição básica de um carro e, em seguida, chama ShowDetails para fornecer informações adicionais. Cada uma das três classes define um ShowDetails método. O new modificador é usado para definir ShowDetails na ConvertibleCar classe. O override modificador é usado para definir ShowDetails na Minivan classe.

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

O exemplo testa qual versão do ShowDetails é chamada. O método a seguir, TestCars1, declara uma instância de cada classe e, em seguida, chama DescribeCar cada instância.

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 produz a seguinte saída. Observe especialmente os resultados para car2, que provavelmente não são o que você esperava. O tipo do objeto é ConvertibleCar, mas DescribeCar não acessa a versão do que é definido na ConvertibleCar classe porque esse método é declarado ShowDetails com o new modificador, não o override modificador. Como resultado, um ConvertibleCar objeto exibe a mesma descrição que um Car objeto. Compare os resultados para car3, que é um Minivan objeto. Nesse caso, o ShowDetails método declarado Minivan na classe substitui o ShowDetails método declarado Car na classe e a descrição exibida descreve uma 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 Cria uma lista de objetos que têm o tipo Car. Os valores dos objetos são instanciados a Carpartir das classes , ConvertibleCare Minivan . DescribeCar é invocado em cada elemento da lista. O código a seguir mostra a definição 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("----------");  
    }  
}  

A saída a seguir é exibida. Observe que é o mesmo que a saída que é exibida pelo TestCars1. O ShowDetails método da ConvertibleCar classe não é chamado, independentemente de o tipo do objeto ser ConvertibleCar, como em TestCars1, ou Car, como em TestCars2. Por outro lado, car3 chama o ShowDetails método da Minivan classe em ambos os casos, se ele tem tipo Minivan ou 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.  
// ----------  

Métodos TestCars3 e TestCars4 completar o exemplo. Esses métodos chamam ShowDetails diretamente, primeiro de objetos declarados como tendo tipo ConvertibleCar e Minivan (TestCars3), depois de objetos declarados como tendo tipo Car (TestCars4). O código a seguir define esses dois 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();  
}  

Os métodos produzem a saída a seguir, que corresponde aos resultados do primeiro exemplo neste tópico.

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

O código a seguir mostra o projeto completo e sua saída.

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 também