Aprenda C#

  • Página Inicial
  • Contato!
  • Tudo sobre C# Parte 1!
  • Tudo sobre C# Parte 2!
  • Tudo sobre C# Parte 3!
  • Tudo sobre C# Parte 4!
  • Tudo sobre C# Parte 5!
  • Tudo sobre C# Parte 6!
  • Tudo sobre C# Parte 7!
  • Tudo sobre C# Parte 8!
  • Tudo sobre C# Parte 9!
  • Tudo sobre C# Parte 10!
  • Orientação a Objetos em C# - Parte 1

    Classes - Criando e Instanciando

    Classes são "pacotes" de código que definem classsificações de dados (igual os tipos), os quais possuem proiedades e operações (métodos) em linguagens orientadas a objetos (OOP). As propriedades (atributos) armazenam valores e características dos dados, já os métoos são funções internas às classes, que permitem agir sobre elas para executar ações.

    Objetos são instancias de uma classe que possuem características e comportamentos. Podemos criar múltiplas instâncias de uma mesma classe. As propriedades, métodos e eventos de uma classe são alocadas em memória através da instância de um objeto.

    As vantagens da POO, incluem reutilização de códigos, modularidade, uso mais simples (realístico) e código mais limpo e claro.

    Para definir classes, usamos a palavra-chave class, por exemplo:

    
    class Caixa {
        double lado;
    
        double volume() {
            return lado * lado * lado;
        }
    }
    
    

    Abra um novo projeto console do C#, note que mesmo no programa principal, já temos uma classe (Program), mas para trabalharmos com classes em C#, é recomendado criar arquivos separados de classes, e os arquivos terão os mesmos nomes das classes, em algumas linguagens POO não é possível criar várias classes no mesmo arquivo.

    No gerenciador de soluções, escolha a opção classe e coloque o nome Caixa, esse será o código dela:

    
    using System;
    
    namespace ClassesCriandoEInstanciando {
        class Caixa {
            double lado;
    
            double volume() {
                return lado * lado * lado;
            }
        }
    }
    
    

    E para criar a instância da classe (o objeto), vá no programa principal da classe principal e coloque o código assim.

    
    using System;
    
    namespace ClassesCriandoEInstanciando {
        class Program {
            static void Main(string[] args) {
                Caixa cx;
                cx = new Caixa(); 
            }
        }
    }
    
    

    Mas o mais comum é chamar o objeto assim:

    
    Caixa cx = new Caixa();
    
    

    Quando criamos uma classe, temos mais trabalho para fazê-la, mas depois que ela está pronta, podemos criar quantos objetos precisarmos vindos dessa mesma classe, e o programa principal fica mais simples, menor e mais natural. Os objetos criados dessa mesma classe são independentes entre si e o status de um não interfere no outro. Essa é uma das vantagens da orientação a objetos.

    PS: Caso queira retornar o nome do objeto em C#, use o método this.GetType().Name no objeto.

    Os atributos podem ser de qualquer tipo, incluindo classes invólucro, o mesmo vale para parâmetros de construtores. É possível também chamar um atributo ou método de objeto agregado, por exemplo objeto1.metodo2().metodo1().

    Classes - Modificando a Acessibilidade

    Quando temos que acessar algum atributo ou método da classe, ele não aparece por padrão no programa principal, devido ao fator de atributos e métodos por padrão, no C#, serem privados.

    Para resolver isso, colocamos a indicação public, para colocar esses métodos públicos, dessa forma:

    
    class Caixa {
        private double lado;
    
        public double volume() {
            return lado * lado * lado;
        }
    }
    
    

    Como vimos, além do método public (acesso total), temos o private (acesso apenas dentro da classe) e o protected (acesso dentro da classe e das descendentes, é como um meio termo entre o public e private).

    Dessa forma, podemos mexer nesse atributo e usar os métodos na classe principal apenas se forem públicos, assim:

    
    Caixa cx = new Caixa();
    
    cx.lado = 5; // Só podemos mexer nele se for público
    Console.WriteLine(cx.volume().ToString());
    
    Console.Read();
    
    

    Getter, Setter e Encapsulamento

    Colocando os atributos privados, eles estarão encapsulados, mas para utilizar eles utilizamos dois tipos de métodos, os getters (que retornam o conteúdo) e os setters (que mudam o valor do conteúdo).

    Veja como encapsular os dados usando o getter e o setter:

    
    class Caixa {
        private double lado;
    
        public double volume() {
            return lado * lado * lado;
        }
    
        public double getLado() {
            return this.lado;
        }
    
        public void setLado(double lado) {
            this.lado = lado;
        }
    }
    
    

    PS: O this vai ser substituído pelo objeto.

    Dessa forma, protegemos o acesso à variável, mas podemos pegar o valor dela com o getter e mudar ela pelo setter, assim:

    
    Caixa cx = new Caixa();
    
    cx.setLado(5);
    Console.WriteLine(cx.getLado().ToString());
    
    Console.Read();
    
    

    No entanto, o mais comum é usarmos o getter e setter dessa forma no C#:

    
    class Caixa {
        private double lado;
    
        public double volume() {
            return lado * lado * lado;
        }
    
        public double Lado {
            get {return this.lado;}
            set {this.lado = value;}
        }
    }
    
    

    Assim também:

    
    class Caixa {
        private double lado;
    
        public double volume() {
            return lado * lado * lado;
        }
    
        public double Lado {
            get => lado;
            set => lado = value;
        }
    }
    
    

    Ou mais simplificado:

    
    class Caixa {
        private double lado;
    
        public double volume() {
            return lado * lado * lado;
        }
    
        public double Lado {
            get;
            set;
        }
    }
    
    

    PS: O último método acima pode dar erro em objetos agregados, nesse caso use um dos dois modos anteriores.

    Inclusive, podemos colocar um private no set ou no get caso queiramos que um deles seja restrito à classe.

    E pra exibir:

    
    Caixa cx = new Caixa();
    
    cx.Lado = 5; // Setter
    Console.WriteLine(cx.Lado); // Getter
    
    Console.Read();
    
    

    PS: Assim como no Java e no PHP, o C# permite trabalhar com interfaces, que faz o encapsulamento a nível de classe, nesse caso, podemos fazer a interface assim:

    
    interface Estoque {
        void defineLado(int l);
        double volume();
    }
    
    

    E na classe que implementará os elementos, basta usar como uma herança, assim:

    
    class Caixa : Estoque {
        private double lado;
    
        public void defineLado(int l) {
            this.lado = l;
        }
    
        public double volume() {
            return lado * lado * lado;
        }
    
        public double Lado {
            get;
            set;
        }
    }
    
    

    Em outras palavras, as interfaces estabelecem contratos entre as classes que as implementam, garantindo que elas tenham um certo comportamento, por isso, podemos declarar um objeto do tipo da interface e iniciar com a classe que a implementa, por exemplo:

    
    Estoque cx = new Caixa();
    
    cx.defineLado(5);
    
    Console.WriteLine(cx.volume());
    
    Console.Read();
    
    

    As interfaces também garantem o encapsulamento, já que podemos usar somente os métodos descritos na interface nos objetos, e não permitir a visualização de atributos da classe implementadora.

    Lembrando que podemos implementar mais de uma interface no programa, separando por vírgulas, algo tipo class NomeDaClasse : NomeDaInterface1, NomeDaInterface2. Lembrando também que muitos programadores colocam as interfaces com um "I" no começo para facilitar a identificação das mesmas (como no caso ficaria IEstoque), mas não é obrigatório.

    PS: Podemos verificar se uma classe implementa uma interface usando um if e else, isso também vale pra classes:

    
    Estoque cx = new Caixa();
    
    if(typeof(Estoque).IsInstanceOfType(cx))) {
        cx.defineLado(5);
    
        Console.WriteLine(cx.volume());
    }
    else {
        Console.WriteLine("O objeto não implementa a interface Estoque!");
    }
    
    

    Classes - Método Construtor

    Para que possamos usar essa classe criada ainda é necessário que os campos privados sejam inicializados, pois é inacessível pro exterior. Faremos isso usando um construtor.

    Quando usamos a palavra new para criar um objeto, o runtime constrói o objeto usando a definição da classe. O runtime toma um pedaço da memória RAM, a preenche com os campos definidos pela classe e então invoca um construtor que realizará as inicializações requeridas.

    Um construtor é um método especial que roda automaticamente quando uma classe é instanciada. Possui o mesmo nome da classe, e pode receber parâmetros, mas não retorna valores, nem mesmo void. Toda classe deve ter um construtor, se um não for escrito, o compilador gerará um automaticamente (que não faz absolutamente nada).

    Veja abaixo o exemplo simples de construtor:

    
    public Caixa() {
        lado = 10;
    }
    
    

    Na classe Caixa, ele ficaria assim (deixe o atributo lado privado):

    
    class Caixa : Estoque {
        private double lado;
    
        public Caixa() {
            lado = 10;
        }
    
        public void defineLado(int l) {
            this.lado = l;
        }
        
        public double volume() {
            return lado * lado * lado;
        }
    }
    
    

    E no programa principal, faça isso:

    
    Estoque cx = new Caixa();
    
    double vol;
    vol = cx.volume();
    
    Console.WriteLine(vol.ToString());
    
    

    Da mesma forma, podemos criar destrutores, mas eles são dispensáveis em C#, que faz a coleta de lixo automaticamente, mas para usar, fazemos assim na classe:

    
    ~Caixa {
        Console.WriteLine("Objeto Destruído");
    }
    
    

    PS: Não existe delete em C#.

    Classes - Sobrecarga de Construtor

    Podemos ter sobrecarga de métodos construtores no C# também. Basicamente, como em qualquer outra função, é criar um segundo construtor (que obviamente, também terá o mesmo nome da classe), mas com parâmetros diferentes.

    No nosso exemplo, a classe Caixa terá o construtor padrão sem parâmetros (que adiciona 10 ao atributo lado automaticamente) e o segundo construtor que receberá um parâmetro para definiir esse mesmo atributo, dessa forma:

    
    public Caixa() {
        lado = 10;
    }
    
    public Caixa(double l) {
        lado = l;
    }
    
    

    E no programa principal, fazemos assim (veja a diferença dos objetos):

    
    Estoque cx1 = new Caixa();
    
    double volume1;
    volume1 = cx1.volume();
    
    Console.WriteLine(volume1.ToString());
    
    Estoque cx2 = new Caixa(8);
    
    double volume2;
    volume2 = cx2.volume();
    
    Console.WriteLine(volume2.ToString());
    
    

    PS: Podemos ter mais de dois construtores sobrecarregados, desde que não sejam do mesmo tipo de parâmetros. Caso num construtor precise inicializa um atributo como nulo, use null.

    Classes - Atributos e Métodos Estáticos

    Nem todo método pertence naturalmente a uma instância de classe, e podemos utilizar alguns métodos sem ter que instânciar as classes (sem criar objetos), isso é possível graças ao static. Ou seja, o método static acessa campos marcados como estáticos.

    Crie esse método na classe Caixa:

    
    public static int caixaLado2() {
        int lat = 2;
        int vol = lat * lat * lat;
    
        return vol;
    }
    
    

    E no programa principal, podemos chamar o método estático sem ter que instanciar a classe onde ela está:

    
    Console.WriteLine(Caixa.caixaLado2());
    
    

    Veja um exemplo de uso de métodos e atributos estáticos numa classe denominada Lampada:

    
    class Lampada {
        private static float preco = 9.50f;
        private static bool acesa = false;
    
        public static void custo() {
            Console.WriteLine("A lâmpada custa R$ {0:0.00}.", preco);
    
            // Note que não usamos this para exibir atributos estáticos.
        }
    
        public static void acender() {
            Console.WriteLine("A lâmpada está acesa!");
            acesa = true;
        }
    
        public static void apagar() {
            Console.WriteLine("A lâmpada está apagada!");
            acesa = false;
        }
    }
    
    

    E no código principal:

    
    static void Main(string[] args) {
        Lampada.custo(); // Método estático.
    
        Lampada.acender();
        Lampada.apagar();
    }
    
    

    PS: Não é recomendado chamar atributos e métodos estáticos através de objetos (tanto que algumas linguagens nem permitem isso), pois são atributos e métodos da classe, por isso devem ser chamados diretamente através dela.

    Também podemos fazer novas atribuições em atributos estáticos no código principal, desde que sejam públicos, e toda instância feita por ele também é alterada, por exemplo:

    
    Lampada.preco = 7.25f; // Atributo estático, deixe ele público
    
    

    O static, teoricamente, significa que só uma alocação de memória é criada para esse atributo ou método, não tendo duas cópias na memória em simultâneo.

    Em outras palavras, o static manipula os atributos e métodos na classe toda, não apenas em uma instância, e todo objeto criado com ela também terá essa alteração. São atributos e métodos globais.

    PS: Métodos estáticos só podem trabalhar outros métodos e atributos quando estes também forem estáticos, e não podem ser sobrepostos. E atributos estáticos é recomendável eles serem inicializados.

    Classes - Campos Compartilhados

    Como visto, podemos criar métodos estáticos, que são compartilhadas com todos objetos instânciados pela mesma classe. Também podemos criar atributos estáticos.

    Crie uma nova classe com o nome Acumula, e esse código aqui:

    
    class Acumula {
        public static int soma;
    
        public Acumula() {
            soma = 0;
        }
    
        public void incrementa() {
            soma++;
        }
    
        public void incrementa(int valor) {
            soma += valor;
        }
    }
    
    

    Note que ele tem um atributo estático (que também pode ser chamado pela classe, sem criar um objeto). Também tem um polimorfismo de sobrecarga com incremento, onde escolhemos o incremento do número.

    No programa principal, coloque isso:

    
    Acumula[] obj = new Acumula[2];
    
    obj[0] = new Acumula();
    obj[1] = new Acumula();
    
    obj[0].incrementa();
    Console.WriteLine("O valor é {0}.",  Acumula.soma.ToString());
    
    obj[1].incrementa(8);
    Console.WriteLine("O valor é {0}.",  Acumula.soma.ToString());
    
    

    Como o campo é compartilhado, ele altera o valor do atributo na classe, e todas as instâncias serão alteradas dessa mesma forma.

    PS: Métodos getters e setters de atributos estáticos também devem ser estáticos, e não se utiliza o this dentro deles. Lembre-se também que qualquer método estático só pode trabalhar com atributos e métodos que também sejam estáticos. E atributos estáticos é recomendável os iniciar dentro da classe. Métodos estáticos não podem ser sobrepostos.

    Também é melhor usarmos listas ao invés de arrays, quando estamos trabalhando com objetos, dessa forma:

    
    IList<Acumula> obj = new List<Acumula>();
    
    obj.Add(new Acumula());
    obj.Add(new Acumula());
    
    

    Ou assim, mais simplificado:

    
    IList<Acumula> obj = new List<Acumula> {
        new Acumula(),
        new Acumula()
    };