Quando criamos uma classe que herda de uma classe abstrata e quando implementamos a classe abstrata herdada, por que precisamos usar a palavra-chave override?
"Por quê?" perguntas como essa podem ser difíceis de responder porque são vagas. Vou assumir que sua pergunta é "que argumentos poderiam ser feitos durante o design da linguagem para argumentar a posição de que a overridepalavra-chave é necessária ?"
Vamos começar dando um passo atrás. Em algumas linguagens, por exemplo, Java, os métodos são virtuais por padrão e substituídos automaticamente. Os designers de C # estavam cientes disso e consideraram que era uma falha menor no Java. O C # não é "Java com as partes estúpidas retiradas", como alguns disseram, mas os designers do C # estavam interessados em aprender com os pontos problemáticos de design do C, C ++ e Java, e não replicá-los no C #.
Os designers de C # consideraram a substituição uma possível fonte de erros; afinal, é uma maneira de alterar o comportamento do código testado e existente , e isso é perigoso. Substituir não é algo que deve ser feito casualmente ou por acidente; deve ser projetado por alguém pensando bem sobre isso . É por isso que os métodos não são virtuais por padrão e é necessário que você diga que está substituindo um método.
Esse é o raciocínio básico. Agora podemos entrar em um raciocínio mais avançado.
A resposta de StriplingWarrior fornece um bom primeiro corte para se fazer um argumento mais avançado. O autor da classe derivada pode não estar informado sobre a classe base, pode estar pretendendo criar um novo método e não devemos permitir que o usuário substitua por engano .
Embora esse ponto seja razoável, existem vários contra-argumentos, como:
- O autor de uma classe derivada tem a responsabilidade de saber tudo sobre a classe base! Eles estão reutilizando esse código e devem fazer a devida diligência para entendê-lo completamente antes de reutilizá-lo.
- No seu cenário específico, o método virtual é abstrato; seria um erro não substituí-lo e, portanto, é improvável que o autor esteja criando uma implementação por acidente.
Vamos então fazer um argumento ainda mais avançado sobre esse ponto. Em que circunstâncias o autor de uma classe derivada pode ser desculpado por não saber o que a classe base faz? Bem, considere este cenário:
- O autor da classe base cria uma classe base abstrata B.
- O autor da classe derivada, em uma equipe diferente, cria uma classe D derivada com o método M.
- O autor da classe base percebe que as equipes que estendem a classe base B sempre precisarão fornecer um método M, portanto, o autor da classe base adiciona o método abstrato M.
- Quando a classe D é recompilada, o que acontece?
O que queremos que aconteça é que o autor de D seja informado de que algo relevante mudou . O relevante foi que M agora é um requisito e que sua implementação deve ser sobrecarregada. O DM pode precisar alterar seu comportamento, uma vez que sabemos que ele pode ser chamado a partir da classe base. A coisa certa a fazer é não dizer silenciosamente "oh, o DM existe e estende o BM". A coisa correta para o compilador fazer é falhar e diga "ei, autor de D, verifique essa suposição que não é mais válida e corrija seu código, se necessário".
No seu exemplo, suponha que o overrideera opcional no SayHelloporque ele está substituindo um método abstrato. Existem duas possibilidades: (1) o autor do código pretende substituir um método abstrato ou (2) o método de substituição é substituído por acidente porque alguém alterou a classe base e o código agora está errado de alguma maneira sutil. Não podemos distinguir essas possibilidades se overridefor opcional .
Mas se overridefor necessário , em seguida, podemos distinguir três cenários. Se houver um possível erro no código, overrideestá faltando . Se for intencionalmente substituindo, overrideestará presente . E se intencionalmente é não substituindo, em seguida, newé presente . O design do C # nos permite fazer essas distinções sutis.
Lembre-se de que o relatório de erros do compilador requer a leitura da mente do desenvolvedor ; o compilador deve deduzir do código errado o código correto que o autor provavelmente tinha em mente e fornecer um erro que os aponte na direção correta. Quanto mais pistas pudermos fazer com que o desenvolvedor deixe no código o que eles estavam pensando, melhor o trabalho que o compilador pode fazer ao relatar erros e, portanto, mais rápido você pode encontrar e corrigir seus erros.
Mas, de maneira mais geral, o C # foi projetado para um mundo em que o código é alterado . Muitos recursos do C # que parecem "ímpares" existem porque informam o desenvolvedor quando uma suposição que costumava ser válida se tornou inválida porque uma classe base foi alterada. Essa classe de erros é chamada de "falhas frágeis da classe base" e o C # possui várias mitigações interessantes para essa classe de falha.