Olá estudante! Boas-vindas a este curso. Eu sou o instrutor Marcelo Oliveira.
Audiodescrição: Marcelo se autodescreve como um homem branco, de olhos castanhos escuros, cabelos e barbas escuras. Usa óculos de grau com armação preta, retangular e arredondada nas laterais. Está vestindo uma camiseta preta. Ao fundo, uma parede azul.
Você será a pessoa encarregada pelo Bytebank, um banco digital para desenvolver e integrar soluções com relatórios de boletos.
Vamos aprender a ler arquivos de texto e usar Reflection para preencher objetos do tipo boleto.
Depois, vamos ler propriedades de objeto, criar e ler atributos customizados utilizando Reflection.
Mais adiante, vamos usar Reflection para instanciar objetos e invocar métodos dinamicamente.
Posteriormente, vamos investigar tipos de assembly , filtrando por metadados.
Por fim, vamos criar uma arquitetura de plugins, investigando e executando assemblies externos.
Ao longo desse curso, você terá todo o código necessário para fazer a implementação e recorrer a ele sempre que tiver alguma dificuldade!
E se você aplicar o conhecimento de Reflection que adquirir neste curso, o que aprender aqui fará diferença no seu trabalho e também nos seus projetos pessoais.
Portanto, aproveite os recursos da plataforma Alura, além dos vídeos, também temos as atividades e todo o apoio do fórum e da comunidade do Discord.
E aí, vamos estudar?
Neste primeiro vídeo, vamos apresentar o projeto inicial que acompanha este curso.
Aqui, você será a pessoa desenvolvedora responsável por criar o módulo de acesso e manipulação de boletos para o Bytebank.
Vamos aprender a abrir o arquivo de boletos, ler cada uma das linhas desse arquivo, que estará no formato de texto, e, no final, você vai converter cada linha desse arquivo de boleto em um objeto da classe Boleto
.
Vamos para o Visual Studio para ver o código inicial do nosso projeto.
Aqui, você está vendo a solução Bytebank que acompanha este curso. Essa solução, esse projeto inicial, você baixou como primeira atividade da primeira aula deste curso.
O primeiro projeto é o Bytebank.Common
, que é um projeto do tipo biblioteca de classes. Dentro desse projeto, vamos criar a maior parte do código deste curso.
O outro projeto é o Bytebank.ConsoleApp
, que é uma aplicação do tipo console application, onde você vai rodar a aplicação e o texto vai aparecer em uma janela no formato de texto para você interagir, onde você vai escolher opções do menu para rodar operações relacionadas a boleto do Bytebank.
Vamos rodar agora a aplicação com a tecla "F5" e veremos a aplicação rodando o código inicial, que por enquanto só tem um menu.
Nesta execução, temos o logo do Bytebank, logo abaixo temos o seguinte menu com uma única opção:
Escolha uma opção:
- Ler arquivo de boletos
Digite o número da opção desejada:
Quando você escolhe a opção 1 e tecla "Enter", ele vai rodar o código que está na classe LeitorDeBoleto
, dentro do método LerBoletos
.
Aqui ele está passando, como argumento para este método, um parâmetro CaminhoArquivo
. Esse CaminhoArquivo
é um arquivo chamado boletos.csv
.
public class LeitorDeBoleto
{
public List<Boleto> LerBoletos(string caminhoArquivo)
// código omitido
A aplicação parou com uma exceção, foi lançada uma exceção do tipo código não implementado, Not Implemented Exception, porque ainda não implementamos o método LerBoletos
.
Vou parar a execução desse arquivo e colar o seguinte bloco de código dentro do método LerBoletos
:
LeitorDeBoleto.cs
public List<Boleto> LerBoletos(string caminhoArquivo)
{
//throw new NotImplementedException();
// montar lista de boletos
var boletos = new List<Boleto>();
// ler arquivo de boletos
using (var reader = new StreamReader(caminhoArquivo))
{
// ler cabeçalho do arquivo CSV
string linha = reader.ReadLine();
string[] cabecalho = linha.Split(',');
// para cada linha do arquivo CSV
while (!reader.EndOfStream)
{
// ler dados
linha = reader.ReadLine();
string[] dados = linha.Split(',');
// carregar objeto Boleto
Boleto boleto = MapearTextoParaBoleto(cabecalho, dados);
// adicionar boleto à lista
boletos.Add(boleto);
}
}
// retornar lista de boletos
return boletos;
}
Após atualizar o código. Vamos rodar novamente a aplicação com a tecla "F5".
Agora, ao selecionarmos a opção 1, ele vai ler arquivo de boletos.
Lendo arquivo de boletos... Cedente: Panificadora Sonho Meu Sonho Teu, Valor: 1500,00, Vencimento: 01/02/2024 00:00:00
Cedente: Panificadora Sonho Meu Sonho Teu, Valor: 2200,00, Vencimento: 15/02/2024 00:00:00
Cedente: Panificadora Sonho Meu Sonho Teu, Valor: 3000,00, Vencimento: 01/03/2024 00:00:00
Cedente: Los Pollos Hermanos Restaurante Ltda, Valor: 4200,00, Vencimen to: 15/03/2024 00:00:00
Ele mostra os dados que estão vindo lá do arquivo de boletos.
O que estamos vendo aqui é a transformação das linhas de um arquivo texto para uma lista de objetos do tipo Boleto
. Vamos ver um pouco agora os detalhes da implementação desse projeto.
Vamos olhar primeiro o arquivo CSV, que é um arquivo de valores separados por vírgula.
Vamos abrir o arquivo boletos.csv
, onde temos na primeira linha os dados do cedente, que é a organização, a empresa que está recebendo o valor do boleto.
Mais para frente temos aqui, ainda na primeira linha: SacadoNome, SacadoCpfCnpj, SacadoEndereco e assim por diante, que são os dados da pessoa que paga o boleto, o pagador do boleto.
E logo abaixo, temos, também separado por vírgula, os valores de cada um dos campos desse boleto.
Então, o que fizemos foi ler esse arquivo e depois converter para um objeto da classe Boleto
.
Boleto.cs
A classe Boleto
está localizada no outro projeto, que é o Bytebank.Common
. Vamos abrir esse arquivo Boleto.cs
, e aqui você vai encontrar, como propriedade dessa classe, os mesmos nomes de coluna que você já tinha lá no arquivo CSV de boletos:
namespace ByteBank.Common
{
public class Boleto
{
// Informações do Cedente (Beneficiário)
public string CedenteNome { get; set; }
public string CedenteCpfCnpj { get; set; }
public string CedenteAgencia { get; set; }
public string CedenteConta { get; set; }
// Informações do Sacado (Pagador)
public string SacadoNome { get; set; }
public string SacadoCpfCnpj { get; set; }
public string SacadoEndereco { get; set; }
// Informações do Boleto
public decimal Valor { get; set; }
public DateTime DataVencimento { get; set; }
public string NumeroDocumento { get; set; }
public string NossoNumero { get; set; }
// Outras Informações
public string CodigoBarras { get; set; }
public string LinhaDigitavel { get; set; }
}
}
Então, existe um casamento entre os nomes de colunas do arquivo texto, e os nomes de propriedades da nossa classe Boleto
.
LeitorDeBoleto
Agora, vamos analisar o código que acabamos de colar aqui na classe LeitorDeBoleto
.
LeitorDeBoleto.cs
public List<Boleto> LerBoletos(string caminhoArquivo)
{
//throw new NotImplementedException();
// montar lista de boletos
var boletos = new List<Boleto>();
// ler arquivo de boletos
using (var reader = new StreamReader(caminhoArquivo))
{
// ler cabeçalho do arquivo CSV
string linha = reader.ReadLine();
string[] cabecalho = linha.Split(',');
// para cada linha do arquivo CSV
while (!reader.EndOfStream)
{
// ler dados
linha = reader.ReadLine();
string[] dados = linha.Split(',');
// carregar objeto Boleto
Boleto boleto = MapearTextoParaBoleto(cabecalho, dados);
// adicionar boleto à lista
boletos.Add(boleto);
}
}
// retornar lista de boletos
return boletos;
}
Quando abro o LeitorDeBoleto
, vejo aqui o método LerBoletos
, e aqui dentro o que ele vai fazer?
Ele começa montando uma lista de boletos, declarando a variável boletos
, como uma nova lista de Boleto
, e aí ele começa a ler o arquivo de boletos.
Em seguida, um objeto da classe StreamReader
, que é o leitor de streams do .NET, e ele vai ler então o arquivo que foi fornecido aqui no CaminhoArquivo
, que é o arquivo boletos.csv
.
Em seguida, ele vai começar a ler o cabeçalho do arquivo csv, nas linhas string linha
e string[] cabecalho
ele vai armazenar numa variável linha
, a primeira linha do nosso arquivo, que contém o cabeçalho, e na linha 28 ele declara uma variável cabecalho
, que é um array de strings, que contém cada uma das colunas da primeira linha do nosso arquivo texto.
Depois, ele orienta num laço while
, para que para cada linha do arquivo csv, enquanto ele não encontrar o final desse arquivo, ele vai começar a ler os dados.
Ele vai armazenar, na variável linha
, o conteúdo de cada linha desse arquivo de boletos, e na linha string[] dados = linha.Split(',')
ele armazena na variável dados
, o que é um array de string, contendo os valores correspondentes a cada um dos campos de dados desse arquivo de boletos.
Então, dados
vai conter os dados propriamente ditos de cada um dos boletos.
Depois, ele vai chamar o método MapearTextoParaBoleto
, passando o array de strings, que é o cabecalho
, contendo nomes das colunas, e depois, como segundo parâmetro, a variável dados
, que agora é um parâmetro que contém os valores de cada um dos campos do cabecalho
. E depois, esse método MapearTextoParaBoleto
vai transformar cada linha do boleto em um objeto da classe Boleto
.
Depois, com boletos.Add(boleto)
esse código adiciona a lista de boletos, o boleto que acabou de ser criado, e com return boletos
retorna uma lista de boletos que então é exibida na nossa aplicação de console, como acabamos de ver.
Na linha 46, ele chama esse método MapearTextoParaBoleto
, que está localizado aqui abaixo na linha 63. Temos o método Boleto MapearTextoParaBoleto
, que ele recebe aqui como o cabecalho
, aqui ele chama o cabecalho
de NomesDasPropriedades
.
E o segundo parâmetro, que eram os dados do arquivo de boletos, aqui esse parâmetro é chamado de ValoresPropriedades
. Então você já pode estar percebendo que ele vai transformar o cabecalho
em nome de propriedades, para que? Para que ele possa acessar, para ele criar aqui na linha 65, uma nova instância de Boleto
, e a partir da linha 66, ele começa a mapear para cada coluna do cabecalho
do arquivo texto, ele começa a mapear para cada uma das propriedades correspondentes do nosso objeto da classe Boleto
.
E ele começa a atribuir o valor já numerado, já na sequência correta, ele pega os valoresPropriedades
, passando o índice adequado, para ele poder casar com as propriedades do nosso objeto Boleto
, e no final ele retorna uma instância nova do objeto Boleto
.
Então é assim que ele faz para montar o código, para montar uma lista de boletos e retornar lá para o nosso menu inicial.
Nesse vídeo, aprendemos a fazer a leitura de um arquivo de boletos, utilizando o StreamReader
para abrir o arquivo de boletos em modo leitura.
Depois, vimos como utilizar o método ReadLine
para ler cada um dos boletos no arquivo CSV, e depois, chamando o método MapearTextoParaBoleto
, para converter cada linha do arquivo texto CSV em um objeto da classe Boleto
.
Porém estamos utilizando até agora a criação de um boleto estaticamente, utilizando o operador new
, então declaramos aqui no código uma variável do tipo Boleto
e fazemos Boleto
igual a new Boleto
. Estamos fazendo tudo isso estaticamente.
A partir do próximo vídeo, começaremos a trabalhar com programação dinâmica no C#, vamos ver como o .NET permite que você utilize Reflection (Reflexão) para criar dinamicamente objetos da classe Boleto
, e mais adiante aprenderemos como manipular as propriedades desse objeto também dinamicamente.
No vídeo anterior, aprendemos a acessar o arquivo de boletos e ler cada uma de suas linhas, criando um objeto boleto correspondente.
Agora, vamos aprender a criar instâncias de boleto de forma dinâmica.
O método mapearTextoParaBoleto
é muito específico. Ele só pode trabalhar com a classe Boleto
.
Por exemplo, o tipo de retorno é Boleto
. Na linha Boleto instancia = new Boleto()
criamos uma instância específica de Boleto
. Isso cria um problema de dependência, de acoplamento. Estamos vinculando muito este método para ser utilizado com uma classe específica.
Para resolver isso, vamos utilizar o conceito de generics no C# e também criar a instância de Boleto
através de uma outra forma, utilizando uma classe específica para essa finalidade.
O primeiro passo para resolver esse problema é modificando a assinatura do nosso método MapearTextoParaBoleto
.
Em primeiro lugar, vamos renomear o método. Então, vamos clicar no atalho "Ctrl + R + R" para refatorar substituindo MapearTextoParaBoleto
por MapearTextoParaObjeto
.
Quando utilizamos o conceito de generics no C#, temos que passar na assinatura qual é o tipo genérico. Temos que especificar que esse método é genérico. Fazemos isso colocando logo após o nome do nosso método, os símbolos <
e >
, e no meio colocamos o T
, que representa o tipo genérico.
Além disso, temos que trocar o tipo de retorno, que não é mais Boleto
, vai ser o tipo genérico T
.
private T MapearTextoParaObjeto<T>(string[] nomesPropriedades, string[] valoresPropriedades)
Na linha 42, como estamos declarando uma instância de Boleto
, esse Boleto
também não pode ser mais a classe Boleto
, tem que ser o tipo genérico T
. No lugar do operador new
, vamos remover esse trecho e utilizar uma classe que se chama Activator
. Essa classe é específica para criar instâncias de objetos.
No Activator
temos um método estático que se chama CreateInstance
, e temos uma versão genérica do CreateInstance
onde recebemos entre <
e >
o tipo que queremos criar, que no caso vai ser Boleto
. Poderíamos colocar aqui, por exemplo, Boleto
, e aí depois abrir e fechar parênteses, seguido de ponto e vírgula, mas ainda estaríamos vinculados com Boleto
. Então, no lugar de Boleto
, temos que colocar o tipo T
com o qual estamos trabalhando nesse método.
private T MapearTextoParaObjeto<T>(string[] nomesPropriedades, string[] valoresPropriedades)
{
T instancia = Activator.CreateInstance<T>();
Com isso, teremos a criação genérica de um objeto dentro desse método.
Agora temos um problema que acabamos de criar, porque entre as linhas 43 até a 55 do arquivo LeitorDeBoleto.cs
, temos as propriedades da nossa classe Boleto
, mas agora que mapearTextoParaObjeto
é um método genérico, ele não vai conhecer essas propriedades.
Se tentarmos rodar o projeto agora, ele vai dar vários erros, porque essas propriedades não são conhecidas.
Já não estamos mais trabalhando com o tipo conhecido Boleto
, estamos trabalhando com o tipo genérico. Vamos resolver esse problema no próximo vídeo.
Por enquanto, vamos comentar as linhas das propriedades, 43 a 55, com o atalho "Ctrl + K + C" . Deixaremos para resolver isso no próximo vídeo.
Agora, que modificamos a assinatura do método mapearTextoParaObjeto
, temos que modificar também a chamada que é feita para ele aqui em cima, na linha 30.
Então, olha só, mapearTextoParaObjeto
, também temos que passar o tipo genérico, que nesse caso é Boleto
. Então, colocamos mapearTextoParaObjeto<Boleto>
.
// carregar objeto Boleto
Boleto boleto = MapearTextoParaObjeto<Boleto>(cabecalho, dados)
Voltando para o método, vamos salvar, e agora vamos rodar novamente com a tecla "F5".
Teremos o nosso console rodando, o menu "Escolha uma opção", número 1, "Ler arquivo de boletos". Digite o número da opção desejada, número 1, ele vai ler para nós todos os boletos do nosso arquivo, porém sem exibir as propriedades, os valores corretos, porque ainda não estamos definindo, ainda não estamos preenchendo as propriedades. Vamos fazer isso no próximo vídeo.
Nesse vídeo, aprendemos que existem duas formas comuns de criar uma instância de objeto no C#. Uma delas é o operador new
, onde vinculamos a criação da instância com uma classe pré-definida, ou então utilizando o Activator.CreateInstance<T>()
, onde criamos uma instância de forma genérica, de forma única.
Temos essa vantagem do Generics para criar de forma flexível um objeto de uma classe qualquer.
No nosso caso é uma classe Boleto
, mas futuramente poderíamos utilizar o mesmo método para criar, por exemplo, uma instância de uma classe chamada Pix
, que é uma outra forma conhecida de pagamento.
Temos essa vantagem do uso do CreateInstance
. Mas nem tudo são vantagens. Por exemplo, temos um impacto significativo no desempenho da nossa aplicação quando usamos por repetidas vezes o CreateInstance
. Então, é preciso ponderar se realmente não vamos ter um prejuízo ao utilizar o CreateInstance
.
Agora que fizemos esse primeiro passo, no sentido de deixar o nosso método mais genérico, a seguir vamos usar o conceito de Reflection do C# para conseguirmos trabalhar com as propriedades do nosso objeto, lendo dinamicamente os valores, associando, atribuindo e definindo as propriedades dinamicamente!
O curso C# Reflection: manipule dinamicamente tipos e assemblies possui 173 minutos de vídeos, em um total de 44 atividades. Gostou? Conheça nossos outros cursos de .NET em Programação, ou leia nossos artigos de Programação.
Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:
Impulsione a sua carreira com os melhores cursos e faça parte da maior comunidade tech.
1 ano de Alura
Assine o PLUS e garanta:
Formações com mais de 1500 cursos atualizados e novos lançamentos semanais, em Programação, Inteligência Artificial, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
A cada curso ou formação concluído, um novo certificado para turbinar seu currículo e LinkedIn.
No Discord, você tem acesso a eventos exclusivos, grupos de estudos e mentorias com especialistas de diferentes áreas.
Faça parte da maior comunidade Dev do país e crie conexões com mais de 120 mil pessoas no Discord.
Acesso ilimitado ao catálogo de Imersões da Alura para praticar conhecimentos em diferentes áreas.
Explore um universo de possibilidades na palma da sua mão. Baixe as aulas para assistir offline, onde e quando quiser.
Acelere o seu aprendizado com a IA da Alura e prepare-se para o mercado internacional.
1 ano de Alura
Todos os benefícios do PLUS e mais vantagens exclusivas:
Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos, corrige exercícios e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com a Luri até 100 mensagens por semana.
Aprenda um novo idioma e expanda seus horizontes profissionais. Cursos de Inglês, Espanhol e Inglês para Devs, 100% focado em tecnologia.
Transforme a sua jornada com benefícios exclusivos e evolua ainda mais na sua carreira.
1 ano de Alura
Todos os benefícios do PRO e mais vantagens exclusivas:
Mensagens ilimitadas para estudar com a Luri, a IA da Alura, disponível 24hs para tirar suas dúvidas, dar exemplos práticos, corrigir exercícios e impulsionar seus estudos.
Envie imagens para a Luri e ela te ajuda a solucionar problemas, identificar erros, esclarecer gráficos, analisar design e muito mais.
Escolha os ebooks da Casa do Código, a editora da Alura, que apoiarão a sua jornada de aprendizado para sempre.