Diseño de buenas extensiones
Aplique prácticas recomendadas y evite errores comunes.
Diseño de buenas extensiones es una lección gratuita de C# Academy en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de C# Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de C# Academy incluye 4 lecciones en total.
Diseñar extensiones agradables de usar
Los métodos de extensión son potentes, pero esa potencia invita a usarlos mal. Las buenas extensiones parecen una parte natural del tipo, son fáciles de descubrir y nunca sorprenden a quien las llama. En esta lección se explican pautas prácticas.
Extender el comportamiento, no la identidad
Prefiera las extensiones para comportamientos utilitarios que se lean bien en una canalización. Si un método realmente pertenece a un tipo que usted controla, conviértalo en un miembro real.
using System;
public static class TimeExtensions
{
// A pleasant, focused helper
public static bool IsWeekend(this DayOfWeek day)
=> day == DayOfWeek.Saturday || day == DayOfWeek.Sunday;
}
public class Program
{
public static void Main()
{
Console.WriteLine(DayOfWeek.Sunday.IsWeekend());
Console.WriteLine(DayOfWeek.Monday.IsWeekend());
}
}Mantener los métodos puros cuando sea posible
Las extensiones que devuelven un valor nuevo y evitan efectos secundarios son más fáciles de entender y de encadenar. Procure que reciban una entrada y devuelvan un valor.
using System;
public static class StringExtensions
{
// Pure: depends only on input, returns a new string
public static string Truncate(this string s, int max)
=> s.Length <= max ? s : s.Substring(0, max) + "...";
}
public class Program
{
public static void Main()
{
Console.WriteLine("Extension methods are great".Truncate(10));
}
}Protegerse contra entradas null
Dado que quien llama puede invocar una extensión sobre un receptor null, decida deliberadamente qué debe ocurrir. Tolere null correctamente o lance una ArgumentNullException clara.
using System;
public static class StringExtensions
{
public static string SafeUpper(this string s)
{
if (s is null) throw new ArgumentNullException(nameof(s));
return s.ToUpper();
}
}
public class Program
{
public static void Main()
{
try { ((string)null).SafeUpper(); }
catch (ArgumentNullException ex) { Console.WriteLine("Caught: " + ex.ParamName); }
Console.WriteLine("ok".SafeUpper());
}
}Elegir nombres fáciles de descubrir
Los desarrolladores encuentran las extensiones escribiendo el punto y examinando IntelliSense. Los nombres claros, basados en verbos y que describen el resultado hacen que sus auxiliares sean fáciles de descubrir y se documenten por sí mismos.
using System;
using System.Collections.Generic;
public static class CollectionExtensions
{
public static bool HasItems<T>(this ICollection<T> c)
=> c != null && c.Count > 0;
}
public class Program
{
public static void Main()
{
var list = new List<int> { 1 };
Console.WriteLine(list.HasItems());
}
}Agrupar de forma coherente las extensiones relacionadas
Coloque las extensiones de un mismo concepto en una única clase estática y un espacio de nombres con nombres claros. Así, las importaciones tienen sentido y los usuarios pueden incorporar exactamente los auxiliares que necesitan.
using System;
using System.Collections.Generic;
public static class EnumerableExtensions
{
public static IEnumerable<T> TakeFirst<T>(this IEnumerable<T> source, int n)
{
int taken = 0;
foreach (var item in source)
{
if (taken++ >= n) yield break;
yield return item;
}
}
}
public class Program
{
public static void Main()
{
var nums = new List<int> { 1, 2, 3, 4 };
Console.WriteLine(string.Join(",", nums.TakeFirst(2)));
}
}No oculte comportamientos inesperados
Una extensión que parece económica, pero realiza un trabajo costoso o con estado, contradice las expectativas. Haga evidente el costo; si consulta una base de datos o modifica el estado, nómbrela en consecuencia.
using System;
public static class MathExtensions
{
// Cheap and obvious: no hidden cost
public static int ClampMin(this int value, int min)
=> value < min ? min : value;
}
public class Program
{
public static void Main()
{
Console.WriteLine((-3).ClampMin(0));
Console.WriteLine(5.ClampMin(0));
}
}Evite extender object
Extender object contamina todos los tipos del programa con su método y satura IntelliSense en todas partes. En su lugar, diríjase al tipo o la interfaz más específicos que tengan sentido.
using System;
public static class IntExtensions
{
// Specific to int, not object - far less noisy
public static string ToOrdinal(this int n)
{
if (n % 10 == 1 && n % 100 != 11) return n + "st";
if (n % 10 == 2 && n % 100 != 12) return n + "nd";
if (n % 10 == 3 && n % 100 != 13) return n + "rd";
return n + "th";
}
}
public class Program
{
public static void Main()
{
Console.WriteLine(1.ToOrdinal());
Console.WriteLine(22.ToOrdinal());
}
}Prefiera devolver IEnumerable para las secuencias
Para los operadores de secuencias, devuelva IEnumerable<T> y use la ejecución diferida. Esto facilita la composición y evita obligar a quien llama a materializar datos que quizá no necesite.
using System;
using System.Collections.Generic;
public static class SeqExtensions
{
public static IEnumerable<int> Doubled(this IEnumerable<int> source)
{
foreach (var n in source) yield return n * 2;
}
}
public class Program
{
public static void Main()
{
var nums = new List<int> { 1, 2, 3 };
foreach (var n in nums.Doubled()) Console.WriteLine(n);
}
}Lista de comprobación para buenas extensiones
Antes de publicar una extensión, pregúntese: ¿el nombre es claro? ¿Es pura o sus efectos secundarios son evidentes? ¿Se gestiona null? ¿El tipo de destino es específico? ¿Está agrupada de forma coherente? Si responde afirmativamente a todo, tiene un auxiliar que los demás disfrutarán al usar.
using System;
public static class StringExtensions
{
public static string OrDefault(this string s, string fallback)
=> string.IsNullOrEmpty(s) ? fallback : s;
}
public class Program
{
public static void Main()
{
Console.WriteLine("".OrDefault("N/A"));
Console.WriteLine("value".OrDefault("N/A"));
}
}Pruébelo usted mismo
Aplique la lista de comprobación: un tipo de destino específico, un nombre claro, tolerancia a null y un resultado puro. Esta pequeña extensión cumple todas las pautas.
using System;
using System.Collections.Generic;
public static class DictionaryExtensions
{
// Clear name, specific type, null-safe, pure
public static TValue GetOrDefault<TKey, TValue>(
this IReadOnlyDictionary<TKey, TValue> dict, TKey key, TValue fallback)
{
if (dict == null) return fallback;
return dict.TryGetValue(key, out var value) ? value : fallback;
}
}
public class Program
{
public static void Main()
{
var ages = new Dictionary<string, int> { ["Ann"] = 30 };
Console.WriteLine(ages.GetOrDefault("Ann", -1));
Console.WriteLine(ages.GetOrDefault("Bob", -1));
}
}Comprobación rápida
Aplique las pautas de diseño.
Recapitulación
Las extensiones bien diseñadas parecen nativas y predecibles.
- Prefiera auxiliares puros que devuelvan valores y se puedan encadenar fácilmente.
- Decida y documente el comportamiento con null.
- Use nombres claros, basados en verbos, para facilitar su descubrimiento.
- Diríjase a tipos o interfaces específicos, no a
object. - Agrupe los auxiliares relacionados y devuelva
IEnumerable<T>para las secuencias.
Preguntas frecuentes
¿La lección «Diseño de buenas extensiones» es gratis?
Sí — el texto completo de «Diseño de buenas extensiones» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de C# Academy, actualiza a CoddyKit PRO. El curso de C# Academy incluye 4 lecciones en total.
¿Qué aprenderé en «Diseño de buenas extensiones»?
Aplique prácticas recomendadas y evite errores comunes. Practicas C# Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar C# Academy?
No se requiere experiencia previa. C# Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.
¿Cuánto tiempo toma la lección «Diseño de buenas extensiones»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de C# Academy?
Sí. Cada lección de C# Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Definición de métodos de extensión
- Extensión de interfaces y genéricos
- Resolución de métodos de extensión
- Diseño de buenas extensiones