Controle de versão com as palavras-chave override e new (Guia de Programação em C#)
A linguagem C# foi projetada para que o controle de versão entre classes derivadas e base em diferentes bibliotecas possa evoluir e manter a compatibilidade com versões anteriores. Isso significa, por exemplo, que a introdução de um novo membro em uma classe base com o mesmo nome que um membro em uma classe derivada tem suporte completo pelo C# e não leva a comportamento inesperado. Isso também significa que uma classe deve declarar explicitamente se um método destina-se a substituir um método herdado ou se um método é um novo método que oculta um método herdado de nome semelhante.
No C#, as classes derivadas podem conter métodos com o mesmo nome que os métodos da classe base.
Se o método na classe derivada não for precedido pelas palavras-chave new ou override, o compilador emitirá um aviso e o método se comportará como se a palavra-chave
new
estivesse presente.Se o método na classe derivada for precedido pela palavra-chave
new
, o método será definido como sendo independente do método na classe base.Se o método na classe derivada for precedido pela palavra-chave
override
, os objetos da classe derivada chamarão esse método em vez do método da classe base.Para aplicar a palavra-chave
override
ao método na classe derivada, o método da classe base deve ser definido como virtual.O método da classe base pode ser chamado de dentro da classe derivada usando a palavra-chave
base
.As palavras-chave
override
,virtual
enew
também podem ser aplicadas a propriedades, indexadores e eventos.
Por padrão, os métodos C# não são virtuais. Se um método for declarado como virtual, qualquer classe que herdar o método pode implementar sua própria versão. Para tornar um método virtual, o modificador virtual
é usado na declaração de método da classe base. A classe derivada pode, em seguida, substituir o método virtual base usando a palavra-chave override
ou ocultar o método virtual na classe base usando a palavra-chave new
. Se nem a palavra-chave override
nem a new
for especificada, o compilador emitirá um aviso e o método na classe derivada ocultará o método na classe base.
Para demonstrar isso na prática, suponha por um momento que a Empresa A tenha criado uma classe chamada GraphicsClass
, que seu programa usa. A seguir está GraphicsClass
:
class GraphicsClass
{
public virtual void DrawLine() { }
public virtual void DrawPoint() { }
}
Sua empresa usa essa classe e você a usa para derivar sua própria classe, adicionando um novo método:
class YourDerivedGraphicsClass : GraphicsClass
{
public void DrawRectangle() { }
}
Seu aplicativo é usado sem problemas, até a Empresa A lançar uma nova versão de GraphicsClass
, que se parece com o seguinte código:
class GraphicsClass
{
public virtual void DrawLine() { }
public virtual void DrawPoint() { }
public virtual void DrawRectangle() { }
}
A nova versão de GraphicsClass
agora contém um método chamado DrawRectangle
. Inicialmente, nada ocorre. A nova versão ainda é compatível em relação ao binário com a versão antiga. Qualquer software que você implantou continuará a funcionar, mesmo se a nova classe for instalada nesses sistemas de computador. Todas as chamadas existentes para o método DrawRectangle
continuarão a fazer referência à sua versão, em sua classe derivada.
No entanto, assim que você recompilar seu aplicativo usando a nova versão do GraphicsClass
, você receberá um aviso do compilador, CS0108. Este aviso informa que você deve considerar como deseja que seu método DrawRectangle
se comporte em seu aplicativo.
Se desejar que seu método substitua o novo método de classe base, use a palavra-chave override
:
class YourDerivedGraphicsClass : GraphicsClass
{
public override void DrawRectangle() { }
}
A palavra-chave override
garante que todos os objetos derivados de YourDerivedGraphicsClass
usarão a versão da classe derivada de DrawRectangle
. Os objetos derivados de YourDerivedGraphicsClass
ainda poderão acessar a versão da classe base de DrawRectangle
usando a palavra-chave base:
base.DrawRectangle();
Se você não quiser que seu método substitua o novo método de classe base, as seguintes considerações se aplicam. Para evitar confusão entre os dois métodos, você pode renomear seu método. Isso pode ser demorado e propenso a erros e simplesmente não ser prático em alguns casos. No entanto, se seu projeto for relativamente pequeno, você poderá usar opções de Refatoração do Visual Studio para renomear o método. Para obter mais informações, consulte Refatorando classes e tipos (Designer de Classe).
Como alternativa, você pode evitar o aviso usando a palavra-chave new
na definição da classe derivada:
class YourDerivedGraphicsClass : GraphicsClass
{
public new void DrawRectangle() { }
}
Usando a palavra-chave new
informa ao compilador que sua definição oculta a definição que está contida na classe base. Esse é o comportamento padrão.
Seleção de método e substituição
Quando um método é chamado em uma classe, o compilador C# seleciona o melhor método a ser chamado se mais de um método for compatível com a chamada, como quando há dois métodos com o mesmo nome e parâmetros que são compatíveis com o parâmetro passado. Os métodos a seguir seriam compatíveis:
public class Derived : Base
{
public override void DoWork(int param) { }
public void DoWork(double param) { }
}
Quando DoWork
é chamado em uma instância do Derived
, o compilador C# tenta primeiro tornar a chamada compatível com as versões do DoWork
originalmente declarado em Derived
. Os métodos de substituição não são considerados como declarados em uma classe, eles são novas implementações de um método declarado em uma classe base. Somente se o compilador C# não puder corresponder a chamada de método a um método original em Derived
é que tentará corresponder a chamada a um método substituído com o mesmo nome e parâmetros compatíveis. Por exemplo:
int val = 5;
Derived d = new Derived();
d.DoWork(val); // Calls DoWork(double).
Como a variável val
pode ser convertida para um duplo implicitamente, o compilador C# chama DoWork(double)
em vez de DoWork(int)
. Há duas formas de evitar isso. Primeiro, evite declarar novos métodos com o mesmo nome que os métodos virtuais. Segundo, você pode instruir o compilador C# para chamar o método virtual fazendo-o pesquisar a lista do método de classe base convertendo a instância do Derived
para Base
. Como o método é virtual, a implementação de DoWork(int)
em Derived
será chamada. Por exemplo:
((Base)d).DoWork(val); // Calls DoWork(int) on Derived.
Para obter mais exemplos de new
e override
, consulte Quando usar as palavras-chave override e new.