C# .NET: SDK, Runtime e CLR

O C# é a linguagem; o .NET é a plataforma que transforma o seu código em programa e o faz rodar. Quem procura "instalar C#" acaba instalando ".NET SDK", e nos tutoriais aparecem siglas como CLR, IL e JIT. Nesta aula você abre o .NET instalado no computador da Ana, vê o que tem dentro do arquivo que o compilador cria e assiste o .NET transformando o seu código em instruções do processador.

Você vai aprender
  • A diferença entre o SDK (para quem programa) e o Runtime (para quem só roda programas).
  • O que fazem o Roslyn (o compilador), a IL (a linguagem intermediária), o CLR (o motor) e o JIT (o tradutor de última hora).
  • O que é a biblioteca de classes e de onde vem o Console.
  • Como ver o que está instalado com dotnet --list-sdks e dotnet --list-runtimes.
  • O que são os arquivos .dll, .pdb e .runtimeconfig.json que o build cria.
  • A diferença entre .NET, .NET Core, .NET Framework e Mono.

Todas as saídas desta aula vêm da máquina da Ana: Ubuntu 26.04 com o pacote dotnet-sdk-10.0 do próprio Ubuntu. Se você ainda não instalou o .NET, pode ler esta aula do mesmo jeito; a instalação vem logo depois, em Instalar no Linux.


A ideia: a receita em língua universal

Imagine uma rede de restaurantes com cozinhas no Brasil, no Japão e na Alemanha. A chef escreve as receitas em português, mas os cozinheiros de cada país falam línguas diferentes. Em vez de escrever três versões, a rede faz assim:

  1. Um tradutor oficial passa a receita do português para uma língua universal da rede, simples e exata, que todo cozinheiro da rede aprendeu.
  2. Em cada país, o cozinheiro lê a receita universal e, na hora de cozinhar, vai traduzindo cada passo para o jeito da cozinha dele, com os fogões e panelas que ele tem.
  3. Os ingredientes básicos (farinha, sal, ovos) já estão na despensa de toda cozinha da rede: a receita só diz "use farinha", sem explicar como fazer farinha.

O .NET funciona exatamente assim:

No restauranteNo .NETNome técnico
A receita em portuguêsO seu código C#código-fonte (ola.cs)
O tradutor oficialO compilador de C#Roslyn
A receita na língua universalO programa traduzido, guardado num arquivo .dllIL (Intermediate Language, linguagem intermediária)
O cozinheiro de cada paísO motor que roda o programa naquele computadorCLR (Common Language Runtime)
Traduzir cada passo na hora de cozinharTransformar a IL em instruções do processador enquanto o programa rodaJIT (just-in-time, "bem na hora")
A despensa com os ingredientes básicosMilhares de classes prontas: Console, Math, List, File…biblioteca de classes (BCL)
Limpar a mesa quando o cliente vai emboraRecolher a memória que o programa não usa maiscoletor de lixo (GC, garbage collector)

A grande vantagem: o mesmo arquivo .dll roda no Linux, no Windows e no macOS, em processadores Intel, AMD ou ARM. Quem se adapta a cada computador é o CLR, não o seu programa.


SDK e Runtime: as duas caixas do .NET

O .NET vem em dois tamanhos, para dois tipos de pessoa:

Runtime ("tempo de execução")SDK (Software Development Kit, "kit de desenvolvimento")
Para quemQuem só quer rodar programas prontosQuem escreve programas: você
Tem dentroO CLR, o JIT, o coletor de lixo e a biblioteca de classesTudo do Runtime, mais o compilador (Roslyn), o MSBuild (que organiza o build), os modelos de projeto e o comando dotnet run, dotnet new, dotnet build…
Pacote no Ubuntudotnet-runtime-10.0dotnet-sdk-10.0
Número da versão10.0.1210.0.112

Para seguir o guia, instale o SDK: ele já traz o Runtime junto. O comando dotnet mostra as duas coisas separadas:

$ dotnet --list-sdks                            « os SDKs instalados
10.0.112 [/usr/lib/dotnet/sdk]
$ dotnet --list-runtimes                        « os Runtimes instalados
Microsoft.AspNetCore.App 10.0.12 [/usr/lib/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 10.0.12 [/usr/lib/dotnet/shared/Microsoft.NETCore.App]
10.0.112
A versão do SDK. Os dois primeiros números (10.0) dizem a versão do .NET. O terceiro (112) tem duas partes: o 1 é a "faixa" de recursos do SDK e o 12 é a 12.ª correção. A aula Versões do C# e do .NET explica a numeração.
Microsoft.NETCore.App 10.0.12
O Runtime básico, versão 10.0.12: tudo o que um programa de terminal precisa. É o que este guia usa.
Microsoft.AspNetCore.App 10.0.12
Um runtime extra, para sites e serviços na internet (ASP.NET Core). O SDK do Ubuntu o instala junto.
[/usr/lib/dotnet/…]
A pasta onde cada um está. No Ubuntu, o .NET mora em /usr/lib/dotnet.

Abrindo o .NET da Ana

Vamos olhar a pasta do .NET por dentro. O ls lista o que há nela, e o du -sh mostra o tamanho:

$ ls /usr/lib/dotnet
dnx
dotnet                                          « o comando dotnet
host
library-packs
LICENSE.txt
metadata
packs
sdk                                             « o SDK: compilador, MSBuild, comandos
sdk-manifests
shared                                          « os Runtimes
templates                                       « os modelos do dotnet new
ThirdPartyNotices.txt
$ du -sh /usr/lib/dotnet
545M	/usr/lib/dotnet
$ du -sh /usr/lib/dotnet/sdk; du -sh /usr/lib/dotnet/shared; du -sh /usr/lib/dotnet/packs
340M	/usr/lib/dotnet/sdk
96M	/usr/lib/dotnet/shared
98M	/usr/lib/dotnet/packs

O .NET inteiro ocupa 545 MB, e a maior parte (340 MB) é o SDK: as ferramentas de quem programa. Os Runtimes, que é o que um programa precisa para rodar, ficam em shared, com menos de 100 MB. A pasta packs guarda as "plantas" da biblioteca que o compilador consulta para saber quais classes existem.

O compilador e o MSBuild, dentro do SDK

$ ls /usr/lib/dotnet/sdk/10.0.112 | grep -i -E "^(MSBuild.dll|dotnet.dll|Roslyn)$"
dotnet.dll                                      « os comandos run, new, build…
MSBuild.dll                                     « o "mestre de obras" do build
Roslyn                                          « a pasta do compilador de C#
$ ls /usr/lib/dotnet/sdk/10.0.112/Roslyn/bincore | head
cs
csc                                             « csc = C Sharp Compiler
csc.deps.json
csc.dll
csc.runtimeconfig.json
de
es
fr
it
ja

O compilador de C# se chama csc (C Sharp Compiler), e o projeto que o criou se chama Roslyn. Repare num detalhe curioso: o csc.dll também é um programa .NET, escrito em C#. O compilador de C# é escrito em C#. As pastas cs, de, es, fr… guardam as mensagens de erro traduzidas (cs, aqui, é tcheco). É de uma pasta dessas, a pt-BR, que vêm as mensagens em português que você vê no terminal da Ana. Você nunca precisa chamar o csc diretamente: o dotnet run e o MSBuild fazem isso por você.

O Runtime e a biblioteca de classes

$ cd /usr/lib/dotnet/shared/Microsoft.NETCore.App/10.0.12
$ ls *.dll | wc -l
172                                             « 172 bibliotecas prontas
$ du -h System.Console.dll System.Private.CoreLib.dll libcoreclr.so libclrjit.so
184K	System.Console.dll                      « onde mora o Console
14M	System.Private.CoreLib.dll              « o coração da biblioteca: int, string, Math…
6,9M	libcoreclr.so                           « o CLR, o motor
3,2M	libclrjit.so                            « o JIT, o tradutor de última hora

Aqui está a "despensa" do restaurante: 172 arquivos .dll, cada um com uma parte da biblioteca de classes. Quando você escreve Console.WriteLine, o código do WriteLine vem do System.Console.dll; ele não fica dentro do seu programa. Os arquivos .so são programas do Linux de verdade, em código de máquina: o libcoreclr.so é o CLR e o libclrjit.so é o JIT.

Um programa consegue perguntar ao .NET quem ele é. Rode e compare com o terminal da Ana:

Experimente: rode e veja a versão do .NET, o sistema e o processador. Aqui no editor aparecem Sistema: Browser e Processador: Wasm, diferentes da saída da Ana logo abaixo. Não é erro: o programa mostra o .NET e o computador onde ele está rodando, e aqui ele roda dentro do seu navegador (em WebAssembly), não no Ubuntu da Ana. O System.Console é o mesmo nos dois.

using System.Runtime.InteropServices;

Console.WriteLine($".NET: {RuntimeInformation.FrameworkDescription}");
Console.WriteLine($"Sistema: {RuntimeInformation.OSDescription}");
Console.WriteLine($"Processador: {RuntimeInformation.ProcessArchitecture}");
Console.WriteLine($"O Console vem de: {typeof(Console).Assembly.GetName().Name}");
$ dotnet run info.cs
.NET: .NET 10.0.12                              « o Runtime, não o SDK
Sistema: Ubuntu 26.04.1 LTS
Processador: X64                                « Intel/AMD de 64 bits
O Console vem de: System.Console                « o System.Console.dll da despensa

Não se preocupe com o using nem com o typeof: eles aparecem nas aulas Namespaces e using e Reflexão. O importante aqui é que o programa mostrou .NET 10.0.12, a versão do Runtime: quem roda o programa é o Runtime; o SDK só ajudou a construí-lo.


Do .cs ao programa: o caminho completo

Quando a Ana digita dotnet run ola.cs, acontecem estas etapas, nesta ordem:

  1. O SDK prepara o build. O MSBuild descobre o que precisa ser compilado e com quais bibliotecas.
  2. O Roslyn compila. Ele lê o ola.cs, confere tudo (tipos, nomes, ponto e vírgula) e, se não houver erro, escreve a IL num arquivo ola.dll. Se houver erro, para aqui, e nada roda.
  3. O CLR carrega o programa. Ele abre o ola.dll e as bibliotecas que ele usa (como o System.Console.dll).
  4. O JIT traduz. Na primeira vez que cada método é chamado, o JIT transforma a IL dele em instruções do processador (x64, no computador da Ana) e guarda o resultado na memória.
  5. O processador executa essas instruções, e o texto aparece na tela.

O dotnet run esconde os arquivos que cria. O comando dotnet build faz só as etapas 1 e 2, e diz onde guardou o resultado:

$ dotnet build ola.cs
  Determinando os projetos a serem restaurados...
  Todos os projetos estão atualizados para restauração.
  ola -> /home/ana/.local/share/dotnet/runfile/ola-d3e0b993ea942ae2aa43a2c132f9667a87cb5c1410a99c454aae33ed39b33823/bin/debug/ola.dll

O ola.dll foi parar numa pasta escondida, dentro de ~/.local/share/dotnet/runfile, com um nome comprido (o nome do arquivo mais um código que identifica a pasta onde ele está). Vamos entrar nela:

$ cd ~/.local/share/dotnet/runfile/ola-*/bin/debug
$ ls -l
total 108
-rwxr-xr-x 1 ana ana 78904 out 17 12:37 ola                     « um "lançador" para Linux
-rw-rw-r-- 1 ana ana   381 out 17 12:37 ola.deps.json           « a lista de bibliotecas usadas
-rw-rw-r-- 1 ana ana  4608 out 17 12:37 ola.dll                 « o seu programa, em IL
-rw-rw-r-- 1 ana ana 10872 out 17 12:37 ola.pdb                 « informações para depurar
-rw-rw-r-- 1 ana ana  2000 out 17 12:37 ola.runtimeconfig.json  « qual Runtime usar
ArquivoO que é
ola.dllO programa de verdade: a IL que o Roslyn gerou, mais a descrição de tudo o que há nele (os "metadados"). Só 4608 bytes, porque o Console e o resto da biblioteca ficam no Runtime. O nome .dll (dynamic-link library) vem do Windows e é usado em todos os sistemas.
olaO apphost, um lançador: um programa pequeno do Linux que acha o Runtime e manda ele rodar o ola.dll. No Windows, ele se chama ola.exe.
ola.runtimeconfig.jsonUm bilhete para o lançador: "este programa precisa do Microsoft.NETCore.App, versão 10.0 ou mais nova".
ola.deps.jsonA lista de bibliotecas extras de que o programa depende (aqui, nenhuma).
ola.pdbO "mapa" que liga cada pedaço da IL à linha do ola.cs. É por causa dele que uma exceção diz line 2 (aula Depuração).

O comando file do Linux diz que tipo de arquivo cada um é:

$ file ola ola.dll
ola:     ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=622d7093dbcd55b2ef4ff850254d90f54a4213c7, stripped
ola.dll: PE32 executable for MS Windows 4.00 (console), Intel i386 Mono/.Net assembly, 3 sections
$ head -c 2 ola.dll; echo
MZ                                              « a assinatura dos programas do Windows

O ola é um programa do Linux (ELF, para processador x86-64). Já o ola.dll o file chama de "executável do Windows", com a etiqueta Mono/.Net assembly: ele usa o mesmo formato de arquivo dos programas do Windows (que começam com as letras MZ, as iniciais de um engenheiro da Microsoft dos anos 1980), mas por dentro não tem instruções de processador nenhum, só IL. Por isso o mesmo ola.dll roda em qualquer sistema. Um arquivo assim, com IL e metadados, se chama assembly no mundo .NET.

Dá para rodar o programa pronto de dois jeitos, sem compilar de novo:

$ ./ola                                         « pelo lançador
Olá, mundo!
$ dotnet ola.dll                                « entregando o .dll direto ao dotnet
Olá, mundo!

Por dentro: a IL e o JIT

Vamos seguir um método pequeno do código-fonte até o processador. Este programa tem um método Somar, que soma dois números (os métodos têm aula própria, Métodos; por enquanto, leia Somar(2, 3) como "some 2 e 3"):

Console.WriteLine(Conta.Somar(2, 3));

static class Conta
{
    public static int Somar(int a, int b) => a + b;
}
$ dotnet run soma.cs
5

1. O que o Roslyn escreveu: a IL

Dentro do soma.dll, o método Somar virou só quatro bytes de IL: 02 03 58 2A (lidos com um programa de reflexão, assunto da aula Reflexão). Cada byte é uma instrução da "língua universal":

ByteInstrução de ILO que faz
02ldarg.0"Pegue o argumento 0" (o a) e ponha numa pilha de trabalho
03ldarg.1"Pegue o argumento 1" (o b) e ponha na pilha
58addTire os dois de cima da pilha, some e ponha o resultado de volta
2AretDevolva o que está na pilha

Repare que a IL não fala de nenhum processador: ela não sabe se vai rodar num Intel, num ARM do celular ou num Mac. É o "passo a passo universal" da receita.

2. O que o JIT fez: código de máquina

O .NET tem uma chave de diagnóstico, a variável DOTNET_JitDisasm, que pede ao JIT para mostrar o que ele gerou para um método. A Ana pediu o Somar (o -c Release pede a versão "de entrega", sem os extras de depuração, para a listagem ficar menor):

$ DOTNET_JitDisasm=Somar dotnet run soma.cs -c Release
; Assembly listing for method Conta:Somar(int,int):int (Tier0)
; Emitting BLENDED_CODE for generic X64 + VEX on Unix
; Tier0 code
; rbp based frame
; partially interruptible
; compiling with minopt

G_M000_IG01:                ;; offset=0x0000
       push     rbp
       sub      rsp, 16
       lea      rbp, [rsp+0x10]
       mov      dword ptr [rbp-0x04], edi       « guarda o a
       mov      dword ptr [rbp-0x08], esi       « guarda o b

G_M000_IG02:                ;; offset=0x0010
       mov      eax, dword ptr [rbp-0x04]       « pega o a
       add      eax, dword ptr [rbp-0x08]       « soma o b: aqui está o a + b

G_M000_IG03:                ;; offset=0x0016
       add      rsp, 16
       pop      rbp
       ret                                      « devolve o resultado

; Total bytes of code 28

5                                               « só agora o Somar roda e o resultado aparece

Isso é código de máquina x64 de verdade, escrito na forma legível (assembly): as instruções que o processador Intel/AMD da Ana executou. Os 4 bytes de IL viraram 28 bytes de instruções do processador. Você não precisa entender cada linha; repare só no add, que é o a + b do seu código, e em três detalhes da primeira parte:

  • Tier0: "nível 0". Na primeira vez, o JIT traduz rápido, sem caprichar, para o programa começar logo. Se o método for chamado muitas vezes, o .NET o traduz de novo, agora caprichando (o "nível 1"). Isso se chama compilação em camadas.
  • X64 e on Unix: o JIT gerou código para o processador e o sistema desta máquina. No Mac com chip M, a mesma IL viraria código ARM64.
  • A listagem aparece antes do 5: o JIT traduz o Somar na primeira vez que ele é chamado, um instante antes de rodar. Só depois a conta é feita e o 5 chega à tela.

Missão: use o mesmo Somar para mostrar a soma de 10 e 20. Mude só os números da primeira linha e rode: a saída deve ser 30.

Console.WriteLine(Conta.Somar(2, 3));

static class Conta
{
    public static int Somar(int a, int b) => a + b;
}
Console.WriteLine(Conta.Somar(10, 20));

static class Conta
{
    public static int Somar(int a, int b) => a + b;
}
E o coletor de lixo?

Enquanto o programa roda, ele cria textos, listas e objetos na memória. O CLR acompanha tudo isso e, de tempos em tempos, o coletor de lixo passa recolhendo o que ninguém usa mais, para a memória não acabar. Você não precisa fazer nada; é por isso que o C# é chamado de linguagem gerenciada. A aula Memória e Coletor de Lixo mostra o coletor trabalhando.


Muitos nomes, um .NET só

O .NET mudou de nome algumas vezes, e a internet está cheia de material de todas as épocas. Este é o mapa:

NomeÉpocaRoda emSituação hoje
.NET Framework (1.0 a 4.8.1)2002 em dianteSó WindowsAinda vem no Windows e roda muito programa antigo, mas não ganha novidades. Fica parado no C# 7.3.
Mono2004 em dianteLinux, macOS, Android, iPhoneA versão livre que levou o C# para fora do Windows. Ainda vive dentro do Unity e de partes do .NET para celular.
.NET Core (1.0 a 3.1)2016 a 2019Linux, Windows, macOSO .NET novo, livre e multiplataforma. Todas as versões já saíram de suporte.
.NET (5, 6, 7, 8, 9, 10…)2020 em dianteLinux, Windows, macOSA continuação do .NET Core, sem o "Core" no nome (o 4 foi pulado para não confundir com o .NET Framework 4). É este que o guia usa: o .NET 10.

Qual usar? Para programas novos, sempre o .NET atual (hoje, o .NET 10). O .NET Framework só faz sentido para manter um programa antigo do Windows que já usa ele. Quando um tutorial disser "crie um projeto .NET Framework", ou mostrar um caminho como C:\Windows\Microsoft.NET\Framework, ele é dessa época antiga.

Quem faz o seu .NET

O código do .NET é aberto, então outras empresas podem compilá-lo. O .NET da Ana não foi baixado do site da Microsoft: foi a Canonical, a empresa do Ubuntu, que o compilou a partir do código-fonte e o colocou no repositório do Ubuntu. Por isso as versões são um pouco diferentes: no mesmo mês, o site da Microsoft tinha o SDK 10.0.402 com o Runtime 10.0.13, e o Ubuntu, o SDK 10.0.112 com o Runtime 10.0.12. Os dois são o mesmo .NET 10 e o mesmo C# 14; a aula Versões explica os números e a aula Instalar no Linux mostra as duas formas de instalar.


Quando dá errado

Só o Runtime, sem o SDK: No .NET SDKs were found.

Para ver o que acontece num computador que só tem o Runtime, a Ana copiou só as peças do Runtime para uma pasta ~/rt e tentou usar o dotnet de lá:

$ ls ~/rt ~/rt/shared
/home/ana/rt:
dotnet
host
shared

/home/ana/rt/shared:
Microsoft.NETCore.App                           « só o Runtime; nada de sdk
$ ~/rt/dotnet run ola.cs
The command could not be loaded, possibly because:
  * You intended to execute a .NET application:
      The application 'run' does not exist or is not a managed .dll or .exe.
  * You intended to execute a .NET SDK command:
      No .NET SDKs were found.

Download a .NET SDK:
https://aka.ms/dotnet/download

Learn about SDK resolution:
https://aka.ms/dotnet/sdk-not-found
$ echo $?
155

O que significa: esta mensagem sai em inglês, porque vem do "anfitrião" do .NET, a parte mais básica, que não tem tradução. Ela diz: "não consegui carregar o comando, talvez porque você quis rodar um aplicativo chamado run (que não existe) ou um comando do SDK, mas nenhum SDK foi encontrado". O Runtime sabe rodar programas prontos (o ~/rt/dotnet ola.dll funciona), mas o run, o build e o new moram no SDK.

Como consertar: instale o SDK, e não só o Runtime: no Ubuntu, sudo apt install dotnet-sdk-10.0 (aula Instalar no Linux).

Programa feito para outra versão: You must install or update .NET to run this application.

A Ana editou o ola.runtimeconfig.json de uma cópia do programa para dizer que ele precisa do .NET 11, que ela não tem, e tentou rodar:

$ grep version ~/ola-pronto/ola.runtimeconfig.json
      "version": "11.0.0"
$ dotnet ~/ola-pronto/ola.dll
You must install or update .NET to run this application.

App: /home/ana/ola-pronto/ola.dll
Architecture: x64
Framework: 'Microsoft.NETCore.App', version '11.0.0' (x64)    « o que o programa pede
.NET location: /usr/lib/dotnet/

The following frameworks were found:
  10.0.12 at [/usr/lib/dotnet/shared/Microsoft.NETCore.App]   « o que a Ana tem

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=11.0.0&arch=x64&rid=ubuntu.26.04-x64&os=ubuntu.26.04
$ echo $?
150

O que significa: "você precisa instalar ou atualizar o .NET para rodar este aplicativo". O programa pede o Runtime 11.0, e só existe o 10.0.12. Um programa feito para uma versão roda nas correções mais novas da mesma versão (um programa do 10.0.0 roda no 10.0.12), mas não "desce" para uma versão mais velha. Isso acontece de verdade quando alguém manda um programa compilado para um .NET mais novo que o seu.

Como consertar: instale o Runtime que a mensagem pede (o endereço do fim já leva à página certa) ou peça um programa compilado para a sua versão.

Instalou o ".NET Framework" pensando que era o .NET

O que acontece: no Windows, sites antigos e alguns programas pedem o ".NET Framework 4.8". Ele não serve para este guia: não tem o comando dotnet nem o C# 14. No Linux e no macOS, ele nem existe.

Como consertar: instale o .NET SDK 10 (aula Instalar no Windows e macOS). Os dois podem ficar instalados juntos no Windows sem problema.


Referência

TermoO que é
.NETA plataforma: compilador, Runtime, biblioteca e ferramentas
SDKO kit de quem programa: tudo do Runtime + compilador + comandos dotnet run/new/build
RuntimeO necessário para rodar programas prontos: CLR, JIT, coletor de lixo e biblioteca
Roslyn (csc)O compilador de C#: transforma .cs em IL
MSBuildOrganiza o build: decide o que compilar, com quais opções e bibliotecas
ILA linguagem intermediária que fica dentro dos .dll
AssemblyUm arquivo .dll (ou .exe) com IL e metadados
CLRO motor que carrega e roda os assemblies (libcoreclr.so)
JITTraduz IL para código de máquina enquanto o programa roda (libclrjit.so)
BCL (biblioteca de classes)As classes prontas: Console, Math, List, File…
GC (coletor de lixo)Recolhe a memória que o programa não usa mais
ComandoPara quê
dotnet --list-sdksLista os SDKs instalados
dotnet --list-runtimesLista os Runtimes instalados
dotnet --versionMostra a versão do SDK em uso
dotnet build ola.csCompila sem rodar e diz onde ficou o .dll
dotnet arquivo.dllRoda um programa pronto (só precisa do Runtime)

Exercícios

1. A linguagem intermediária

Qual é a sigla da linguagem intermediária que o compilador escreve dentro do .dll?

2. Listar os Runtimes

Complete o comando que mostra os Runtimes instalados:

dotnet

3. O pacote certo

Complete o nome do pacote do Ubuntu que tem tudo o que o guia precisa (o kit de quem programa):

sudo apt install dotnet--10.0

4. Quem transforma a IL em instruções do processador, enquanto o programa roda?

5. Um colega só quer rodar o programa que a Ana fez, sem programar. O que ele precisa instalar, no mínimo?

6. Por que o mesmo ola.dll roda no Linux, no Windows e no Mac?

7. O dotnet run ola.cs respondeu No .NET SDKs were found. O que falta?

Missão final: o caminho do código

Missão: o programa mostra as etapas do caminho do código, mas a Ana trocou a ordem. Ponha as três linhas na ordem certa (Roslyn, CLR, JIT) e rode.

Console.WriteLine("3. JIT: IL vira código de máquina");
Console.WriteLine("1. Roslyn: .cs vira IL");
Console.WriteLine("2. CLR: carrega o .dll");
Console.WriteLine("1. Roslyn: .cs vira IL");
Console.WriteLine("2. CLR: carrega o .dll");
Console.WriteLine("3. JIT: IL vira código de máquina");

Resumo

  • O .NET é a plataforma por baixo do C#. O SDK é para quem programa (compilador e comandos); o Runtime é para quem só roda programas. O SDK já traz o Runtime.
  • O caminho: o Roslyn compila o .cs para IL num .dll; o CLR carrega o .dll; o JIT traduz a IL para o processador na hora de rodar.
  • O Console e as outras classes prontas vêm da biblioteca de classes, nos 172 .dll do Runtime. O coletor de lixo cuida da memória.
  • dotnet --list-sdks e dotnet --list-runtimes mostram o que está instalado. No Ubuntu, tudo fica em /usr/lib/dotnet.
  • .NET Framework (só Windows, antigo), Mono, .NET Core e .NET 5 a 10 são a mesma família; o guia usa o .NET 10.
  • Próximo passo: entender os números das versões, em Versões do C# e do .NET.