Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Sugerencia
¿No está familiarizado con el desarrollo de software? Comience primero con los tutoriales de introducción . Obtendrá información sobre la organización del programa de forma natural a medida que crecen los proyectos.
¿Competente en otro idioma? Si está familiarizado con soluciones y proyectos en Visual Studio o compila sistemas como Maven o Cargo, en este artículo se asignan esos conceptos a .NET.
A medida que crece una aplicación de C#, debe organizar el código. .NET proporciona una jerarquía de herramientas organizativas (soluciones, proyectos, ensamblados, espacios de nombres y tipos) que funcionan conjuntamente para mantener administrables los código base grandes. Las convenciones descritas aquí representan un amplio consenso en la comunidad de .NET. Puede desviarse por motivos específicos, pero seguir estas convenciones hace que el código sea familiar y navegable a otros desarrolladores de .NET.
Jerarquía organizativa
Organice una aplicación .NET típica en capas, de más amplia a más específica:
| Level | Descripción | Ejemplo |
|---|---|---|
| Solution | Contenedor que agrupa proyectos relacionados. | MyApp.slnx |
| Proyecto | Unidad de compilación que genera un ensamblaje. | MyApp.Web.csproj |
| Ensamblaje | El resultado de la compilación .dll o .exe producido por un proyecto. |
MyApp.Web.dll |
| Namespace | Agrupación lógica de tipos. | MyApp.Web.Controllers |
| Tipo | Clase, estructura, interfaz, enumeración o delegado. | OrderController |
Cada nivel sirve para un propósito diferente. Las soluciones organizan el flujo de trabajo de desarrollo. Los proyectos definen lo que se compila juntos, y cada proyecto genera un ensamblaje. Los ensamblados son la unidad de implementación y control de versiones. Los espacios de nombres impiden colisiones de nombres y facilitan la búsqueda de tipos. Un único ensamblado puede contener varios espacios de nombres y un único espacio de nombres puede abarcar varios ensamblados. Los tipos definen el comportamiento y los datos reales.
Proyectos y ensamblados
Cada proyecto se compila en un único ensamblado: una biblioteca de clases o un ejecutable. Comience con un solo proyecto para aplicaciones pequeñas; no se divida prematuramente. La razón principal para crear un proyecto independiente es reutilizar ese código en más de una aplicación. Más allá de la reutilización, agregue proyectos cuando tenga un motivo concreto:
- Compartir código entre aplicaciones : extraiga lógica compartida en una biblioteca de clases a la que hacen referencia varias aplicaciones.
- Problemas independientes : mantenga independientes el acceso a los datos, la lógica de negocios y las capas de presentación.
- Dependencias de control : un proyecto solo puede usar tipos de proyectos a los que hace referencia explícitamente.
Un único proyecto funciona bien para muchas aplicaciones. Resista la necesidad de crear proyectos independientes "solo en caso". Siempre puede extraer una biblioteca más adelante cuando una segunda aplicación necesita el mismo código.
Coincidir los espacios de nombres con la estructura de carpetas
Los nombres de espacio de nombres deben seguir la estructura de carpetas de tu proyecto. Cuando veas el espacio de nombres MyApp.Services.Payments, sabes que debes buscar en la carpeta Services/Payments el código fuente de los tipos definidos en ese espacio de nombres. El SDK de .NET admite esta convención y, como es tan ampliamente seguida, violarla confunde activamente a otros desarrolladores.
// 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}";
}
El espacio de nombres raíz se establece automáticamente en el nombre del archivo del proyecto. Los tipos ubicados en subcarpetas no obtienen automáticamente subespacios de nombres; debes declarar el espacio de nombres explícitamente en cada archivo, pero asegúrate siempre de mantenerlos sincronizados.
Sugerencia
Puede cambiar el espacio de nombres raíz estableciendo <RootNamespace> en el archivo del proyecto.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>MyCompany.MyApp</RootNamespace>
</PropertyGroup>
</Project>
Organizar espacios de nombres por característica, no por tipo
Agrupa los tipos relacionados en espacios de nombres por característica o responsabilidad. Coloque una interfaz, sus implementaciones y tipos auxiliares juntos:
// 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);
La organización basada en funcionalidades mantiene todo lo que necesitas en un solo lugar, lo que facilita la navegación y el razonamiento sobre el código.
Modificadores de acceso y ensamblajes
Los modificadores de acceso funcionan junto con el proyecto y la estructura de ensamblaje para controlar la accesibilidad.
-
public: accesible desde cualquier ensamblado que haga referencia a este ensamblado. -
internal: solo se puede acceder dentro del mismo ensamblado (el valor predeterminado para los tipos de nivel superior). -
private,protected, ,private protected:protected internalaccesible en función del tipo contenedor, el ensamblado o los tipos derivados.
El valor predeterminado es internal para los tipos que no necesitan otros proyectos. Esta práctica oculta los detalles de implementación y le ofrece libertad para refactorizar sin interrumpir a los consumidores. Es especialmente importante para las bibliotecas compartidas:
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ácticas recomendadas
- Nombre los espacios de nombres de forma coherente. Use
CompanyName.ProductName.Featurecomo patrón de nomenclatura. Por ejemplo, useContoso.Inventory.Shipping. La nomenclatura coherente ayuda a los desarrolladores a encontrar tipos sin buscar. - Mantenga los proyectos enfocados. Cada proyecto debe tener una sola responsabilidad clara. Cuando un proyecto controla demasiadas preocupaciones no relacionadas, dividala.
- Use espacios de nombres con ámbito de archivo. La sintaxis
namespace MyApp.Services;reduce el nivel de sangría y es el estilo recomendado. Utilícelo en todo el código nuevo. - El valor predeterminado es
internal. Solo marque los tipospubliccuando otros ensamblajes los necesiten realmente. Siempre puede ampliar el acceso más adelante; restringirlo es un cambio disruptivo.