Extension Properties en C#: cuando quieres, pero no puedes (y cómo solucionarlo)
Imagine una situación: está trabajando con una biblioteca donde la clase Person tiene una propiedad FullName. Todo está bien, pero hay un problema: necesita agregar una propiedad calculada AgeInDays que calcule la edad en días desde la fecha de nacimiento. Está acostumbrado a los extension methods (métodos de extensión) y piensa: "¿Por qué no hacer extension properties (propiedades de extensión)?"
Y entonces busca en Stack Overflow, ve una pregunta con miles de visitas: "Does C# have extension properties?" — y con decepción lee la respuesta: "No, no son compatibles oficialmente". ¡Pero no se apresure a cerrar la pestaña! En este artículo no solo analizaremos por qué no existen, sino que también encontraremos soluciones elegantes que no comprometen el rendimiento.
¿Por qué C# no agrega extension properties? Contexto histórico
Empecemos con lo principal: extension properties no es un capricho, sino una solicitud antigua de la comunidad. Ya en 2016 apareció una propuesta en GitHub (C# feature request), pero aún no se ha implementado. ¿Por qué?
- Complejidad con el compilador: A diferencia de los métodos, las propiedades tienen getters y setters que deben estar vinculados a una instancia específica. Extender esta lógica a nivel de sintaxis requiere cambios significativos en el compilador.
- Conflicto con indexadores: Las propiedades y los indexadores en C# están estrechamente relacionados. Agregar extension properties podría crear ambigüedades.
- Prioridad de otras características: El equipo de C# se ha centrado en records, pattern matching y static abstract members (C# 11), que aportan más beneficios para la mayoría de los escenarios.
Pero la falta de soporte oficial no es una sentencia. Veamos cómo los desarrolladores experimentados resuelven este problema.
Métodos de extensión: su herramienta principal (pero con matices)
La forma más obvia es usar extension methods en lugar de propiedades. En lugar de person.AgeInDays, escribe person.GetAgeInDays(). Esto funciona, pero rompe el principio de uniformidad: en el resto del código usa propiedades, y aquí de repente un método.
public static class PersonExtensions{ public static int GetAgeInDays(this Person person) { return (DateTime.Now - person.BirthDate).Days; }}
// Uso:var age = person.GetAgeInDays(); // Funciona, pero no es bonitoDesventaja: el código se vuelve menos legible, especialmente en cadenas de llamadas LINQ. Ventaja: es completamente gratuito en términos de rendimiento: la llamada a un método de extensión no es diferente de un método estático normal.
Alternativa n.º 1: Fábricas estáticas y clases auxiliares
Si necesita una "propiedad" que se calcule a partir de datos, puede crear una clase auxiliar estática:
public static class PersonHelper{ public static int AgeInDays(Person person) => (DateTime.Now - person.BirthDate).Days;}
// Uso:var age = PersonHelper.AgeInDays(person);Esto es más honesto que un método de extensión: se ve de inmediato que es una función auxiliar. Pero sigue sin ser una propiedad.
Alternativa n.º 2: Atributos y generación de código (Source Generators)
Para escenarios complejos (por ejemplo, agregar propiedades a clases de paquetes NuGet), puede usar source generators. Aparecieron en .NET 5 y permiten generar código en tiempo de compilación. Usted escribe un atributo y el generador crea una clase parcial con las propiedades necesarias.
[AutoExtensionProperty(typeof(Person), nameof(Person.BirthDate))]public partial class PersonExtensions{ // El generador creará la propiedad AgeInDays}
// Código generado:public partial class PersonExt