C# O Arquivo .csproj

Todo projeto C# tem um arquivo com o nome do projeto e a extensão .csproj (de C# project, "projeto C#"). Ele é pequeno, quase sempre umas dez linhas, mas manda em tudo: qual versão do .NET usar, se o programa é um executável ou uma biblioteca, se o compilador avisa sobre null, que nome o programa vai ter, que arquivos vão junto com ele, que pacotes ele usa. Nesta aula você vai ler o .csproj linha por linha e aprender a mudar as configurações mais comuns.

Você vai aprender
  • As linhas do .csproj que o dotnet new console cria: Project Sdk, PropertyGroup, OutputType, TargetFramework, ImplicitUsings e Nullable.
  • O que são os usings implícitos e o arquivo GlobalUsings.g.cs; como acrescentar um com <Using Include="…" />.
  • A mudar o nome e a versão do programa (AssemblyName, Version) e a copiar um arquivo de dados junto com ele (ItemGroup).
  • A tratar avisos como erros (TreatWarningsAsErrors) e a calar um aviso (NoWarn).
  • LangVersion, as diretivas #:property dos arquivos soltos .NET 10 e o dotnet project convert.
  • Os erros MSB4025, NETSDK1045 e CS8805, e a propriedade escrita errado que ninguém avisa.

Todas as saídas desta aula são reais, feitas com o .NET 10 SDK (10.0.112) na máquina fictícia da Ana, no projeto ~/projetos/Lojinha, criado com dotnet new console -o Lojinha (veja Projetos e O Comando dotnet).


A ideia: a ficha técnica do projeto

Numa receita de bolo, antes do modo de fazer, vem a ficha: "rende 12 fatias, forno a 180 °C, tempo 40 minutos, contém glúten". A ficha não é o bolo, mas diz como ele deve ser feito. O .csproj é a ficha técnica do projeto. Os arquivos .cs são o "modo de fazer" (o código); o .csproj diz ao dotnet build como transformar esse código num programa.

Ele é escrito em XML, um formato de texto com etiquetas (em inglês, tags) entre < >, como o HTML das páginas da internet. Cada informação fica entre uma etiqueta de abertura e uma de fechamento, com o mesmo nome:

<Nome>valor</Nome>
«abre com <Nome>, fecha com </Nome> (com a barra)»

1. O .csproj de um projeto novo

$ cat Lojinha.csproj
<Project Sdk="Microsoft.NET.Sdk">

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

</Project>
<Project Sdk="Microsoft.NET.Sdk">
"Projeto": a etiqueta que envolve o arquivo inteiro (ela fecha na última linha, </Project>). O Sdk="Microsoft.NET.Sdk" é um atributo: diz que este é um projeto .NET comum e traz centenas de regras prontas (onde ficam os .cs, como chamar o compilador, onde pôr o resultado). É por isso que o arquivo é tão curto: o resto vem do SDK. Sites usam Microsoft.NET.Sdk.Web.
<PropertyGroup>
"Grupo de propriedades": uma caixa com as configurações do projeto. Cada linha dentro dela é uma propriedade: um nome e um valor.
<OutputType>Exe</OutputType>
"Tipo de saída": Exe (de executable) é um programa que dá para rodar. A outra opção comum é Library, uma biblioteca, que só serve para outros projetos usarem (veja Bibliotecas de Classes).
<TargetFramework>net10.0</TargetFramework>
"Plataforma alvo": para qual versão do .NET o programa é feito. net10.0 é o .NET 10; net8.0, o .NET 8. Ela decide também a versão do C# (o .NET 10 usa o C# 14) e quais classes você pode usar.
<ImplicitUsings>enable</ImplicitUsings>
"Usings implícitos: ligado". É por causa desta linha que você escreve Console, List<T> e File sem nenhum using no começo do arquivo (seção 2).
<Nullable>enable</Nullable>
"Anuláveis: ligado". Liga os avisos sobre null: o compilador passa a separar string (nunca nulo) de string? (pode ser nulo) (seção 3 e null e Tipos Anuláveis).

Repare no que não está ali: a lista dos arquivos .cs. O SDK compila automaticamente todos os .cs da pasta do projeto e das subpastas (menos bin e obj). Criou um Produto.cs? Ele já faz parte do projeto, sem mexer no .csproj.

Maiúsculas importam. No XML, <OutputType> e <outputtype> são etiquetas diferentes. Copie os nomes exatamente como aparecem nesta aula.

2. Os usings implícitos

Quando ImplicitUsings está ligado, o dotnet build escreve sozinho um arquivo de código na pasta obj e o compila junto com o seu:

$ cat obj/Debug/net10.0/Lojinha.GlobalUsings.g.cs
// <auto-generated/>
global using System;
global using System.Collections.Generic;
global using System.IO;
global using System.Linq;
global using System.Net.Http;
global using System.Threading;
global using System.Threading.Tasks;
// <auto-generated/>
"Gerado automaticamente": não edite; ele é reescrito a cada build. O .g. no nome também quer dizer generated.
global using System;
Um using com global na frente vale para todos os arquivos do projeto, não só para este (veja Namespaces e using). System traz Console; System.Collections.Generic, List e Dictionary; System.IO, File; System.Linq, o LINQ; System.Threading.Tasks, Task.

Para ver a diferença, a Ana desligou (<ImplicitUsings>disable</ImplicitUsings>) e compilou um programa de duas linhas:

var itens = new List<string> { "caderno", "lápis" };
Console.WriteLine(itens.Count);
$ dotnet build 2>&1 | grep -E "warning|error|Aviso|Erro"
/home/ana/projetos/Lojinha/Program.cs(1,17): error CS0246: O nome do tipo ou do namespace "List<>" não pode ser encontrado (está faltando uma diretiva using ou uma referência de assembly?) [/home/ana/projetos/Lojinha/Lojinha.csproj]
/home/ana/projetos/Lojinha/Program.cs(2,1): error CS0103: O nome "Console" não existe no contexto atual [/home/ana/projetos/Lojinha/Lojinha.csproj]
«… as mesmas duas linhas, repetidas no resumo …»
    0 Aviso(s)
    2 Erro(s)

Sem os usings implícitos, nem o Console existe: seria preciso escrever using System; e using System.Collections.Generic; no começo. É assim que eram os programas até o .NET 5, e é por isso que código antigo começa com uma fileira de using. (No fim de cada mensagem, entre colchetes, vem o projeto em que o erro aconteceu. O dotnet build mostra cada erro duas vezes: quando acontece e no resumo do fim.)

Acrescentar um using para o projeto inteiro

A Ana usa StringBuilder (de System.Text, veja StringBuilder) em vários arquivos. Ela não está na lista acima:

var sb = new StringBuilder();
sb.Append("caderno, ");
sb.Append("lápis");
Console.WriteLine(sb.ToString());
$ dotnet build 2>&1 | grep -E " error " | head -1
/home/ana/projetos/Lojinha/Program.cs(1,14): error CS0246: O nome do tipo ou do namespace "StringBuilder" não pode ser encontrado (está faltando uma diretiva using ou uma referência de assembly?) [/home/ana/projetos/Lojinha/Lojinha.csproj]

Em vez de pôr using System.Text; em cada arquivo, ela acrescenta um item Using no .csproj:

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

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

  <ItemGroup>
    <Using Include="System.Text" />
  </ItemGroup>

</Project>
$ dotnet run
caderno, lápis
$ cat obj/Debug/net10.0/Lojinha.GlobalUsings.g.cs
// <auto-generated/>
global using System;
global using System.Collections.Generic;
global using System.IO;
global using System.Linq;
global using System.Net.Http;
global using System.Text;
global using System.Threading;
global using System.Threading.Tasks;
<ItemGroup>
"Grupo de itens": a segunda caixa do .csproj. O PropertyGroup guarda configurações (um valor cada); o ItemGroup guarda listas de coisas: arquivos, pacotes, outros projetos, usings.
<Using Include="System.Text" />
"Inclua o using System.Text". A barra antes do > fecha a etiqueta ali mesmo: é uma etiqueta sem conteúdo, só com atributo. O global using System.Text; apareceu no arquivo gerado.

Fazer isso num arquivo de código também vale: um arquivo Usings.cs com global using System.Text; tem o mesmo efeito. Experimente:

Missão: as duas abas são compiladas juntas, como num projeto. O Program.cs usa StringBuilder, que não está nos usings implícitos, e o build falha com CS0246. Sem mexer no Program.cs, escreva na aba Usings.cs a linha global using System.Text; e rode.

var sb = new StringBuilder();
sb.Append("caderno, ");
sb.Append("lápis");
Console.WriteLine(sb.ToString());
// os usings do projeto inteiro
// os usings do projeto inteiro
global using System.Text;

3. Nullable: os avisos sobre null

Com <Nullable>enable</Nullable>, o compilador avisa quando você pode estar usando algo que vale null:

string? apelido = null;
Console.WriteLine($"Tamanho: {apelido.Length}");
$ dotnet build 2>&1 | grep -E "warning|error|Aviso|Erro"
/home/ana/projetos/Lojinha/Program.cs(2,31): warning CS8602: Desreferência de uma referência possivelmente nula. [/home/ana/projetos/Lojinha/Lojinha.csproj]
«… a mesma linha, repetida no resumo …»
    1 Aviso(s)
    0 Erro(s)

O aviso CS8602 ("uso de uma referência possivelmente nula") salvou a Ana: esse programa daria NullReferenceException. Com disable, o aviso some, e aparece outro, dizendo que o ? de string? não tem efeito:

$ dotnet build 2>&1 | grep -E "warning|error|Aviso|Erro"
/home/ana/projetos/Lojinha/Program.cs(1,7): warning CS8632: A anotação para tipos de referência anuláveis deve ser usada apenas em código em um contexto de anotações '#nullable'. [/home/ana/projetos/Lojinha/Lojinha.csproj]
«…»
    1 Aviso(s)
    0 Erro(s)

Deixe sempre enable, como o modelo cria. Projetos antigos (de antes do .NET 6) não têm essa linha, e por isso não mostram esses avisos.


4. Nome, versão e arquivos de dados

A Ana quer que o executável se chame loja (sem maiúscula, como os comandos do Linux), que o programa saiba a própria versão e que a tabela de preços precos.txt vá junto com ele. O .csproj ficou assim:

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

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <AssemblyName>loja</AssemblyName>
    <Version>1.2.0</Version>
  </PropertyGroup>

  <ItemGroup>
    <None Update="precos.txt" CopyToOutputDirectory="PreserveNewest" />
  </ItemGroup>

</Project>
caderno;12,50
lápis;1,99
using System.Reflection;

string versao = Assembly.GetExecutingAssembly().GetName().Version!.ToString();
Console.WriteLine($"Lojinha da Ana, versão {versao}");

foreach (string linha in File.ReadAllLines("precos.txt"))
{
    string[] partes = linha.Split(';');
    Console.WriteLine($"{partes[0],-10} R$ {partes[1]}");
}
$ dotnet build
  Determinando os projetos a serem restaurados...
  /home/ana/projetos/Lojinha/Lojinha.csproj restaurado (em 161 ms).
  Lojinha -> /home/ana/projetos/Lojinha/bin/Debug/net10.0/loja.dll

Compilação com êxito.
    0 Aviso(s)
    0 Erro(s)

Tempo Decorrido 00:00:04.91
$ ls bin/Debug/net10.0/
loja
loja.deps.json
loja.dll
loja.pdb
loja.runtimeconfig.json
precos.txt
$ cd bin/Debug/net10.0 && ./loja && cd ../../..
Lojinha da Ana, versão 1.2.0.0
caderno    R$ 12,50
lápis      R$ 1,99
<AssemblyName>loja</AssemblyName>
"Nome do assembly": o nome dos arquivos gerados (loja, loja.dll…). Sem ela, o nome é o do .csproj. Um assembly é o nome que o .NET dá a um .dll ou executável compilado. Repare que a linha Lojinha -> …/loja.dll mostra os dois: o projeto continua se chamando Lojinha.
<Version>1.2.0</Version>
A versão do programa, gravada dentro do loja.dll. O Assembly.GetExecutingAssembly().GetName().Version a lê de volta (veja Reflexão), com um quarto número, .0. O costume é MAIOR.MENOR.CORREÇÃO.
<None Update="precos.txt" … />
Para o SDK, um .txt é um item do tipo None ("nenhum": não é para compilar). Update ("atualize") muda as regras de um item que o SDK já conhece: aqui, a regra de cópia.
CopyToOutputDirectory="PreserveNewest"
"Copie para a pasta de saída se for mais novo": o precos.txt aparece em bin/Debug/net10.0/, ao lado do executável. A outra opção é Always ("sempre").
O programa procura o arquivo na pasta em que ele roda. O File.ReadAllLines("precos.txt") procura na pasta atual do terminal. Por isso a Ana entrou em bin/Debug/net10.0 antes de rodar. Com dotnet run, a pasta atual é a do projeto, onde o precos.txt original está, e também funciona. Para não depender disso, use Path.Combine(AppContext.BaseDirectory, "precos.txt"), que é sempre a pasta do executável (veja Pastas e Caminhos).

5. Avisos: mais rígido ou mais calmo

TreatWarningsAsErrors: aviso vira erro

Num projeto sério, aviso ignorado vira defeito. Com <TreatWarningsAsErrors>true</TreatWarningsAsErrors> ("trate avisos como erros") no PropertyGroup, o build falha em qualquer aviso (veja Avisos do Compilador):

int naoUsada;
Console.WriteLine("oi");
$ dotnet build 2>&1 | grep -E "warning|error|Aviso|Erro" | head -3
/home/ana/projetos/Lojinha/Program.cs(1,5): error CS0168: A variável "naoUsada" está declarada, mas nunca é usada [/home/ana/projetos/Lojinha/Lojinha.csproj]
/home/ana/projetos/Lojinha/Program.cs(1,5): error CS0168: A variável "naoUsada" está declarada, mas nunca é usada [/home/ana/projetos/Lojinha/Lojinha.csproj]
    0 Aviso(s)

O CS0168 é normalmente um warning; aqui saiu como error.

NoWarn: calar um aviso específico

Para desligar um aviso no projeto inteiro, use NoWarn com o código dele:

    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
    <NoWarn>$(NoWarn);CS0168</NoWarn>
$ dotnet build 2>&1 | grep -E "warning|error|Aviso|Erro" | head -3
    0 Aviso(s)
    0 Erro(s)
$(NoWarn)
O $( ) lê o valor atual de uma propriedade. O SDK já põe alguns códigos no NoWarn; escrevendo $(NoWarn);CS0168, você acrescenta o seu à lista em vez de apagar os dele.
;
Separa os itens de uma lista: $(NoWarn);CS0168;CS8602.

Use o NoWarn com moderação: calar um aviso não conserta o problema. Uma variável não usada se apaga.


6. Versão do C# e do .NET

LangVersion: travar a versão do C#

O TargetFramework já escolhe o C# certo (net10.0 → C# 14), e quase nunca você precisa mudar. Mas a propriedade LangVersion existe, e é útil para ver o que uma versão antiga não aceita. A Ana usou a palavra-chave field C# 14 numa propriedade (veja Propriedades):

var p = new Produto { Nome = "  caderno  " };
Console.WriteLine($"[{p.Nome}]");

class Produto
{
    public string Nome
    {
        get;
        set => field = value.Trim();
    } = "";
}
$ dotnet run
[caderno]

Com <LangVersion>13</LangVersion> no PropertyGroup:

$ dotnet build 2>&1 | grep -E "error" | head -2
/home/ana/projetos/Lojinha/Program.cs(6,19): error CS9260: O recurso "campo palavra-chave" não está disponível no C# 13.0. Use a versão da linguagem 14.0 ou superior. [/home/ana/projetos/Lojinha/Lojinha.csproj]
«…»

A mensagem diz o recurso, a versão atual e a versão necessária (a tradução "campo palavra-chave" é do original field keyword, "a palavra-chave field"). Valores comuns: um número (12, 13, 14), latest (a mais nova que o compilador conhece) e preview (recursos ainda em teste).

TargetFramework: outra versão do .NET

Trocar para net8.0 faz o projeto ser compilado para o .NET 8 (o dotnet build baixa da internet as "referências" do .NET 8, e o C# vira o 12). Compila, mas, na máquina da Ana, que só tem o runtime do .NET 10, não roda:

$ dotnet run; echo "código de saída: $?"
/home/ana/projetos/Lojinha/Program.cs(2,31): warning CS8602: Desreferência de uma referência possivelmente nula. [/home/ana/projetos/Lojinha/Lojinha.csproj]
You must install or update .NET to run this application.

App: /home/ana/projetos/Lojinha/bin/Debug/net8.0/loja
Architecture: x64
Framework: 'Microsoft.NETCore.App', version '8.0.0' (x64)
.NET location: /usr/lib/dotnet

The following frameworks were found:
  10.0.12 at [/usr/lib/dotnet/shared/Microsoft.NETCore.App]

Learn more:
https://aka.ms/dotnet/app-launch-failed

To install missing framework, download:
https://aka.ms/dotnet-core-applaunch?framework=Microsoft.NETCore.App&framework_version=8.0.0&arch=x64&rid=ubuntu.26.04-x64&os=ubuntu.26.04
código de saída: 150

"Você precisa instalar ou atualizar o .NET para rodar este aplicativo": o programa pede o runtime 8.0.0, e só o 10.0.12 foi encontrado. Um programa feito para o .NET 8 não roda sozinho num .NET 10 (veja Versões do C# e do .NET). Para um programa novo, deixe net10.0.


7. Arquivos soltos: #:property .NET 10

Um arquivo solto rodado com dotnet run arquivo.cs não tem .csproj... mas tem um escondido: o dotnet cria um projeto "virtual" para ele. Para mudar uma propriedade desse projeto, escreva uma diretiva #:property Nome=Valor no começo do arquivo:

#:property LangVersion=13
#:property Nullable=disable

var p = new Produto { Nome = "  caderno  " };
Console.WriteLine($"[{p.Nome}]");

class Produto
{
    public string Nome
    {
        get;
        set => field = value.Trim();
    } = "";
}
$ dotnet run preco.cs
/home/ana/cs/csproj/preco.cs(9,19): error CS9260: O recurso "campo palavra-chave" não está disponível no C# 13.0. Use a versão da linguagem 14.0 ou superior.

Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
$ sed -i '1d' preco.cs
$ dotnet run preco.cs
[caderno]

O #:property LangVersion=13 fez o mesmo que o <LangVersion>13</LangVersion>. O sed -i '1d' apagou a primeira linha, e o programa voltou a compilar. (A outra diretiva, #:package, que traz pacotes, está em Pacotes NuGet.)

Do arquivo solto ao projeto: dotnet project convert

Quando o arquivo cresce e merece virar projeto, o dotnet faz a conversão e transforma as diretivas em propriedades:

$ dotnet project convert preco.cs
$ ls
preco
preco.cs
$ ls preco
preco.cs
preco.csproj
$ cat preco/preco.csproj
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <PublishAot>true</PublishAot>
    <PackAsTool>true</PackAsTool>
    <UserSecretsId>preco-7a1225a797ad0e935c00e2d01a8479b119ec6ea9614670c215fe58bd7891b396</UserSecretsId>
    <Nullable>disable</Nullable>
  </PropertyGroup>

</Project>

A pasta preco nova tem uma cópia do código e um .csproj com as configurações que um arquivo solto usa sem você ver. Além das quatro conhecidas, aparecem três que um dotnet new console não põe: PublishAot (publicar em código nativo, veja Publicar o Programa), PackAsTool (poder virar uma ferramenta do dotnet tool) e UserSecretsId (um nome único para guardar senhas de desenvolvimento fora do código). O Nullable é o disable que veio da diretiva. Pode apagar as três linhas se não for usá-las.


Referência: as propriedades mais usadas

No PropertyGroupValoresO que faz
OutputTypeExe, LibraryPrograma ou biblioteca.
TargetFrameworknet10.0, net8.0…A versão do .NET (e, com ela, a do C#).
ImplicitUsingsenable, disableOs global using automáticos.
Nullableenable, disableOs avisos sobre null.
AssemblyNameum nomeO nome do .dll e do executável.
RootNamespaceum nomeO namespace que o editor usa nos arquivos novos.
Version1.2.0A versão do programa.
TreatWarningsAsErrorstrue, falseTodo aviso vira erro.
NoWarn$(NoWarn);CS0168Desliga avisos pelo código.
LangVersion13, 14, latest, previewTrava a versão do C#.
No ItemGroupO que faz
<Using Include="System.Text" />Um global using para o projeto todo.
<None Update="dados.txt" CopyToOutputDirectory="PreserveNewest" />Copia o arquivo para a pasta do executável.
<PackageReference Include="Nome" Version="1.0.0" />Usa um pacote do NuGet (veja Pacotes NuGet).
<ProjectReference Include="../Outro/Outro.csproj" />Usa outro projeto (veja Bibliotecas de Classes).
<Compile Remove="Rascunho.cs" />Tira um .cs da compilação.

Quando dá errado

error MSB4025: Não foi possível carregar o arquivo de projeto.
$ dotnet build
/home/ana/projetos/Lojinha/Lojinha.csproj(4,22): error MSB4025: Não foi possível carregar o arquivo de projeto. The 'OutputType' start tag on line 4 position 6 does not match the end tag of 'OutputTipe'. Line 4, position 22.

FALHA da compilação.
«… a mesma linha, repetida …»
    0 Aviso(s)
    1 Erro(s)

O que aconteceu: o XML está quebrado. A linha 4 abre com <OutputType> e fecha com </OutputTipe>: "a etiqueta de abertura OutputType não combina com a de fechamento OutputTipe". O mesmo erro aparece se faltar um </…>, um > ou uma aspa.

Como resolver: vá à linha e à coluna indicadas ((4,22)) e deixe a abertura e o fechamento com o mesmo nome. Um editor com destaque de XML ajuda a achar a etiqueta errada.

error NETSDK1045: o SDK .NET atual não oferece suporte ao direcionamento .NET 11.0.
$ dotnet build
  Determinando os projetos a serem restaurados...
/usr/lib/dotnet/sdk/10.0.112/Sdks/Microsoft.NET.Sdk/targets/Microsoft.NET.TargetFrameworkInference.targets(184,5): error NETSDK1045: o SDK .NET atual não oferece suporte ao direcionamento .NET 11.0.  Destina-se a .NET 10.0 ou inferior ou usa uma versão do .NET SDK compatível com .NET 11.0. Baixe o SDK .NET em https://aka.ms/dotnet/download [/home/ana/projetos/Lojinha/Lojinha.csproj]

FALHA da compilação.
«…»

O que aconteceu: o projeto pede net11.0, mas o SDK instalado é o do .NET 10, que não sabe compilar para uma versão mais nova que ele. Acontece ao baixar um projeto feito por alguém com um SDK mais novo. O começo da mensagem é o arquivo de regras do SDK onde o erro foi detectado: não é para você mexer nele.

Como resolver: troque para <TargetFramework>net10.0</TargetFramework> (se o código não usa nada do .NET 11) ou instale o SDK mais novo.

error CS8805: O programa que usa as instruções de nível superior precisa ser um executável.
$ dotnet run 2>&1 | tail -4
/home/ana/projetos/Lojinha/Program.cs(1,1): error CS8805: O programa que usa as instruções de nível superior precisa ser um executável. [/home/ana/projetos/Lojinha/Lojinha.csproj]

Ocorreu uma falha no build. Corrija os erros de build e execute novamente.

O que aconteceu: o .csproj tem <OutputType>Library</OutputType>, mas o Program.cs tem código solto, fora de classes (as instruções de nível superior, veja Program.cs e o Main). Uma biblioteca não tem por onde começar a rodar.

Como resolver: para um programa, <OutputType>Exe</OutputType>. Para uma biblioteca, tire o código solto (só classes).

Escrevi a propriedade e nada mudou

A Ana escreveu <Nulable>enable</Nulable> (com um l só) e compilou o programa do apelido.Length:

$ dotnet build 2>&1 | grep -E "warning|Aviso"
/home/ana/projetos/Lojinha/Program.cs(1,7): warning CS8632: A anotação para tipos de referência anuláveis deve ser usada apenas em código em um contexto de anotações '#nullable'. [/home/ana/projetos/Lojinha/Lojinha.csproj]
«…»
    1 Aviso(s)

O que aconteceu: é o aviso de quando o Nullable está desligado. No PropertyGroup, qualquer nome é aceito: Nulable virou uma propriedade nova, que ninguém lê. O SDK não avisa nada, e o Nullable de verdade ficou com o valor padrão (desligado).

Como resolver: confira a grafia letra por letra, com as maiúsculas. Na dúvida, copie o nome da tabela de referência acima ou da documentação.


Exercícios

1. Programa ou biblioteca?

Complete para o projeto gerar um programa que dá para rodar:

<OutputType></OutputType>

2. Para o .NET 10

Complete o valor da plataforma alvo do .NET 10:

<TargetFramework></TargetFramework>

3. Aviso vira erro

Complete a propriedade que faz o build falhar em qualquer aviso:

<>true</TreatWarningsAsErrors>

4. Por que um projeto novo usa Console e List<T> sem nenhum using?

5. A Ana criou o arquivo Cliente.cs na pasta do projeto. O que precisa mudar no .csproj?

6. O dados.csv não aparece em bin/Debug/net10.0. Qual linha resolve?


Resumo

  • O .csproj é a ficha técnica do projeto, em XML: <Project Sdk="Microsoft.NET.Sdk"> com PropertyGroup (configurações) e ItemGroup (listas).
  • As quatro linhas do modelo: OutputType (Exe), TargetFramework (net10.0), ImplicitUsings e Nullable (enable).
  • Todos os .cs da pasta entram sozinhos. Os usings implícitos ficam em obj/…/NOME.GlobalUsings.g.cs; acrescente outros com <Using Include="…" />.
  • AssemblyName muda o nome do executável; Version, a versão; <None Update="…" CopyToOutputDirectory="PreserveNewest" /> copia arquivos de dados.
  • TreatWarningsAsErrors deixa o projeto rígido; NoWarn cala um aviso; LangVersion trava o C#.
  • Arquivo solto: #:property Nome=Valor; para virar projeto, dotnet project convert.
  • Propriedade com nome errado não dá erro nenhum: só não funciona.