CodeGym /Corsi /C# SELF /Membri Extension: proprietà

Membri Extension: proprietà

C# SELF
Livello 18 , Lezione 3
Disponibile

1. Introduzione

Prima potevi aggiungere solo Extension-metodi alle classi. Ma non potevi aggiungere una proprietà (property) come extension di una classe! Cioè, se volevi che ogni DateTime avesse una proprietà IsWeekend, dovevi farlo tramite un metodo, che non è sempre comodo — soprattutto se vuoi la sintassi date.IsWeekend invece di date.IsWeekend().

Con l’arrivo di C# 14 il sogno si è avverato: ora puoi scrivere proprietà extension in modo simile ai metodi. Ti permettono di aggiungere una nuova proprietà a un tipo esistente senza toccare il suo codice sorgente, e usarle come se fossero proprietà normali!

Dove è davvero utile?

  • Per tipi standard .NET e librerie di terze parti, dove non puoi modificare il codice.
  • Per proprietà "virtuali" comode in view-model, UI, viste calcolate.
  • Quando vuoi una sintassi pulita tipo .SomeCalculatedProperty senza parentesi inutili.

2. Nuova sintassi in C# 14

In C# 14 hanno deciso di abbandonare la parola chiave this per le extension. Invece hanno introdotto l’operatore speciale extension(Type this). E con questo puoi aggiungere nuove entità virtuali a una classe.

Vecchia sintassi

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

Nuova sintassi


public static class Enumerable
{
    extension(TSource source)
    {
        // Membri virtuali per l’oggetto
    }

    extension(TSource)
    {
        // Membri statici virtuali
    }
}
Nuova sintassi extension members (C# 14+)

Importante! Questa nuova sintassi extension funziona solo in C# 14+ e .NET 10+. Se usi .NET 9, non funzionerà per niente. Ma se vuoi provarla e .NET 10 non è ancora uscito, installa la versione .NET 10 preview 5+
Nota! Il nostro corso di C# usa le feature più nuove del linguaggio C# 14+, anche quelle che non sono ancora ufficiali. Nell’autunno 2025 uscirà .NET 10, e quando finirai il corso avrai le conoscenze più avanzate su C#! 😎

3. Sintassi delle proprietà extension

Come appare?

Supponiamo che vuoi aggiungere al tipo DateTime una proprietà che ti dice se la data è un giorno festivo:

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

Ma questo è il vecchio modo con Extension Method, e si chiama così: myDate.IsWeekend()

Nuovo stile — Extension Property


public static class DateTimeExtensions
{
    extension(DateTime source)
    {
        public bool IsWeekend // proprietà extension!
        {
            get => source.DayOfWeek == DayOfWeek.Saturday || source.DayOfWeek == DayOfWeek.Sunday;
        }        
    }
}

Ora puoi scrivere così:

DateTime today = DateTime.Now;
if (today.IsWeekend)
{
    Console.WriteLine("Si può dormire di più!");
}

4. Tipi di extension properties

Puoi aggiungere non solo proprietà read-only, ma anche read-write:


public static class DateTimeExtensions
{
    extension(DateTime source)
    {
        public int YearFrom1900 // proprietà extension!
        {
            get => source.Year - 1900;
            set => source.Year = value + 1900; 
        }        
    }
}
Proprietà extension read-write
Importante: Per le extension properties read-write devi capire che modificano l’oggetto stesso, ma non possono memorizzare un "nuovo" valore da qualche parte (come un campo automatico). Se vuoi aggiungere una proprietà che fisicamente non esiste nell’oggetto, serve uno storage separato (tipo un dizionario), che va oltre l’uso base.

5. Proprietà statiche

Grazie alla nuova sintassi, puoi aggiungere proprietà virtuali non solo all’oggetto, ma anche alla classe! Sì, hai capito bene, ora possiamo aggiungere proprietà statiche virtuali a una classe. La sintassi è quasi la stessa:


public static class DateTimeExtensions
{
    // Nuova sintassi: extension block per DateTime (C# 14)
    extension(DateTime)
    {
        // Proprietà statica per memorizzare il fuso orario corrente dell’utente
        private static TimeZoneInfo _currentTimeZone = TimeZoneInfo.Local;

        // Proprietà extension per accedere al fuso orario corrente
        public static TimeZoneInfo CurrentTimeZone
        {
            get => _currentTimeZone;
            set => _currentTimeZone = value ?? TimeZoneInfo.Local;
        }

        // Restituisce l’ora nella zona oraria dell’utente
        public DateTime InCurrentTimeZone
        {
            get => TimeZoneInfo.ConvertTime(this, CurrentTimeZone);
        }
    }
}

6. Esempi: estendiamo la classe Dog!

Continuiamo a sviluppare la nostra app con i cani, che portiamo avanti da lezione a lezione.

Ora abbiamo non solo i cani, ma anche una lista delle loro vaccinazioni (List<DateTime>), ma la classe Dog viene da una libreria esterna e non possiamo modificarne il codice. Vogliamo aggiungere una proprietà: Tutti i vaccini necessari sono stati fatti quest’anno?

Vecchio modo (extension method):

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("Il cane è sano!");

Nuovo modo (extension property):


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

// Uso:
if (myDog.AllVaccinatedThisYear)
    Console.WriteLine("Il cane è sano!");

Esempio completo

// Classe Dog — per esempio, da una libreria esterna:
public class Dog
{
    public string Name { get; set; }
    public List<DateTime> Vaccinations { get; set; } = new List<DateTime>();
}

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

// Nel programma:
var myDog = new Dog { Name = "Druzhok" };
myDog.Vaccinations.Add(new DateTime(DateTime.Now.Year, 2, 16)); // vaccino quest’anno

Console.WriteLine($"{myDog.Name}: vaccinato? {(myDog.AllVaccinatedThisYear ? "Sì" : "No")}");

7. Tabella: Extension Methods vs Extension Properties

Extension Method Extension Property
Sintassi di chiamata obj.Method() obj.Property
Passa parametri No (solo this obj)
Si può scrivere Non applicabile Solo se implementato
Comodo per View/UI A volte Sì (binding bidirezionale, lambda)
Visibilità in reflection No No

8. Trappole potenziali ed errori tipici

Quando le extension property non aiutano

  • Non possono sostituire completamente i veri campi: non hanno accesso ai membri privati dell’oggetto.
  • Non puoi aggiungere un cambiamento di stato automatico dell’oggetto (ad esempio, non puoi gestire direttamente il cambiamento di valore nella classe originale).
  • Non puoi usare una extension property per implementare un’interfaccia o una classe astratta.

Errore con i nomi

Se nella classe originale appare una proprietà con lo stesso nome della tua, verrà usata quella "nativa" e non la tua extension. Quindi fai attenzione nella scelta dei nomi, soprattutto quando lavori con tipi pubblici di .NET.

Problemi con serializzazione/reflection

Una extension property non è un vero membro del tipo, ma solo una comodità sintattica. Quindi, se usi reflection o serializzazione, la proprietà extension potrebbe non essere visibile a questi strumenti.

Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION