DEV Community

Cover image for O que o .csproj conta sobre o seu projeto .NET
Bea Tavernaro
Bea Tavernaro

Posted on

O que o .csproj conta sobre o seu projeto .NET

No primeiro dia dos 30 dias de .NET eu fui entender o que acontece por trás de um simples dotnet run.

Mas antes do dotnet run existe o projeto em si.

Quando criamos uma aplicação .NET, mesmo a mais simples possível, alguns arquivos e pastas já aparecem automaticamente. Program.cs, .csproj, obj, depois bin e a gente simplesmente segue programando sem se preocupar em entender.

Eu mesma já abri um .csproj várias vezes para instalar um pacote, mudar alguma configuração ou conferir a versão do .NET e depois fechei sem entender direito o que tinha feito.

Então hoje resolvi abrir essa caixinha e entender para que serve cada uma dessas coisas.

Criando o projeto mais simples possível

Vamos começar criando uma aplicação Console:

Resultado do comando no terminal

O dotnet new cria projetos e outros arquivos a partir de templates. Nesse caso estamos usando o template console e dando ao projeto o nome Dia02.

Entrando na pasta temos uma estrutura parecida com essa:

Resultado do comando no terminal

Pouca coisa pra começar, mas já temos bastante coisa para entender aqui.

Program.cs — onde tudo começa

Abrindo o Program.cs de uma aplicação Console moderna encontramos algo parecido com:

Console.WriteLine("Hello, World!");
Enter fullscreen mode Exit fullscreen mode

O Program.cs é o arquivo onde encontramos o ponto de entrada da aplicação. É onde a execução do nosso programa começa e, em aplicações maiores, normalmente é onde configuramos e inicializamos os componentes necessários antes da aplicação começar a trabalhar de fato.

Se você já viu projetos C# mais antigos, talvez esteja sentindo falta de algumas coisas no Program.cs

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Hello, World!");
    }
}
Enter fullscreen mode Exit fullscreen mode

Desde o C# 9 temos os Top-level statements, que permitem escrever o código do ponto de entrada da aplicação sem declarar explicitamente uma classe Program e um método Main, como no segundo exemplo. O compilador gera o ponto de entrada necessário para nós, deixando o Program.cs bem menos verboso e mais bonito.

.csproj — o arquivo que descreve nosso projeto

Agora chegamos no arquivo mais importante do artigo.

Se abrirmos o Dia02.csproj, vamos encontrar algo parecido com:

Arquivo csproj

É no .csproj que ficam várias informações que dizem ao sistema de build como aquele projeto deve ser tratado: qual SDK utilizar, qual framework ele tem como alvo, referências a pacotes, referências a outros projetos e diversas configurações de compilação. Aah, e é um arquivo XML.

Olhando para o nosso exemplo:

<Project Sdk="Microsoft.NET.Sdk"> : define o Project SDK utilizado pelo projeto. Nesse caso estamos usando o SDK base do .NET. Em uma aplicação ASP.NET Core, por exemplo, é comum encontrarmos Microsoft.NET.Sdk.Web.

<PropertyGroup> : agrupa propriedades e configurações do projeto. Ele não define uma configuração específica sozinho, mas funciona como um "grupo" para as propriedades que estão dentro dele.

<OutputType>Exe</OutputType> : define o tipo de saída que será gerada pelo projeto. Exe indica que estamos construindo uma aplicação executável.

<TargetFramework>net10.0</TargetFramework> : define para qual Target Framework o projeto será construído. No exemplo, nosso alvo é o .NET 10. Isso não quer dizer "qual SDK tenho instalado", mas sim qual framework essa aplicação tem como alvo.

<ImplicitUsings>enable</ImplicitUsings> : faz com que o SDK adicione automaticamente alguns using comuns ao projeto. É por isso que conseguimos usar coisas como Console.WriteLine() sem necessariamente escrever using System; no começo do arquivo.

<Nullable>enable</Nullable> : habilita o contexto de Nullable Reference Types, permitindo que o compilador nos avise sobre situações em que uma referência pode ser null. Por exemplo, ele passa a diferenciar string nome de string? nome.

E quando instalamos um pacote NuGet?

Agora vamos mexer um pouquinho no nosso projeto.

Podemos adicionar um pacote usando a CLI:

dotnet package add Dapper
Enter fullscreen mode Exit fullscreen mode

Depois disso, se abrirmos novamente nosso .csproj, alguma coisa nova apareceu:

Dapper adicionado ao projeto

Vamos entender o que apareceu de novo:

<ItemGroup> : enquanto o PropertyGroup agrupa propriedades e configurações, o ItemGroup agrupa itens que fazem parte ou são utilizados pelo projeto, como pacotes, referências a outros projetos e arquivos.

<PackageReference Include="Dapper" Version="..." /> : declara uma dependência de um pacote NuGet. O Include="Dapper" informa qual pacote queremos utilizar e o Version="..." informa qual versão desse pacote o projeto utiliza.

Ao instalar um pacote NuGet, a dependência fica registrada no próprio .csproj, junto com a versão que o projeto utiliza. É também por isso que não precisamos enviar os pacotes junto com nosso código para o Git. Quando outra pessoa clona o projeto, o .NET consegue ler os PackageReference e restaurar as dependências necessárias.

E de onde surgiram bin e obj?

Quando executamos o dotnet build

resultado do dotnet build

o .NET inicia o processo de build do nosso projeto e, no meio disso, duas pastas que provavelmente todo desenvolvedor .NET já encontrou são atualizadas ou aparecem: bin e obj.

bin e obj

Apesar de as duas estarem relacionadas ao processo de build, elas têm funções diferentes.

A pasta bin é onde ficam os principais arquivos resultantes da compilação da nossa aplicação. Dentro dela, o .NET organiza esses arquivos de acordo com a configuração utilizada no build e o Target Framework do projeto.

Já a pasta obj funciona como uma área de trabalho do processo de build. Enquanto o resultado que nos interessa vai para bin, durante a compilação o .NET e o MSBuild precisam gerar e armazenar diversos arquivos intermediários. É na obj que essas informações ficam.

Normalmente, nenhuma das duas é versionada no Git. Se apagarmos bin e obj e executarmos novamente o dotnet build, o .NET gera de novo o que for necessário.

Concluindo

Na correria das entregas é muito fácil simplesmente criar o projeto, instalar os pacotes, rodar um dotnet build e seguir a vida sem pensar muito no que está acontecendo por trás de tudo isso. E, na maior parte do tempo, vai funcionar mesmo. Mas entender como o projeto está estruturado e de onde o .NET tira as informações que precisa para construir nossa aplicação faz bastante diferença quando alguma coisa deixa de funcionar como esperado quase o tempo todo. Quanto mais entendemos as ferramentas que usamos todos os dias, mais fácil fica investigar um erro e, por consequência, mais fácil e rápido fica entregar a task.

Até amanhã!

Referências

Top comments (0)