← C# EspañolChapter 13 of 13

Mejores Prácticas

## Objetivos de Aprendizaje - Escribir código C# limpio y mantenible - Comprender la recolección de basura - Introducción a la programación asíncrona - Seguir convenciones de nomenclatura ## Convenciones de Nomenclatura ### PascalCase ```csharp // Clases, métodos, propiedades, eventos public class BankAccount { public string AccountNumber { get; set; } public decimal Balance { get; set; } public event EventHandler? BalanceChanged; public void Deposit(decimal amount) { } } ``` ### camelCase ```csharp // Variables locales, parámetros public void CalculateTotal(int firstNumber, int secondNumber) { decimal totalAmount = firstNumber + secondNumber; } ``` ### Campos Privados ```csharp class Person { private string _name; // Prefijo de guión bajo (recomendado) private int _age; // O private string name; // Sin prefijo private int age; } ``` ### Constantes ```csharp // const - PascalCase public const int MaxRetryCount = 3; public const string DefaultCulture = "en-US"; // readonly - _camelCase private readonly ILogger _logger; ``` ## Organización del Código ### Organización de Namespaces ```csharp namespace MyApp.Services { public class OrderService { } } namespace MyApp.Models { public class Order { } } namespace MyApp.Repositories { public class OrderRepository { } } ``` ### Organización de Archivos ```csharp // Una clase pública por archivo (recomendado) // El nombre del archivo coincide con la clase // Order.cs contiene la clase Order ``` ### Directivas Using ```csharp // System namespaces primero using System; using System.Collections.Generic; using System.Linq; // Luego de terceros using Newtonsoft.Json; // Luego del proyecto using MyApp.Models; using MyApp.Services; ``` ### Using Global (C# 10+) ```csharp // GlobalUsings.cs global using System; global using System.Collections.Generic; global using System.Linq; global using System.Threading.Tasks; ``` ## Recolección de Basura ### Cómo Funciona GC ```csharp // Objetos sin referencias son elegibles para recolección class Program { static void Main() { var person = new Person("Alice"); person = null; // Ahora elegible para GC GC.Collect(); // Forzar recolección (no recomendado) GC.WaitForPendingFinalizers(); } } ``` ### Patrón IDisposable ```csharp class ResourceHolder : IDisposable { private bool _disposed; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!_disposed) { if (disposing) { // Liberar recursos administrados } // Liberar recursos no administrados _disposed = true; } } ~ResourceHolder() // Finalizador { Dispose(false); } } // Uso using (var holder = new ResourceHolder()) { // Usar holder } // Disposed automáticamente ``` ### Instrucción using ```csharp // Asegurar disposición using (var reader = new StreamReader("file.txt")) using (var writer = new StreamWriter("output.txt")) { // Usar recursos } // Ambos disposed // C# 8+ simplificado using var reader = new StreamReader("file.txt"); string content = reader.ReadToEnd(); // Disposed al final del alcance ``` ## Programación Asíncrona ### async y await ```csharp using System.Threading.Tasks; class Program { static async Task Main() { Console.WriteLine("Iniciando..."); await LongRunningOperation(); Console.WriteLine("¡Hecho!"); } static async Task LongRunningOperation() { await Task.Delay(1000); // Simular trabajo Console.WriteLine("Operación completada"); } } ``` ### Devolver Valores ```csharp static async Task CalculateSumAsync(int a, int b) { await Task.Delay(100); // Simular trabajo asíncrono return a + b; } // Uso int result = await CalculateSumAsync(5, 3); Console.WriteLine(result); // 8 ``` ### Async vs Síncrono ```csharp // Síncrono (bloquea) string content = File.ReadAllText("file.txt"); // Asíncrono (no bloquea) async Task ReadFileAsync() { return await File.ReadAllTextAsync("file.txt"); } // Uso string content = await ReadFileAsync(); ``` ### No Usar async void ```csharp // MAL - fire and forget, excepciones no pueden capturarse async void BadMethod() { await Task.Delay(100); throw new Exception("¡Ups!"); } // BIEN - async Task async Task GoodMethodAsync() { await Task.Delay(100); throw new Exception("¡Ups!"); } ``` ## Seguridad Null ### Tipos de Referencia Anulables (C# 8+) ```csharp // No anulable por defecto (en contexto anulable) string name = "Alice"; // No puede ser null // name = null; // ¡Advertencia! string? nullable = null; // Puede ser null // Verificación de null requerida antes de usar if (nullable != null) { Console.WriteLine(nullable.Length); } // Operador condicional nulo int? length = nullable?.Length; // Coalcencia nula string safe = nullable ?? "default"; ``` ### Patrones para Null ```csharp // Pattern matching con null if (obj is string s && s.Length > 0) { Console.WriteLine(s); } // expresión switch con null string Describe(object? obj) => obj switch { null => "null", string s => $"Cadena: {s}", int i => $"Int: {i}", _ => "Algo más" }; ``` ## Consejos de Rendimiento ### Construcción de Cadenas ```csharp // Malo - crea muchas cadenas string result = ""; for (int i = 0; i < 100; i++) { result += i.ToString(); } // Bueno - StringBuilder StringBuilder sb = new StringBuilder(); for (int i = 0; i < 100; i++) { sb.Append(i); } string result = sb.ToString(); ``` ### Capacidad de List ```csharp // Si conoces el tamaño List numbers = new List(100); // Pre-allocar for (int i = 0; i < 100; i++) { numbers.Add(i); } ``` ### Rendimiento de LINQ ```csharp // Ejecución diferida var query = collection.Where(x => x > 5); // No ejecutada todavía // Ejecución forzada cuando se necesita var list = query.ToList(); // Ejecuta // Para datasets grandes, considerar // - ToList() temprano para evitar enumeración múltiple // - AsParallel() para consultas intensivas en CPU ``` ## Mejores Prácticas de Manejo de Errores ```csharp // Excepciones específicas primero try { await ReadDataAsync(); } catch (FileNotFoundException ex) { // Manejar específico } catch (IOException ex) { // Manejar IO } catch (Exception ex) { // Registrar errores inesperados Logger.Error(ex); throw; } // Filtros de excepciones para control granular try { Process(data); } catch (Exception ex) when (ex is InvalidOperationException or ArgumentException) { // Manejar excepciones específicas } ``` ## Calidad del Código ### Principios SOLID ```csharp // Responsabilidad Única class UserService { void CreateUser() { } } class EmailService { void SendEmail() { } } // Abierto/Cerrado - extender, no modificar abstract class Shape { public abstract double Area(); } class Rectangle : Shape { public double Width, Height; public override double Area() => Width * Height; } // Sustitución de Liskov // Las clases derivadas deben ser usables como clase base // Segregación de Interfaces interface IReadable { void Read(); } interface IWritable { void Write(); } // Inversión de Dependencias class OrderService { private readonly IRepository _repository; public OrderService(IRepository repository) => _repository = repository; } ``` ### Atributos Útiles ```csharp // Atributos comunes [Obsolete("Usar NewMethod en su lugar")] void OldMethod() { } [DebuggerDisplay("Name = {Name}")] class Person { public string Name { get; set; } } [Serializable] class Data { } // Anulable #nullable enable class MyClass { public string? NullableProperty { get; set; } } #nullable restore ``` ## Resumen - Seguir convenciones de nomenclatura (PascalCase, camelCase) - Usar global using (C# 10+) para archivos limpios - Implementar IDisposable para recursos no administrados - Preferir async/await para operaciones de E/S - Habilitar tipos de referencia anulables - Usar StringBuilder para concatenación de cadenas - Manejar excepciones de lo específico a lo general - Seguir principios SOLID - Escribir código que se autodocumente con nombres claros

Comments

Comments powered by Giscus

To enable comments, add your Giscus embed code here.

Learn more about Giscus →