Desenvolvimento - C#
A confusão dos modificadores (C#)
Neste artigo o autor escreve sobre os modificadores abstract, interface, override, virtual, sealed, extern, volatile e partial.
por Cleyton BrunoVejo muita confusão quando se fala nos modificadores abstract, interface, override, virtual, sealed, extern, volatile e partial... Temos também os modificadores public, protected, private e internal, mas eles são de uso simples e acho que todos tem bem claros as suas funções. Vou tentar falar um pouco sobre o que sei deles e espero que cada um fique bem claro para que vocês façam o melhor uso deles.
Override
O modificador override simplesmente diz que o método que ele segue está sobrescrevendo outro, ou seja, você está apagando o método antigo e colocando no lugar este novo. Veremos exemplos mais abaixo.
Abstract
O modificador abstract pode ser aplicado tanto em classes como em métodos. Em classes, ele as cria de modo que não podem ser instanciadas, pois são como o nome diz: abstratas. São classes ‘imcompletas’.
Classes abstratas são muito usadas em conjunto com interfaces (que veremos adiante).
Em métodos, abstract força eles a não terem código. Servem apenas de protótipo, uma simples definição, para uma implementação que deverá obrigatóriamente ser escrita na classe filho. É como se fosse uma variável: você declara um método e só depois atribui um valor a ele.
Veja um exemplo:
// Esta classe é abstrata, ela é imcompleta e não pode ser instanciada
public abstract class Pai {
// Funciona também com variáveis.
// OBS: Não podemos ter variáveis abstratas. Nem faz sentido!
public int i = 10;
public int j = 20;
// Implementação básica de dois métodos que podem, ou não, serem sobrescritos
public void teste() {
Console.WriteLine("Olá, eu tenho uma implementação básica”);
Console.WriteLine(“Não precisa me sobrescrever se não quiser.");
}
public void teste2() {
Console.WriteLine("Olá, eu tenho uma implementação básica”);
Console.WriteLine(“Não precisa me sobrescrever se não quiser.");
}
// Método abstrato que não pode ter código nenhum
public abstract void teste3();
}
// Esta classe deriva da classe abstrata Pai. Uma obrigação dela é escrever o método teste3, ou
// seja, atribuir um valor ao método que foi declarado na classe Pai.
public class Filho: Pai {
// Aqui estamos opcionalmente sobrescrevendo a variável j.
// destaque para o new antes de int, estamos criando uma "nova" variável
public new int j = 5;
// Aqui estamos opcionalmente sobrescrevendo o método teste
// Destaque para o new antes de void, estamos criando um ‘novo’ método
public new void teste() {
Console.WriteLine("Olá, eu vim para sobrescrever o teste da classe pai");
}
// Aqui estamos obrigatóriamente "sobrescrevendo" o método teste3
// Coloque sobrescrevendo entre aspas porque na verdade estamos mesmo é
// escrevendo, visto que teste3 foi apenas declarado, não tendo ainda código nenhum.
// Destaque para o override antes de void. Porque não new? Porque não estamos
// criando um novo método, estamos atribuindo um valor ao método que já existe!
public override void teste3() {
Console.WriteLine("Ufa, eu sou o teste3, finalmente eu tenho utilidade.");
}
}
class Program {
static void Main(string[] args) {
Filho f = new Filho();
f.teste();
f.teste2();
f.teste3();
Console.WriteLine(f.i);
Console.WriteLine(f.j);
Console.ReadLine();
}
}
Legal né?
Em f.teste() foi chamado o método novo escrito na classe filho. Em f.teste2() foi chamado a implementação básica de teste2 na classe pai. Em f.teste3() foi chamado o método que teve valor atribuído na classe Filho. Ok, vamos continuar.
Interface
Existe uma grande confusão quando se fala de interface, é muito confundido com abstractt. Eu sempre vi que interface é um "contrato" mas nunca entendi direito então eu vou adicionar um pouco a esta explicação.
Interfaces são regras para a criação de outras classes, são moldes. Se uma classe implementa uma interface ela obrigatóriamente tem que ter implementação para tudo o que a interface tem.
As interfaces não podem ter variáveis, não podem ter implementações básicas e não podemos definir modificadores de acesso nos métodos dela, quem deve fazer isso é a classe que a implementa. O legal é que uma classe pode implementar várias interfaces. Lembrando que não podemos definir modificadores de acesso nos métodos de uma interface. Vamos ver um exemplo abaixo:
interface IObjeto {
string formato();
}
interface IColorido {
public string cor();
}
public class BolaVermelha : IObjeto, IColorido {
public string formato() {
return "esférico";
}
public string cor() {
return "vermelho";
}
}
Bacana!
Então para que fique bem claro vamos ver as diferenças entre interface e classe abstrata:
Classe abstrata:
- Pode ter implementação básica de métodos
- Pode ter variáveis
- Pode definir modificadores de acesso a seus métodos
- Classes só podem derivar de uma abstrata
Interface:
- NÃO pode ter implementação básica de métodos
- NÃO pode ter variáveis
- NÃo pode definir modificadores de acesso a seus métodos
- Classes podem implementar várias interfaces
Agora vou deixar aqui a minha opinião e porque muitos desenvolvedores utilizam as interfaces e classes abstratas em conjunto.
Não devemos nunca nos esquecer do básico sobre classes. Classe define um tipo de objeto, uma classe literalmente. Vi uma vez um artigo que deixou bem claro isso. Imagine um gavião e um boeing, ambos tem o método voar, mas são completamente diferentes, que classe seria esta? Neste caso teríamos uma interface IVoador com o método voar, classes abstratas avião e pássaro que implementem a interface IVoador e definem métodos padrões ou obrigatórios que todos os pássaros (no caso da classe pássaro) e aviões (no caso da classe avião) devem ter e as classes gavião e boeing, derivando de pássaro e avião respectivamente. Enfim, é só orientação a objeto ;)
Espero que tenho ficado bem claro, porque agora veremos...
Virtual
O modificador virtual diz que um método não tem estado definido até que seja executada, ou seja, é "virtual". Provavelmente você não irá usar o virtual como mostrarei no exemplo, mas escolhi assim porque fica muito mais visível. Veja:
public class Avo {
public virtual void teste() {
Console.WriteLine("Eu sou o método teste");
}
}
// O avo tem um filho, que é o pai
public class Pai: Avo {
public override void teste() {
Console.WriteLine("Eu sou o método teste do pai");
}
}
// O pai tem um filho. Ou seja, um neto do avo
public class Filho : Pai {
public override void teste() {
Console.WriteLine("Eu sou o método teste do filho");
}
}
class Program {
static void Main(string[] args) {
// O filho veio do pai e, então, veio também do avô. Por isso eu posso
// fazer esta atribuição
Filho n = new Filho();
Pai f = n;
Avo p = n;
p.teste();
f.teste();
n.teste();
Console.ReadLine();
}
}
Vamos rodar.... Nossa, o que aconteceu?
O pai sobrescreveu o avô, e o filho por sua vez sobrescreveu o pai. Logo todo mundo ficou com o método que o filho fez. Mas existe uma coisa ainda mais legal, o uso do new. Como assim? Veja:
public class Avo {
public virtual void teste() {
Console.WriteLine("Eu sou o método teste");
}
}
public class Pai: Avo {
public new virtual void teste() {
Console.WriteLine("Eu sou o método teste do pai");
}
}
public class Filho : Pai {
public override void teste() {
Console.WriteLine("Eu sou o método teste do filho");
}
}
class Program {
static void Main(string[] args) {
Filho n = new Filho();
Pai f = n;
Avo p = n;
p.teste();
f.teste();
n.teste();
Console.ReadLine();
}
}
Vamos rodar... Hmmm, e agora?
O que aconteceu foi o seguinte: O avô foi declarado e seu método teste foi para a memória. Então o pai foi declarado. Ele criou um novo método virtual chamado teste, ele foi para outro lugar na memória. Assim, quando o filho foi declarado ele sobrescreveu o método virtual do pai, e o do avô que estava em outro lugar não foi sobrescrita. Ok? Espero que tenha ficado claro porque agora eu vou falar de sealed.
Sealed
O modificador "sealed" tranca um método para que ele não seja mais sobrescrito. Vamos usar o exemplo anterior:
public class Avo {
public virtual void teste() {
Console.WriteLine("Eu sou o método teste");
}
}
public class Pai: Avo {
sealed public override void teste() {
Console.WriteLine("Eu sou o método teste do pai");
}
}
public class Filho : Pai {
public override void teste() {
Console.WriteLine("Eu sou o método teste do filho");
}
}
class Program {
static void Main(string[] args) {
Filho n = new Filho();
Pai f = n;
Avo p = n;
p.teste();
f.teste();
n.teste();
Console.ReadLine();
}
}
Esse exemplo não compila, porque a classe Filho está tentando sobrescrever o método teste da classe Pai, mas este método é sealed, seria mais ou menos "a partir daqui ninguém pode mais derivar".
Extern
O modificador extern define um método externo ao seu projeto. Um método que está implementado em outro arquivo. Normalmente em uma DLL. Veja o exemplo:
// IMPORTANTE: Sem esse using o DllImport não funciona!
using System.Runtime.InteropServices;
// Aqui estamos carregando o método SetCurrentDirectory que está implementada
// na dll kernel32
[DllImport(“kernel32”, setLastError=true)]
Static extern bool SetCurrentDirectory(string name);
Vejamos agora o volatile
Volatile
O modificador volatile permite que a variável/propriedade possa ser modificada por fatores externos, como o sistema operacional ou outros processos. Com o modificador volatile você garante que, independente do processo que esteja acessando a váriavel/propriedade, ele pegará o valor atual da váriavel/propriedade. Vou ficar devendo um exemplo para vocês, mas é só pesquisar um pouco sobre threads em c# que é fácil de encontrar.
E por último...
Partial
O modificador partial define que temos apenas um pedaço da classe. Isto é bem legal para divisão de trabalho. Exemplo:
// Um pedaço da classe Teste
public partial class Teste {
public void teste() {
Console.WriteLine(“Eu sou o método teste. Estou em um pedaço”);
}
}
// Outro pedaço da classe Teste
public partial class Teste {
public void teste2() {
Console.WriteLine(“Eu sou o método teste2. Estou em outro pedaço”);
}
}
class Program {
static void Main(string[] args) {
// Aqui todas as partes de Teste são unidas
Teste teste = new Teste();
Teste.teste();
Teste.teste2();
Console.ReadLine();
}
}