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
}
}
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;
}
}
}
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 | Sì | 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.
GO TO FULL VERSION