Menu

Conhecendo Arrays em C#

O array é um tipo de referência, não é um tipo de valor!

Quando tenho variáveis que compartilham o mesmo contexto, posso "juntar" todas num tipo chamado array! Um array agrupará vários tipos da mesma variável sendo possível consultar por índices. Por exemplo: 

Tenho um conjunto de peças, e quero guardar quantas peças iguais tenho em cada conjunto. Supondo que seria uma coleção de números inteiros: 

int conjunto_1 = 15;
int conjunto_2 = 20;
int conjunto_3 = 13;
int conjunto_4 = 22;

Para representar todos os conjuntos agrupados, sem ter que declarar uma variavel para cada um, podemos usar um conjunto de números interios, representado por um array, dessa forma:

int[] conjunto;

Para inicializar essa nova variavel que declaramos, que é do tipo array, precisamos informar ao compilador que estamos criando uma váriável que suporta diversos valores do tipo inteiro. Não basta simplesmente atribuir um valor inteiro, pois não é mais um valor inteior único... é um conjunto de valores inteiros.

O compilador também precisa saber quantos valores iremos armarezar na memória, qual a quantidade de espaço ele precisará cirar, e para isso, informamos também esse valor!

int[] conjunto = new int[4];
conjunto[0] = 15;
conjunto[1] = 20;
conjunto[2] = 13;
conjunto[3] = 22;

Dessa forma, criamos um array de inteiros com 4 posições (lembrando que o índice inicial aqui sempre será zero). Mas o que acontece se você inicilizar um array de 4 posições e não colocar nenhum valor nessa última posição? O valor padrão de inicialização é 0! Agora, como iterar sobre esse array? Fácil! Podemos acessar algumas propriedade especificas do array:
for(int indice = 0; indice < conjunto.Length; indice++){
	Console.WriteLine($"Acessando o array no índice: {indice}, com valor: conjunto[{indice}]");
}

Entendendo Herança e Interface em C#

Começando pela Herança:
Herança é um princípio de orientação a objetos, que permite que classes compartilhem atributos e métodos, através de "heranças". Ela é usada na intenção de reaproveitar código ou comportamento generalizado ou especializar operações ou atributos
Dessa forma, podemos criar uma situação hipotética para exemplificar uma herança. Supondo que temos um sistema de uma empresa. Nesta empresa teremos funcionários... então vamos criar a classe funcionario:
public class Funcionario
{
	public string Nome { get; set; }
	public string CPF { get; set; }
	public double Salario { get; set; }
}
Mas os funcionários podem ter características específicas, tais como Diretor, Desenvolvedor, Vendedor, etc... Normalmente, poderíamos criar para cada um outra classe:
public class Diretor
{
	public string Nome { get; set; }
	public string CPF { get; set; }
	public double Salario { get; set; }
}
mas neste caso estaríamos apenas repetindo código. Funcionário tem as mesmas propriedades do Diretor... e do Desenvovledor também terá. Para evitar repetição de código e relacionar dizer pro compilador que toda classe de Diretor é um Funcionário, podemos utilizar o conceito de Herança. No C# para especificar a herança usamos dois pontos, assim:
public class Diretor : Funcionario { }
Através desse código, podemos criar um objeto Diretor e estanciar as mesmas propriedades de um funcionário:
Diretor camila = new Diretor();
camila.Nome = "Camila";
camila.CPF = "123.456.789-01";
camila.Salario = 1000;
E os métodos? Também são compartilhados? Sim! Por exemplo:
public class Funcionario
{
	public string Nome { get; set; }
	public string CPF { get; set; }
	public double Salario { get; set; }
    
	public void AumentarSalario()
	{
		Salario *= 1.15; //aumento de 15%
	}
}

public class Diretor : Funcionario
{
	public void AumentarSalario()
	{
		Salario *= 1.11; //aumento de 11%
	}
}
Supondo que o aumento do Diretor é diferente de um funcionário comum, poderíamos criar este código, porém a função AumentarSalario() não será sobrescrita se tivermos o seguinte código:
Funcionario funcionario = new Diretor();
Mesmo sendo completamente compilável e correto, a função AumentarSalario não estará correta. Para sobrescrever funções, em caso de herança em C#, é necessário tornar a função da classe mais genérica virtual. Dessa forma sinalizamos para o compilador que ela pode ser sobrescrita por outras classes... e na classe que herda temos que especificar que a função será sobrescrita. Para isso usamos as palavras reservadas "virtual" e "override".
public class Funcionario
{
	(...)
	public virtual void AumentarSalario()
	{
		Salario *= 1.15; //aumento de 15%
	}
}

public class Diretor : Funcionario
{
	public override void AumentarSalario()
	{
		Salario *= 1.11; //aumento de 11%
	}
}
Dessa forma garantimos que o código sempre retorne a porcentagem correta de aumento para o Diretor. Mas, todo funcionário sempre terá um tipo específico, não é mesmo? Então instanciar a classe funcionário pode gerar um problema, pois não saberemos qual funcionário se trata. Gostaríamos de sempre instaciar apenas as classes que herdam de funcionário. Em C#, para impedir que isso aconteça, tornamos a classe abstrata.
Classes abstratas são classes que servem como modelo para suas subclasses. Classes abstratas não podem ser instanciadas, apenas herdadas. Os métodos declarados na classe abstrata devem ser implementados nas subclasses (classes concretas).
Assim, impedimos que classes genéricas sejam instânciadas. Porém, quando uma classe se torna abstrata, como vimos na descrição, ela não deve implementar métodos, apenas indicar métodos que devem ser implementados nas classes que herdam dela, dessa forma, o nosso novo código será:
public abstract class Funcionario
{
	(...)
	public abstract void AumentarSalario();
}

public class Diretor : Funcionario
{
	public override void AumentarSalario()
	{
		Salario *= 1.11; //aumento de 11%
	}
}
Existe também a possibilidade, no C#, do uso da palavra reservada "base" quando desejamos passar informações da subclasse para a classe abstrata. Podemos utilizar o exemplo no caso de termos construtores:
public abstract class Funcionario
{
	(...)
	public Funcionario(string cpf, double salario)
	{
		CPF = cpf;
		Salario = salario;
	}	
	public abstract void AumentarSalario();
}

public class Diretor : Funcionario
{
	public Diretor(string cpf) : base(cpf, 5000)
	{ }
        
	public override void AumentarSalario()
	{
		Salario *= 1.11; //aumento de 11%
	}
}
No código acima, definimos que toda vez que instaciamos uma classe que herda de Funcionario deve ser especificado, obrigatoriamente um CPF e um salário. E fizemos ainda mais, todo Diretor da empresa tem o salário inicial de 5000. Dessa forma, não precisamos escrever o construtor em todas as subclasses, podemos passar os valores para funcionário utilizado a palavra reservada base. Mas, vamos imaginar que nosso sistema precisa de modificações. A empresa decidiu dar acesso externo a clientes da empresa. E além disso, é necessário implementar uma forma de autenticação para esses clientes e para Diretores da empresa, afim de acessar dados específicos apenas visiveis para eles. Como fazer então? Teoricamente seria possivel criar uma outra classe abstrada chamada Autenticação e tanto o Cliente quanto o Diretor herdariam dessa classe. Porém, com o Diretor teríamos duas heranças, e em C# não é permitido ter heranças multiplas... então, temos que pensar em outra solução. Um novo conceito que pode nos ajudar nesta tarefa é o de interfaces:
Interfaces definem o que uma classe deve fazer e não como, podendo se assimilar a um "contrato assinado". Dessa forma, as interfaces não possuem a implementação de métodos (apenas os declaram), deixando a classe responsável por usa implementação.
Uma interface, diferente da herança, pode ser utilizada por diversas classes e podemos ter multiplas interfaces dentro de uma "subclasse". Quando usamos interface o conceito de subclasse não existe mais, pois a classe que implementa uma interface "assina um compromisso" de implementar os métodos que a interface definiu. Então, voltando ao exemplo, podemos criar uma interface para Autenticação:
public interface IAutenticavel
{
	bool Autenticar(string senha);
}
E agora a classe Diretor pode implementar esta interface:
public class Diretor : IAutenticavel, Funcionario
{
	public string Senha { get; set; }
	(...)
	public bool Autenticar(string senha)
	{
		return Senha == senha;
	}    
}

public class ClienteExterno : IAutenticavel
{
	public String Nome { get; set; }
	public String Senha { get; set; }
	public bool Autenticar(string senha)
	{
		return Senha == senha;
	}
}
A implementação do método Autenticar pode ser completamente diferente nas duas classes, inclusve podem retornar acesso a diferentes áreas, foi apenas uma conincidência (e praticidade) aparecerem iguais!