Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Sugestão
Novo no desenvolvimento de software? Começa primeiro pelos tutoriais para começar . Vais aprender sobre organização de programas naturalmente à medida que os teus projetos crescem.
Experiente noutra língua? Se está familiarizado com soluções e projetos no Visual Studio, ou constrói sistemas como Maven ou Cargo, este artigo mapeia esses conceitos para .NET.
À medida que uma aplicação C# cresce, é preciso organizar o código. O .NET fornece uma hierarquia de ferramentas organizacionais — soluções, projetos, assemblies, namespaces e tipos — que trabalham em conjunto para manter grandes bases de código geríveis. As convenções aqui descritas representam um consenso amplo na comunidade .NET. Podes desviar-te por razões específicas, mas seguir estas convenções torna o teu código familiar e navegável para outros programadores .NET.
A hierarquia organizacional
Organize uma aplicação .NET típica em camadas, da mais ampla à mais específica:
| Nível | Descrição | Exemplo |
|---|---|---|
| Solução | Um contentor que agrupa projetos relacionados. | MyApp.slnx |
| Projeto | Uma unidade de construção que produz um único conjunto. | MyApp.Web.csproj |
| Montagem | O resultado de compilação .dll ou .exe produzido por um projeto. |
MyApp.Web.dll |
| Espaço de nomes | Um agrupamento lógico de tipos. | MyApp.Web.Controllers |
| Type | Uma classe, estrutura, interface, enum ou delegado. | OrderController |
Cada nível tem um propósito diferente. As soluções organizam o seu fluxo de trabalho de desenvolvimento. Os projetos definem o que é compilado, e cada projeto produz uma assembly. Os assemblies são a unidade de implementação e versionamento. Os namespaces evitam colisões de nomes e tornam os tipos fáceis de encontrar. Um único assembly pode conter múltiplos namespaces, e um único namespace pode abranger múltiplos assemblies. Os tipos definem o comportamento real e os dados.
Projetos e assembleias
Cada projeto compila-se numa única assembleia: uma biblioteca de classes ou executável. Comece com um único projeto para pequenas aplicações — não divida prematuramente. A principal razão para criar um projeto separado é reutilizar esse código em mais do que uma aplicação. Para além da reutilização, adiciona projetos quando tiveres uma razão concreta:
- Partilhe código entre aplicações — extraia lógica partilhada para uma biblioteca de classes que várias aplicações referenciam.
- Preocupações separadas — mantenha o acesso aos dados, lógica de negócio e camadas de apresentação independentes.
- Dependências de controlo — um projeto só pode usar tipos de projetos que referencia explicitamente.
Um único projeto funciona bem para muitas aplicações. Resista à tentação de criar projetos separados "por precaução". Podes sempre extrair uma biblioteca mais tarde quando uma segunda aplicação precisar do mesmo código.
Combinar namespaces com a estrutura de pastas
Os nomes dos namespaces devem seguir a estrutura de pastas do seu projeto. Quando vês o namespace MyApp.Services.Payments, sabes que deves procurar na Services/Payments pasta o código-fonte dos tipos definidos nesse namespace. O SDK .NET suporta esta convenção, e é tão amplamente seguida que violá-la confunde ativamente outros programadores:
// File: Services/OrderService.cs
// Namespace mirrors the folder path
using MyApp.Core;
namespace MyApp.Services;
public class OrderService
{
public Order CreateOrder(string product, int quantity, decimal price) =>
new() { ProductName = product, Quantity = quantity, UnitPrice = price };
public string FormatSummary(Order order) =>
$"{order.Quantity}x {order.ProductName} = {order.Total:C}";
}
O seu espaço de nomes raiz é automaticamente definido como o nome do ficheiro do seu projeto. Os tipos nas subpastas não recebem automaticamente subnamespaces — declara-se o namespace explicitamente em cada ficheiro — mas devem ser sempre mantidos sincronizados.
Sugestão
Podes alterar o namespace raiz definindo <RootNamespace> no ficheiro do teu projeto.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>MyCompany.MyApp</RootNamespace>
</PropertyGroup>
</Project>
Organize os namespaces por característica, não por tipo de tipo
Agrupar tipos relacionados em espaços de nomes por característica ou responsabilidade. Junte uma interface, as suas implementações e tipos de suporte:
// Good: group by feature
namespace MyApp.Payments;
public interface IPaymentProcessor
{
bool ProcessPayment(decimal amount);
}
public class CreditCardProcessor : IPaymentProcessor
{
public bool ProcessPayment(decimal amount)
{
Console.WriteLine($"Processing credit card payment of {amount:C}");
return true;
}
}
public record PaymentResult(bool Success, string? TransactionId);
A organização baseada em funcionalidades mantém tudo o que precisa num só lugar, tornando o código mais fácil de navegar e de raciocinar.
Modificadores de acesso e montagens
Os modificadores de acesso funcionam com o projeto e a estrutura de montagem para controlar a acessibilidade:
-
public— acessível a partir de qualquer assembleia que faça referência a esta assembleia. -
internal— acessível apenas dentro do mesmo conjunto (o padrão para tipos de topo). -
private,protected,private protected,protected internal— acessível com base no tipo que contém, na montagem ou nos tipos derivados.
Utilize internal por padrão para tipos de que outros projetos não precisam. Esta prática esconde detalhes da implementação e dá-lhe liberdade para refatorar sem prejudicar os consumidores. É especialmente importante para bibliotecas partilhadas:
namespace MyApp.Inventory;
// Public — other projects can use this type
public class InventoryService
{
public int GetStockLevel(string productName) =>
StockDatabase.Lookup(productName);
}
// Internal — only visible within this assembly
internal static class StockDatabase
{
private static readonly Dictionary<string, int> _stock = new()
{
["Widget"] = 42,
["Gadget"] = 17
};
internal static int Lookup(string productName) =>
_stock.GetValueOrDefault(productName);
}
Práticas recomendadas
- Nomeie os namespaces de forma consistente. Usa
CompanyName.ProductName.Featurecomo padrão de nomes. Por exemplo, useContoso.Inventory.Shipping. A nomeação consistente ajuda os programadores a encontrar tipos sem necessidade de pesquisar. - Mantenha os projetos focados. Cada projeto deve ter uma responsabilidade única e clara. Quando um projeto lida com demasiadas preocupações não relacionadas, divide-o.
- Utilize espaços de nomes com escopo de ficheiro. A
namespace MyApp.Services;sintaxe reduz a indentação e é o estilo recomendado. Use-o em código totalmente novo. - Definir para
internalpor padrão. Só assinala os tipospublicquando outros conjuntos realmente precisam deles. Podes sempre alargar o acesso mais tarde; Restringi-la é uma mudança decisiva.