Introdução
Toda vez que preciso de uma pequena automação no dia a dia — verificar uma configuração, importar um CSV, chamar uma API interna — a pergunta inevitável aparece: uso PowerShell, Python, ou crio um projeto .NET completo? Durante anos a resposta padrão para times .NET era criar um projeto console, mesmo para tarefas de cinco minutos. Isso mudou. Hoje é possível executar C# como script no .NET sem criar solução, sem .csproj, sem boilerplate.
Neste artigo vou mostrar as três abordagens disponíveis, quando cada uma faz sentido e como incorporar isso no dia a dia de um time .NET. Os exemplos completos estão no repositório blog-zocateli-sample — no artigo vou mostrar apenas os trechos essenciais para explicar cada conceito.
As três abordagens são:
- Top-level statements — eliminam o boilerplate de
class Programestatic void Main(), mas ainda precisam de um.csproj - dotnet-script — executa arquivos
.csxsem projeto, com suporte a NuGet inline dotnet run arquivo.cs— novidade do .NET 10 que executa um único arquivo.cssem nenhuma configuração
Para quem já tem familiaridade com .NET e quer aproveitar C# para automações do cotidiano, as três abordagens têm casos de uso complementares — e entender as diferenças é o que vai guiar a escolha certa na hora certa.
Recomendo a leitura do artigo sobre Pipeline de Configuração .NET 8+ para entender como gerenciar segredos e variáveis de ambiente, algo que inevitavelmente aparece em scripts de automação.
Pré-requisitos
Para acompanhar os exemplos práticos, você vai precisar de:
- .NET SDK 10 instalado — verifique com
dotnet --version(deve retornar10.x.x) - dotnet-script instalado como global tool:
| |
Após a instalação, verifique com dotnet script --version.
- Familiaridade básica com C# e linha de comando
O Que É C# como Script?
Historicamente, executar código C# exigia criar uma solução, um projeto .csproj, escrever um Program.cs com namespace, class Program e static void Main(string[] args) — só depois vinha o código em si. Para uma tarefa de dez linhas, o overhead era desproporcional.
A ideia de usar C# como linguagem de script não é nova: o Roslyn Scripting API existe desde 2015 e é a base do dotnet-script. O que mudou ao longo do tempo foi a integração dessa capacidade diretamente no fluxo de trabalho .NET:
- .NET 5 (2020): top-level statements eliminam o boilerplate no
Program.cs, mas o projeto ainda é necessário - dotnet-script (independente da versão do SDK): ferramenta de terceiros que executa
.csxsem projeto nenhum - .NET 10 (2025):
dotnet run arquivo.csexecuta um único arquivo.csdiretamente, sem projeto
A diferença conceitual entre “script” e “aplicação” está no ciclo de vida e na intenção: um script é executado uma vez, resolve uma tarefa específica e não precisa de distribuição formal. Uma aplicação tem ciclo de vida gerenciado, deploys, versionamento. Quando o script começa a crescer — múltiplos arquivos, testes, configuração externalizada — o momento de criar um projeto dedicado chegou.
A tabela abaixo resume as três abordagens:
| Abordagem | Extensão | Requer projeto | NuGet inline | .NET mínimo |
|---|---|---|---|---|
| Top-level statements | .cs | Sim (mas mínimo) | Não | .NET 5 |
| dotnet-script | .csx | Não | Sim (#r "nuget:...") | .NET Core 3.1+ |
| dotnet run arquivo | .cs | Não | Não (ainda) | .NET 10 |

ℹ️ Informação: As três abordagens são complementares, não concorrentes. A escolha depende do contexto: complexidade do script, necessidade de pacotes externos e versão do SDK disponível no ambiente.
Top-Level Statements: C# Moderno sem Boilerplate
Os top-level statements foram introduzidos no C# 9 (.NET 5) e permitem escrever o código diretamente no arquivo Program.cs, sem declarar namespace, class ou Main. O compilador gera tudo isso automaticamente.
A diferença visual é clara:
| |
| |
Ambos compilam para o mesmo resultado. O compilador infere a classe e o método de entrada. Quando preciso de argumentos de linha de comando, o array args está disponível implicitamente:
| |
📂 Código Fonte: O exemplo completo está disponível no repositório de exemplos do blog:
BlogSamples/Scripting/TopLevelStatements/
⚠️ Atenção: Top-level statements ainda exigem um arquivo
.csprojpara compilar. Eles removem o boilerplate da classe e do métodoMain, mas não eliminam o projeto. Se você quer executar código sem projeto nenhum, as próximas duas abordagens são o caminho.
A limitação principal é que apenas um arquivo por projeto pode usar top-level statements — os demais precisam de declarações normais. Na prática isso é suficiente para a maioria dos projetos console.
dotnet-script: Executando Arquivos .csx
O dotnet-script é uma ferramenta global que interpreta arquivos .csx (C# Script) usando o Roslyn Scripting API. A diferença fundamental para top-level statements é que não precisa de projeto — o arquivo é a unidade de execução completa.
| |
O recurso mais poderoso é a referência a pacotes NuGet diretamente no arquivo, sem csproj, sem dotnet add package:
| |
📂 Código Fonte: O exemplo completo está disponível no repositório de exemplos do blog:
BlogSamples/Scripting/DotnetScript/
Os casos de uso onde o dotnet-script brilha:
- Migrations avulsas: rodar uma migração pontual em ambiente específico sem o contexto do projeto principal
- Seed de dados: popular banco de dados de desenvolvimento com dados realistas
- Utilitários de CI/CD: verificar versões, validar configurações, gerar artefatos
- Consultas e relatórios: combinar Entity Framework (via NuGet) com lógica C# sem criar projeto
💡 Dica: No Linux e macOS, adicione
#!/usr/bin/env dotnet-scriptna primeira linha do arquivo.csxpara torná-lo executável diretamente no shell, sem precisar prefixar comdotnet script.
Executando um Arquivo .cs Diretamente com dotnet run
O .NET 10 trouxe uma funcionalidade aguardada: executar um único arquivo .cs sem projeto, sem solução, sem configuração nenhuma:
| |
Isso é diferente do dotnet run tradicional que precisa ser executado dentro de um diretório de projeto. Aqui, o arquivo .cs é a unidade completa de execução.
Um exemplo de uso real — verificar a versão de um serviço em múltiplos ambientes:
| |
| |
📂 Código Fonte: O exemplo completo está disponível no repositório de exemplos do blog:
BlogSamples/Scripting/DotnetRunSingleFile/
ℹ️ Informação: Na versão inicial do .NET 10,
dotnet run arquivo.csnão suporta múltiplos arquivos nem referências NuGet inline. Para scripts que precisam de pacotes externos, odotnet-scriptcom.csxainda é a melhor opção.
A maior vantagem dessa abordagem é a fricção zero: não há instalação de ferramenta adicional, funciona em qualquer máquina com .NET 10 SDK e não exige nenhum arquivo auxiliar.
Casos de Uso Reais: Quando C# como Script Faz Sentido
Após usar as três abordagens no dia a dia, alguns cenários onde a escolha por C# como script se pagou:
Automações de CI/CD: scripts de pipeline que precisam de lógica de negócio — validar configurações de appsettings.json, verificar que variáveis de ambiente obrigatórias estão presentes, gerar artefatos de release com nomes padronizados. Tudo isso em C# tipado, com as mesmas bibliotecas que o projeto principal usa.
Data seeding de desenvolvimento: popular um banco de dados local com dados realistas usando o mesmo DbContext e as mesmas entidades do projeto. Com dotnet-script e #r "nuget:Microsoft.EntityFrameworkCore.SqlServer,...", o script acessa o banco sem criar um projeto dedicado.
Utilitários de equipe: ferramentas internas que o time .NET precisa manter ao longo do tempo. Em C# com tipagem estática, o próximo desenvolvedor que abrir o script vai entender o código sem aprender uma nova linguagem.
Prototipação: testar o comportamento de uma API ou biblioteca antes de integrá-la ao projeto principal. Um arquivo .csx com #r "nuget:..." é mais rápido do que criar um projeto de testes.
⚠️ Atenção: Quando o script começa a crescer — múltiplas funções, múltiplos arquivos, necessidade de testes unitários, configuração externalizada — o momento de criar um projeto console dedicado chegou. Scripts são para tarefas pontuais, não para sistemas. O artigo sobre .NET Worker Background Service mostra o caminho quando a automação precisa virar serviço de longa duração.
Comparativo: C# Script vs PowerShell vs Python para Automações
Para times .NET, a comparação com as alternativas mais comuns é relevante. A tabela abaixo é objetiva — cada ferramenta tem vantagens reais:
| Critério | C# Script | PowerShell | Python |
|---|---|---|---|
| Curva de aprendizado (time .NET) | Baixa | Média | Alta |
| Acesso a bibliotecas .NET / NuGet | ✅ Nativo | ❌ Limitado | ❌ Não |
| Tipagem estática | ✅ Sim | ❌ Não | ❌ Não |
| Portabilidade (Linux/macOS/Windows) | ✅ Sim | ✅ Sim (PS Core) | ✅ Sim |
| Suporte a IDE (IntelliSense, debug) | ✅ VS Code + OmniSharp | Parcial | ✅ Sim |
| Performance de startup (JIT warm-up) | ⚠️ Lento | Médio | Médio |
| Administração do sistema operacional | ⚠️ Verboso | ✅ Nativo | Bom |
| Ecossistema de ML / data science | ❌ Não | ❌ Não | ✅ Sim |
Quando C# script perde: tarefas de shell puro (mover arquivos, gerenciar processos, pipeline de comandos), administração de sistema operacional, scripts de menos de dez linhas onde PowerShell é mais conciso.
Quando C# script ganha: lógica de negócio que usa os mesmos tipos e bibliotecas do projeto principal, consumo de APIs .NET, operações de banco via Entity Framework, qualquer coisa onde tipagem estática reduz erros.
Exemplo Prático
O cenário: um script que lê um arquivo CSV com produtos e envia cada registro para uma API REST interna — uma necessidade recorrente em projetos de migração de dados.
Com dotnet-script, o arquivo .csx referencia o pacote CSV diretamente:
| |
📂 Código Fonte: O exemplo completo, com tratamento de erros, cancelamento via
CancellationToken, retry e log estruturado está disponível no repositório de exemplos do blog:BlogSamples/Scripting/ExemploPratico/
O script completo no repositório inclui o record Produto, tratamento de HttpRequestException com retry manual, log de progresso e suporte a --dry-run via argumento.
Dicas e Boas Práticas
Prefira
.csxcomdotnet-scriptquando precisar de NuGet. Top-level statements num projeto.csprojmínimo ainda exigemdotnet add packagee alteram o arquivo de projeto. Com.csx, a referência fica dentro do próprio script e qualquer pessoa pode executar comdotnet script arquivo.csxsem setup adicional.Nunca hardcode segredos em scripts. Use variáveis de ambiente (
Environment.GetEnvironmentVariable) ou odotnet user-secretspara desenvolvimento local. Scripts de automação frequentemente circulam em repositórios e logs de CI — uma connection string ou token expostos são um risco real. Veja o artigo sobre Logging Estruturado para entender como evitar dados sensíveis em logs também.Para scripts usados em CI/CD, fixe a versão da ferramenta. Crie um
.config/dotnet-tools.jsonno repositório comdotnet tool install --local dotnet-script. Isso garante que o pipeline sempre usa a mesma versão, sem depender do que está instalado globalmente no agente de build.Adicione
#!/usr/bin/env dotnet-scriptpara Linux/macOS. Com essa linha na primeira linha do arquivo.csxe permissão de execução (chmod +x script.csx), o script pode ser chamado diretamente como./script.csxno terminal — sem prefixo. Em times com desenvolvedores em diferentes sistemas operacionais, isso reduz fricção.Defina um limite de crescimento. Scripts acima de 150–200 linhas geralmente indicam que a tarefa cresceu além do escopo de um script. Nesse ponto, criar um projeto console com estrutura adequada — testes, configuração externalizada, injeção de dependência — é mais sustentável do que expandir o script indefinidamente.
Resumo Objetivo
- Top-level statements — eliminam o boilerplate de
class Programestatic void Main()desde o C# 9 (.NET 5), mas ainda exigem um arquivo.csprojpara compilação; não são execução sem projeto. - dotnet-script — ferramenta global open-source que executa arquivos
.csxsem projeto, com suporte a referências NuGet inline via#r "nuget:Pacote,Versão", baseada no Roslyn Scripting API. dotnet run arquivo.cs— recurso nativo do .NET 10 que executa um único arquivo.cssem projeto, sem solução e sem configuração adicional; não suporta NuGet inline na versão inicial.- C# como script no CI/CD — permite que times .NET escrevam automações de pipeline reutilizando bibliotecas e tipagem do ecossistema .NET, sem aprender PowerShell ou Python.
- Limitação do
dotnet runarquivo único (.NET 10) — não suporta múltiplos arquivos nem NuGet inline; para essas necessidades,dotnet-scriptcom.csxé a alternativa adequada. - Critério de escolha — sem NuGet inline e sem projeto:
dotnet run arquivo.cs; com NuGet inline e sem projeto:dotnet-script .csx; com distribuição formal e múltiplos arquivos: projeto console com top-level statements.
Leia Também
- Pipeline de Configuração .NET 8+ — gerenciar configuração, segredos e variáveis de ambiente em scripts e aplicações .NET
- Logging Estruturado Dinâmico no .NET 8 — adicionar observabilidade estruturada aos seus scripts e workers
- .NET Worker Background Service — quando o script cresce e precisa virar um serviço de longa duração com ciclo de vida gerenciado
Referências
- What’s new in .NET 10 — Run C# files directly — Microsoft Learn — documentação oficial da feature
dotnet run arquivo.cs - dotnet-script GitHub — repositório oficial da ferramenta open-source para execução de
.csx - Top-level statements — C# Programming Guide — Microsoft Learn — referência completa da feature
- Roslyn Scripting API Samples — dotnet/roslyn — base técnica do dotnet-script, amostras do Roslyn Scripting API
- .NET CLI reference — dotnet run — Microsoft Learn — referência completa do comando
dotnet run - Global tools — .NET CLI — Microsoft Learn — instalação e gerenciamento de global tools como
dotnet-script - CsvHelper — GitHub — biblioteca usada no exemplo prático para leitura de CSV

Ao comentar, você concorda com nossa Política de Privacidade, Termos de Uso e Política de Exclusão de Dados.