Alura > Cursos de Programação > Cursos de .NET > Conteúdos de .NET > Primeiras aulas do curso C#: criando e distribuindo bibliotecas em .NET

C#: criando e distribuindo bibliotecas em .NET

Projeto de biblioteca - Apresentação

Boas-vindas! Meu nome é André Bessa, serei seu instrutor ao longo deste treinamento, em que vamos estudar bibliotecas de DLLs.

André Bessa é um homem negro de rosto arredondado, com cabelo baixo e barba por fazer. Veste uma camiseta rosa claro e está em um ambiente com iluminação azul suave. Ao fundo, há uma estante de ferro com livros e outros objetos, e uma parede com dois pôsteres.

Este curso é voltado para pessoas que já conhecem a linguagem C# e desejam se aprofundar nos estudos, aprendendo a compartilhar componentes entre projetos. Para melhor aproveitamento deste curso, é recomendado que você já tenha os conhecimentos sobre fundamentos da linguagem C# e do paradigma de programação orientada a objetos.

Continuaremos a atender nosso cliente fictício, o banco virtual Bytebank. No decorrer do curso, vamos aprender sobre:

Ao final, teremos um componente no formato de uma DLL, que poderá ser reaproveitado em outras soluções.

Espero que você esteja animado! Vamos estudar?

Projeto de biblioteca - Criando um projeto de classes

Para iniciar nossos estudos, vamos fazer o download do projeto inicial. Trata-se do mesmo projeto console que usamos no treinamento de C# sobre arrays e coleções.

De início, vamos rodar a aplicação para conferir as funcionalidades disponíveis. Clicando no ícone de play no menu superior do Visual Studio, o programa será executado no console, onde aparecerá o menu principal, com opções de 1 a 6. Esse projeto visa atender o gerente de contas do Bytebank, permitindo:

  1. Cadastrar conta
  2. Listar contas
  3. Remover conta
  4. Ordenar contas
  5. Pesquisar contas
  6. Sair do sistema

Voltando ao Visual Studio, vamos pressionar "Ctrl + Alt + L" para abrir o gerenciador de soluções na lateral direita da IDE. Nesse projeto, há uma série de namespaces, cada um com sua funcionalidade.

Um desses namespaces é o "bytebank.Modelos", onde constam os modelos das classes de funcionários, parceiros comerciais, utilitários, conta-corrente, entre outros. Especificamente nessa solução, estamos trabalhando somente com a classe ContaCorrente, responsável pelas funcionalidades que conferimos há pouco (cadastro, listagem etc).

Após algumas discussões, nós da equipe de desenvolvimento do Bytebank chegamos à conclusão que o namespace "bytebank.Modelos" pode ser reutilizado em outros projetos. Há outras soluções rodando em paralelo que também poderiam fazer uso das classes Funcionario, Cliente e ContaCorrente, por exemplo.

Portanto, nosso objetivo é descobrir uma maneira de reaproveitar esse namespace em outros projetos. Precisamos de um projeto compartilhado, um pacote de classes que possa ser consumido por outras soluções.

Biblioteca de classes

Com tudo isso mente, vamos desenvolver um novo tipo de projeto que conterá essas classes. Podemos criá-lo em uma nova instância do Visual Studio ou dentro da própria solução. No caso, seguiremos pela segunda opção.

Quando iniciamos um projeto no Visual Studio — por exemplo, de console, API ou Windows Forms —, sempre criamos uma solução e, dentro dessa solução, temos o nosso projeto. Uma solução pode ter mais de um projeto e é isso que faremos a seguir.

No gerenciador de soluções, clicaremos com o botão direito do mouse sobre a solução "bytebank_ATENDIMENTO" e selecionaremos "Adicionar > Novo Projeto...". Uma nova janela será aberta.

Esse projeto conterá somente as classes que serão consumidas por outros projetos do Bytebank. Então, selecionaremos a opção "Biblioteca de Classes". Na parte superior da tela, há um campo de pesquisa que pode nos auxiliar a filtrar essa opção. Após selecioná-la, pressionaremos o botão "Próximo" no canto inferior direito.

No campo "Nome do projeto", digitaremos "bytebank_Modelos". Assim, mantemos a uniformidade entre os nomes dos projetos dessa solução. Em seguida, clicaremos em "Próximo" novamente. Na nova tela, vamos manter o framework ".NET 6.0" selecionado e pressionar o botão "Criar" no canto inferior direito.

Ao criar esse projeto na nossa solução, será gerado automaticamente um arquivo chamado Class1.cs, em branco. Vamos excluí-lo do nosso projeto.

Nosso primeiro objeto é transferir o namespace "bytebank.Modelos" para o nosso projeto "bytebank_Modelos". No gerenciador de soluções, vamos clicar com o botão direito sobre "bytebank.Modelos" e selecionar "Recortar" (ou usar o atalho "Ctrl + X"). Em seguida, clicaremos com o botão direito sobre "bytebank_Modelos" e selecionaremos "Colar" ("Ctrl + V"). Pronto, a primeira etapa já foi feita!

O projeto bytebank_Modelos conterá as classes referentes aos modelos usados nos projetos do Bytebank, como ContaCorrente e Funcionarios. Outras classes que venham a ser criadas também serão adicionadas nessa biblioteca, que será utilizada para diversos projetos do Bytebank.

Na sequência, vamos compilar nossa solução para gerar um executável e checar se há qualquer tipo de erro. No gerenciador de soluções, clicaremos com o botão direito sobre a solução e selecionar "Recompilar Solução". No painel inferior do Visual Studio, aparecerá uma lista com 12 erros. Como movemos um namespace para outro projeto (na mesma solução), as referências ao "bytebank.Modelos" não funcionam mais.

Como exemplo, podemos abrir o arquivo ByteBankAtendimento.cs. Essa classe, que encapsula as funcionalidades de atendimento para montar o menu, não reconhece mais onde encontrar a classe ContaCorrente! Para resolver esse problema, é preciso que o projeto bytebank_ATENDIMENTO consiga reconhecer o projeto bytebank_Modelos.

Então, no bytebank_ATENDIMENTO, temos que referenciar o projeto bytebank_Modelos. No gerenciador de soluções, imediatamente abaixo do projeto bytebank_ATENDIMENTO, vamos clicar em "Dependências" e selecionar "Adicionar Referência de Projeto...". Uma nova janela será aberta, onde estará listado o projeto bytebank_Modelos, pois estamos dentro da mesma solução. Vamos selecionar a caixa à esquerda dele e, em seguida, pressionar o botão "OK" no canto inferior direito.

Ao clicar no projeto bytebank_ATENDIMENTO, o Visual Studio abrirá o arquivo bytebank_ATENDIMENTO.csproj, que contém as configurações do nosso projeto, como a versão do .NET. Abaixo de <ItemGroup>, foi adicionada a referência para o bytebank_Modelos:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\bytebank_Modelos\bytebank_Modelos.csproj" />
  </ItemGroup>

</Project>

Após salvar essa alteração, vamos recompilar nossa solução. Dessa vez, não teremos erros e nossa solução voltará a funcionar. Nós removemos o namespace referente aos modelos, adicionamos uma biblioteca de classes à solução e incluímos a referência a essa biblioteca no projeto bytebank_ATENDIMENTO.

Para nos certificar de tudo continua funcionando normalmente, podemos rodar a aplicação. No menu, vamos digitar "2" e pressionar "Enter" para conferir a lista de clientes iniciais do programa, por exemplo.

Portanto, a nossa meta é reaproveitar essa biblioteca em outros projetos. Nós já criamos uma biblioteca de classes com modelos do nosso negócio e, na sequência, podemos encapsular algumas funcionalidades nela.

Existem bibliotecas de classes com funções bem específicas, por exemplo: manipulação um arquivo ou acesso a um banco de dados. Uma vez que entendermos como criar uma biblioteca de classes e o que podemos encapsular nela, exploraremos como utilizar bibliotecas de terceiros para adicionar mais funcionalidades ao nosso projeto bytebank_ATENDIMENTO.

Projeto de biblioteca - Bibliotecas externas

Acabamos de criar nosso primeiro projeto de biblioteca, chamado bytebank_Modelos. Ele contém as classes referentes aos modelos que podem ser utilizados em projetos internos do Bytebank. Por exemplo, na pasta "Conta", temos as classes ContaCorrente e Cliente. Já na parte administrativa (pasta "ADM"), temos Funcionario, Auxiliar, Desenvolvedor, Designer, Diretor e assim em diante.

Contudo, uma biblioteca de classes não contempla apenas modelos que podem ser reaproveitados. Também é possível incluir funcionalidades, como a opção de gerar um arquivo TXT ou de nos conectar a um banco de dados específico.

A partir dessas bibliotecas, conseguimos montar nosso projeto como se fosse um LEGO, em que cada peça tem sua função — uma biblioteca gerará arquivos, outra conectará ao banco de dados, outra implementará a interface gráfica etc.

Pacote Newtonsoft.Json

Há uma nova demanda para o projeto bytebank_ATENDIMENTO. Precisamos gerar um arquivo no formato JSON com todas as contas cadastradas no nosso sistema, para ser enviado ao Banco Central. Nós poderíamos criar uma biblioteca responsável por esse processo, mas será que já não existe uma que faça isso? Vamos pesquisar.

O NuGet é um repositório de pacotes de bibliotecas da plataforma .NET em que podemos pesquisar se existe uma biblioteca com a função que precisamos. No menu superior do Visual Studio, vamos acessar "Ferramentas > Gerenciador de Pacotes do NuGet > Gerenciar Pacotes do NuGet para a Solução". Também podemos trabalhar na linha de comando, usando CLI.

Na aba "Procurar", selecionaremos o pacote Newtonsoft.Json, por James Newton-King (provavelmente a primeira opção da lista). Se necessário, você pode utilizar a barra de pesquisa no topo da tela, pressionando "Ctrl + L".

No vídeo, estamos instalando a versão 13.0,1, a mais recente no momento de gravação deste curso.

Ao selecionar o pacote, será aberto um painel à direita com uma lista de nossos projetos. Vamos marcar a caixa referente ao bytebank_ATENDIMENTO para instalar a biblioteca nele. Em seguida, pressionaremos o botão "Instalar" na parte direita inferior da lista.

Aparecerá uma mensagem na tela e, para confirmar a alteração do projeto, clicaremos em "OK" no canto inferior direito. O primeiro indício de que a instalação foi finalizada é que o botão "Desinstalar" será habilitado, imediatamente acima do botão "Instalar" que pressionamos há pouco.

Acabamos de instalar o pacote Newtonsoft, que trabalhará com o formato JSON. Já podemos fechar a aba do gerenciador do NuGet.

Nova funcionalidade

Para implementar a nova funcionalidade, precisamos conhecer nosso projeto. Vamos acessar o arquivo ByteBankAtendimento.cs, que encapsula as funções do nosso projeto atual.

De início, essa classe cria uma lista interna de contas já cadastradas para facilitar nosso trabalho. Em seguida, temos o menu com 6 opções — cadastrar, listar, remover, ordenar, pesquisar contas e sair do sistema. Vamos adicionar a funcionalidade de exportação de contas no menu e incluir um novo case na estrutura switch, com o método ExportarContas(). Além disso, na definição do while, substituiremos (opcao != '6') por (opcao != '7'):

// ...

public void AtendimentoCliente()
{
    try
    {
        char opcao = '0';
        while (opcao != '7')
        {
            Console.Clear();
            Console.WriteLine("===============================");
            Console.WriteLine("===       Atendimento       ===");
            Console.WriteLine("===1 - Cadastrar Conta      ===");
            Console.WriteLine("===2 - Listar Contas        ===");
            Console.WriteLine("===3 - Remover Conta        ===");
            Console.WriteLine("===4 - Ordenar Contas       ===");
            Console.WriteLine("===5 - Pesquisar Conta      ===");
            Console.WriteLine("===6 - Exportar Conta       ===");
            Console.WriteLine("===7 - Sair do Sistema      ===");
            Console.WriteLine("===============================");
            Console.WriteLine("\n\n");
            Console.Write("Digite a opção desejada: ");
            try
            {
                opcao = Console.ReadLine()[0];
            }
            catch (Exception excecao)
            {
                throw new ByteBankException(excecao.Message);
            }

            switch (opcao)
            {
                case '1':
                    CadastrarConta();
                    break;
                case '2':
                    ListarContas();
                    break;
                case '3':
                    RemoverContas();
                    break;
                case '4':
                    OrdenarContas();
                    break;
                case '5':
                    PesquisarContas();
                    break;
                case '6':
                    ExportarContas();
                    break;
                case '7':
                    EncerrarAplicacao();
                    break;
                default:
                    Console.WriteLine("Opcao não implementada.");
                    break;
            }
        }
    }
    catch (ByteBankException excecao)
    {
        Console.WriteLine($"{excecao.Message}");
    }
}

// ...

O Visual Studio indicará um erro em ExportarContas() porque esse método ainda não existe. Vamos desenvolvê-lo, a seguir.

Posicionando o cursor sobre ExportarContas(), podemos pressionar "Ctrl + ." ou clicar no ícone de lâmpada amarela que aparece à esquerda dessa linha e, em seguida, selecionar "Gerar método 'ExportarContas'". Logo abaixo do while, teremos a seguinte estrutura:

private void ExportarContas()
{
    throw new NotImplementedException();
}

Vamos adaptá-la:

private void ExportarContas()
{
        Console.Clear();
        Console.WriteLine("===============================");
        Console.WriteLine("===     EXPORTAR CONTAS     ===");
        Console.WriteLine("===============================");
        Console.WriteLine("\n");

        if (_listaDeContas.Count <= 0)
        {
                Console.WriteLine("... Não existe dados para exportação...");
                Console.ReadKey();
        }
}

Seguindo o padrão dos demais métodos nesse arquivo, primeiramente imprimimos um cabeçalho. Em seguida, temos uma estrutura if para fazer uma validação. Se a _listaDeContas (isto é, o objeto que contém as contas cadastradas) for menor ou igual a 0, exibimos uma mensagem de que não há dados para exportação. Do contrário, trabalharemos com o JSON.

Então, vamos desenvolver o else. A princípio, criaremos uma variável do tipo string chamada json e usaremos a biblioteca Newtonsoft. Nela, temos a classe JsonConvert, que possui o método SerializeObject():

private void ExportarContas()
{
        Console.Clear();
        Console.WriteLine("===============================");
        Console.WriteLine("===     EXPORTAR CONTAS     ===");
        Console.WriteLine("===============================");
        Console.WriteLine("\n");

        if (_listaDeContas.Count <= 0)
        {
                Console.WriteLine("... Não existe dados para exportação...");
                Console.ReadKey();
        }
        else
        {
            string json = JsonConvert.SerializeObject();
        }
}

Esse método é responsável pela serialização, por transformar um objeto em outro formato para que ele seja persistido em um arquivo ou em uma base de dados ou seja transferido para outro sistema. Vale lembrar que também existe o caminho contrário: a desserialização, em que retornamos um formato específico como um objeto.

O método SerializeObject() receberá como parâmetro a nossa lista de contas e o formato:

private void ExportarContas()
{
        Console.Clear();
        Console.WriteLine("===============================");
        Console.WriteLine("===     EXPORTAR CONTAS     ===");
        Console.WriteLine("===============================");
        Console.WriteLine("\n");

        if (_listaDeContas.Count <= 0)
        {
                Console.WriteLine("... Não existe dados para exportação...");
                Console.ReadKey();
        }
        else
        {
            string json = JsonConvert.SerializeObject(_listaDeContas,
                Formatting.Indented);
        }
}

Quando trabalhamos com bibliotecas de terceiros, é interessante consultar a documentação para conhecer suas funcionaliades e descobrir como utilizá-las. A documentação do Newtonsoft, por exemplo, está disponível tanto no repositório do NuGet quanto no site oficial da biblioteca.

Em seguida, vamos criar um arquivo. Usaremos a FileStream, uma bliblioteca do .NET para fazer operações de leitura e escrita de arquivos nos diretórios da própria máquina.

Para tratar possíveis exceções nesses processos de leitura e escrita, vamos desenvolver uma estrutura try/catch:

private void ExportarContas()
{
        Console.Clear();
        Console.WriteLine("===============================");
        Console.WriteLine("===     EXPORTAR CONTAS     ===");
        Console.WriteLine("===============================");
        Console.WriteLine("\n");

        if (_listaDeContas.Count <= 0)
        {
                Console.WriteLine("... Não existe dados para exportação...");
                Console.ReadKey();
        }
        else
        {
            string json = JsonConvert.SerializeObject(_listaDeContas,
                Formatting.Indented);
            try
            {

            }
            catch (Exception)
            {
                throw;
            }
        }
}

No bloco try, criaremos um FileStream chamado fs. Exportaremos o arquivo para o caminho c:\tmp\export\contas.json. Como se trata de criação de arquivo, usaremos o FileMode.Create:

private void ExportarContas()
{
        Console.Clear();
        Console.WriteLine("===============================");
        Console.WriteLine("===     EXPORTAR CONTAS     ===");
        Console.WriteLine("===============================");
        Console.WriteLine("\n");

        if (_listaDeContas.Count <= 0)
        {
                Console.WriteLine("... Não existe dados para exportação...");
                Console.ReadKey();
        }
        else
        {
            string json = JsonConvert.SerializeObject(_listaDeContas,
                Formatting.Indented);
            try
            {
                FileStream fs = new FileStream(@"c:\tmp\export\contas.json",
                    FileMode.Create);
            }
            catch (Exception)
            {
                throw;
            }
        }
}

Lembre-se de criar o caminho c:\tmp\export\ na sua máquina. Do contrário, ocorrerá um erro na execução da aplicação.

Na sequência, usaremos a estrutura using para escrever nesse arquivo. Desse modo, criaremos um objeto e o descartaremos após sua utilização. Vamos criar um StreamWriter chamado streamwriter. Como parâmetro, passaremos o fs:

private void ExportarContas()
{
        Console.Clear();
        Console.WriteLine("===============================");
        Console.WriteLine("===     EXPORTAR CONTAS     ===");
        Console.WriteLine("===============================");
        Console.WriteLine("\n");

        if (_listaDeContas.Count <= 0)
        {
                Console.WriteLine("... Não existe dados para exportação...");
                Console.ReadKey();
        }
        else
        {
            string json = JsonConvert.SerializeObject(_listaDeContas,
                Formatting.Indented);
            try
            {
                FileStream fs = new FileStream(@"c:\tmp\export\contas.json",
                    FileMode.Create);
                using (StreamWriter streamwriter = new StreamWriter(fs))
                {

                }
            }
            catch (Exception)
            {
                throw;
            }
        }
}

Agora, usando o método WriteLine() do objeto streamwriter, conseguiremos escrever o conteúdo da variável json no arquivo. Após o using, vamos imprimir uma mensagem para avisar o usuário que o arquivo foi salvo, indicando o diretório de destino.:

private void ExportarContas()
{
        Console.Clear();
        Console.WriteLine("===============================");
        Console.WriteLine("===     EXPORTAR CONTAS     ===");
        Console.WriteLine("===============================");
        Console.WriteLine("\n");

        if (_listaDeContas.Count <= 0)
        {
                Console.WriteLine("... Não existe dados para exportação...");
                Console.ReadKey();
        }
        else
        {
            string json = JsonConvert.SerializeObject(_listaDeContas,
                Formatting.Indented);
            try
            {
                FileStream fs = new FileStream(@"c:\tmp\export\contas.json",
                    FileMode.Create);
                using (StreamWriter streamwriter = new StreamWriter(fs))
                {
                    streamwriter.WriteLine(json);
                }
                Console.WriteLine(@"Arquivo salvo em c:\tmp\export\");
                Console.ReadKey();
            }
            catch (Exception)
            {
                throw;
            }
        }
}

A seguir, modificaremos o bloco catch. Nele, vamos lançar um ByteBankException, a classe de exceção do nosso projeto:

private void ExportarContas()
{
        Console.Clear();
        Console.WriteLine("===============================");
        Console.WriteLine("===     EXPORTAR CONTAS     ===");
        Console.WriteLine("===============================");
        Console.WriteLine("\n");

        if (_listaDeContas.Count <= 0)
        {
                Console.WriteLine("... Não existe dados para exportação...");
                Console.ReadKey();
        }
        else
        {
            string json = JsonConvert.SerializeObject(_listaDeContas,
                Formatting.Indented);
            try
            {
                FileStream fs = new FileStream(@"c:\tmp\export\contas.json",
                    FileMode.Create);
                using (StreamWriter streamwriter = new StreamWriter(fs))
                {
                    streamwriter.WriteLine(json);
                }
                Console.WriteLine(@"Arquivo salvo em c:\tmp\export\");
                Console.ReadKey();
            }
            catch (Exception excecao)
            {
                throw new ByteBankException(excecao.Message);
                Console.ReadKey();
            }
        }
}

Recapitulando: selecionamos uma lista de objetos, realizamos uma serialização no formato JSON indentado e armazenamos o resultado na variável json. Depois, criamos um arquivo chamado contas.json, escrevemos o conteúdo da variável json nele e salvamos o arquivo.

Testando

Para testar, vamos executar o projeto. No menu da aplicação, pressionaremos "2" para checar as contas cadastradas. Temos as contas do Henrique (com final X), do Pedro (com final X) e da Marisa (com final W).

Voltando ao menu, pressionaremos "6" para exportar as contas. No console, teremos a seguinte mensagem:

Arquivo salvo em c:\tmp\export\

Vamos abrir o gerenciador de arquivos e navegar até "c:\tmp\export", onde teremos o arquivo contas.json! Clicaremos sobre ele com o botão direito do mouse e selecionaremos "Abrir com > Bloco de Notas". Seu conteúdo é o seguinte:

[
  {
    "Titular": {
      "Cpf": "11111",
      "Nome": "Henrique",
      "Profissao": null
    },
    "Nome_Agencia": null,
    "Numero_agencia": 95,
    "Conta": "123456-X",
    "Saldo": 100.0
  },
  {
    "Titular": {
      "Cpf": "22222",
      "Nome": "Pedro",
      "Profissao": null
    },
    "Nome_Agencia": null,
    "Numero_agencia": 95,
    "Conta": "951258-X",
    "Saldo": 200.0
  },
  {
    "Titular": {
      "Cpf": "33333",
      "Nome": "Marisa",
      "Profissao": null
    },
    "Nome_Agencia": null,
    "Numero_agencia": 94,
    "Conta": "987321-W",
    "Saldo": 60.0
  }
]

Assim, temos a lista de contas salva em um arquivo no formato JSON, que podemos enviar para o Banco Central e atender à demanda do Bytebank.

Na sequência, continuaremos evoluindo nossa aplicação, entendendo mais sobre bibliotecas e como trabalhar com elas. Estudaremos o nível de visibilidade e como aplicar esse conceito no contexto de bibliotecas de classes.

Sobre o curso C#: criando e distribuindo bibliotecas em .NET

O curso C#: criando e distribuindo bibliotecas em .NET possui 76 minutos de vídeos, em um total de 38 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:

Aprenda .NET acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas