CodeGym /Cursos /C# SELF /Extension Members: propiedades

Extension Members: propiedades

C# SELF
Nivel 18, Lección 3
Disponible

1. Introducción

Antes solo se podían añadir a las clases extension methods. Pero no se podía añadir una propiedad (property) como extensión de clase. O sea, si querías que cualquier DateTime tuviera la propiedad IsWeekend, tenías que hacerlo con un método, lo cual no siempre es cómodo — sobre todo si quieres la sintaxis date.IsWeekend en vez de date.IsWeekend().

Con la llegada de C# 14 el sueño se hizo realidad: ahora puedes escribir propiedades de extensión de una forma parecida a los métodos. Te permiten añadir una nueva propiedad a un tipo existente sin tocar su código original, ¡y usarlas como si fueran propiedades normales!

¿Dónde es realmente útil?

  • Para tipos estándar de .NET y librerías de terceros cuyo código no puedes modificar.
  • Para propiedades "virtuales" cómodas en view models, UI, vistas calculadas.
  • Donde quieres una sintaxis bonita tipo .SomeCalculatedProperty sin paréntesis extra.

2. Nueva sintaxis en C# 14

En C# 14 decidieron dejar de usar la palabra clave this para extensiones. En vez de eso, introdujeron el operador especial extension(Type this). Y con él puedes añadir nuevas entidades virtuales a una clase.

Sintaxis antigua

public static class StringExtensions
{
    public static bool IsNullOrWhiteSpace(this string str)
        => string.IsNullOrWhiteSpace(str);
}

Nueva sintaxis


public static class Enumerable
{
    extension(TSource source)
    {
        // Miembros virtuales para el objeto
    }

    extension(TSource)
    {
        // Miembros estáticos virtuales
    }
}
Nueva sintaxis extension members (C# 14+)

¡Importante! Esta nueva sintaxis de extension solo funciona en C# 14+ y .NET 10+. Si usas .NET 9, no va a funcionar. Pero si quieres probarlo y .NET 10 aún no ha salido, instala la versión .NET 10 preview 5+
¡Nota! Nuestro curso de C# usa las funcionalidades más nuevas del lenguaje C# 14+, incluso las que todavía no han salido oficialmente. En otoño de 2025 saldrá .NET 10, y cuando termines el curso, tendrás los conocimientos más avanzados de C#! 😎

3. Sintaxis de propiedades de extensión

¿Cómo se ve?

Supón que quieres añadir al tipo DateTime una propiedad que devuelva si la fecha es un día de fin de semana:

public static class DateTimeExtensions
{
    public static bool IsWeekend(this DateTime date) => 
        date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday;
}

Pero esto es la versión antigua de Extension Method, y se llama así: myDate.IsWeekend()

Nuevo estilo — Extension Property


public static class DateTimeExtensions
{
    extension(DateTime source)
    {
        public bool IsWeekend // ¡propiedad de extensión!
        {
            get => source.DayOfWeek == DayOfWeek.Saturday || source.DayOfWeek == DayOfWeek.Sunday;
        }        
    }
}

Ahora puedes escribir así:

DateTime today = DateTime.Now;
if (today.IsWeekend)
{
    Console.WriteLine("¡Puedes dormir más!");
}

4. Tipos de extension properties

Puedes añadir no solo propiedades de solo lectura, sino también de lectura y escritura:


public static class DateTimeExtensions
{
    extension(DateTime source)
    {
        public int YearFrom1900 // ¡propiedad de extensión!
        {
            get => source.Year - 1900;
            set => source.Year = value + 1900; 
        }        
    }
}
Propiedad de extensión de lectura y escritura
Importante: Para las extension properties de lectura y escritura tienes que entender que modifican el propio objeto, pero no pueden guardar un "nuevo" valor en otro sitio (como un campo automático). Si quieres añadir una propiedad que físicamente no existe en el objeto, necesitas un storage aparte (por ejemplo, un diccionario), lo cual ya es más avanzado.

5. Propiedades estáticas

Gracias a la nueva sintaxis, puedes añadir propiedades virtuales no solo al objeto, ¡sino también a la clase! Sí, lo has entendido bien, ahora podemos añadir propiedades estáticas virtuales a la clase. La sintaxis es casi igual:


public static class DateTimeExtensions
{
    // Nueva sintaxis: extension block para DateTime (C# 14)
    extension(DateTime)
    {
        // Propiedad estática para guardar la zona horaria actual del usuario
        private static TimeZoneInfo _currentTimeZone = TimeZoneInfo.Local;

        // Propiedad de extensión para acceder a la zona horaria actual
        public static TimeZoneInfo CurrentTimeZone
        {
            get => _currentTimeZone;
            set => _currentTimeZone = value ?? TimeZoneInfo.Local;
        }

        // Devuelve la hora en la zona horaria del usuario
        public DateTime InCurrentTimeZone
        {
            get => TimeZoneInfo.ConvertTime(this, CurrentTimeZone);
        }
    }
}

6. Ejemplos: ¡ampliamos la clase Dog!

Vamos a seguir desarrollando nuestra app de perros, que llevamos arrastrando de lección en lección.

Supón que ahora tenemos no solo perros, sino también una lista de sus vacunas (List<DateTime>), pero la clase Dog viene de una librería externa y no podemos cambiar su código. Queremos añadir una propiedad: ¿Tiene todas las vacunas necesarias este año?

Forma antigua (método de extensión):

public static class DogExtensions
{
    public static bool AllVaccinatedThisYear(this Dog dog)
    {
        return dog.Vaccinations.Any(v => v.Year == DateTime.Now.Year);
    }
}
// Uso:
if (myDog.AllVaccinatedThisYear())
    Console.WriteLine("¡El perro está sano!");

Nueva forma (propiedad de extensión):


public static class DogExtensions
{
    extension(Dog)
    {
        public bool AllVaccinatedThisYear
        {
            get => Vaccinations.Any(v => v.Year == DateTime.Now.Year);
        }
    }
}

// Uso:
if (myDog.AllVaccinatedThisYear)
    Console.WriteLine("¡El perro está sano!");

Ejemplo completo

// Clase Dog — por ejemplo, de una librería externa:
public class Dog
{
    public string Name { get; set; }
    public List<DateTime> Vaccinations { get; set; } = new List<DateTime>();
}

// Clase de extensiones con extension block:
public static class DogExtensions
{
    extension(Dog)
    {
        public bool AllVaccinatedThisYear
        {
            get => Vaccinations.Any(v => v.Year == DateTime.Now.Year);
        }
    }
}

// En el programa:
var myDog = new Dog { Name = "Buddy" };
myDog.Vaccinations.Add(new DateTime(DateTime.Now.Year, 2, 16)); // vacuna este año

Console.WriteLine($"{myDog.Name}: ¿vacunada? {(myDog.AllVaccinatedThisYear ? "Sí" : "No")}");

7. Tabla: Extension Methods vs Extension Properties

Método de extensión Propiedad de extensión
Sintaxis de llamada obj.Method() obj.Property
Pasa parámetros Sí No (solo this obj)
Se puede escribir No aplicable Solo si está implementado
Cómodo para View/UI A veces Sí (binding bidireccional, lambda)
Visibilidad en reflexión No No

8. Trampas potenciales y errores típicos

Cuando las extension properties no ayudan

  • No pueden sustituir completamente a los campos reales: no tienen acceso a miembros privados del objeto.
  • No puedes añadir un cambio automático de estado del objeto (por ejemplo, no puedes controlar el cambio de valor en la clase original directamente).
  • No puedes usar extension property para implementar una interfaz o clase abstracta.

Error con los nombres

Si de repente en la clase original aparece una propiedad con el mismo nombre que la tuya, se usará la propiedad "nativa" y no tu extensión. Así que ten cuidado al elegir nombres, sobre todo cuando trabajas con tipos públicos de .NET.

Problemas con serialización/reflexión

Extension property no es un miembro "real" del tipo, sino solo azúcar sintáctico. Por eso, si usas reflexión o serialización, la propiedad de extensión puede no ser visible para esas herramientas.

2
Tarea
C# SELF, nivel 18, lección 3
Bloqueada
Creación de una propiedad de extensión para comprobar si un año es bisiesto
Creación de una propiedad de extensión para comprobar si un año es bisiesto
2
Tarea
C# SELF, nivel 18, lección 3
Bloqueada
Creación de una propiedad de extensión para string
Creación de una propiedad de extensión para string
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION