This post originated from an RSS feed registered with .NET Buzz
by Adrian Florea.
Original Post: Default constructor and not nullable value type generic parameter constraints
Feed Title: Web Log di Adrian Florea
Feed URL: /error.aspx?aspxerrorpath=/adrian/Rss.aspx
Feed Description: "You know you've achieved perfection in design, not when you have nothing more to add, but when you have nothing more to take away." Antoine de Saint-Exupery
Questo post di David Hayden, segnalato da Michele, che riprende un paragrafo da questo libro (non l'ho ancora visto nelle librerie ma un altro libro di Christian Gross mi è piaciuto molto) mi ha incuriosito perché sia in C# (CS0451) che in VB.NET (BC32103) è impossibile avere insieme una constraint struct/Structure e una constraint new()/New. E quindi mi sono chiesto come mai "without the class constraint, the compiler doesn't know if the type T is a value type or reference type and hence has to check for both"? Se struct e new() non possono stare insieme non significa che il type parameter è obbligatoriamente un reference class? No! Possiamo tranquillamente passare dei value type a un type parameter vincolato da new(). Cioè, è lecito avere istanze di Foo<int> se abbiamo definito class Foo<T> where T: new(){}.
I seguenti due snippet, in C# respettivamente VB.NET, generano per il corpo del metodo Construct lo stesso codice IL:
C#
VB.NET
classObjectFactory<T> where T : class,new() { public T Construct() { returnnew T(); } }
Class ObjectFactory(Of T AsNew) PublicFunction Construct() As T ReturnNew T EndFunction EndClass
dove si nota la chiamata al System.Activator.CreateInstance<T>(). Se T è un reference type (vedi la constraint class), questa chiamata al CreateInstance ci sta benissimo, ma se T fosse un value type (lo snippet VB.NET sopra lo permeterebbe!) non avrebbe senso come performance. Ed ecco che il compilatore C#, togliendo via la constraint class, genera il seguente codice IL:
che, nel caso di un type parameter value type, istanzia tramite initobj anziché CreateInstance. Quello che sembrava a uno sguardo superficiale un overhead, si mostra essere in realtà un'ottimizzazione! E allora perché non possiamo avere struct e new() insieme? Secondo me, perché i compilatori in discussione (C# e VB.NET) non permettono di definire costruttori default per le struct. Il fatto che per una struct Foo possiamo scrivere new Foo(), non vuol dire che così chiamiamo il costruttore senza parametri della Foo ma, semplicemente che inizializziamo la Foo, ovvero non dobbiamo confondere una questione di sintassi con una questione di semantica.
Per l'ennesima volta, il compilatore C# si mostra, IMHO, più saggio.