Web Log di Adrian Florea

"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
posts - 440, comments - 2715, trackbacks - 3944

My Links

Archives

Post Categories

Image Galleries

.RO Blogs

.RO People

.RO Sites

Blogs

Furls

Links

vinCitori

Default constructor and not nullable value type generic parameter constraints

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
class ObjectFactory<T> where T : class, new() {
    public T Construct() {
          return new T();
     }
}
Class ObjectFactory(Of T As New)
    Public Function Construct() As T
        Return New T
    End Function
End Class

Il codice IL corrispondente è questo:

.maxstack 1
.locals init (!T V_0)
call !!0 [mscorlib]System.Activator::CreateInstance<!T>()
stloc.0
br.s label1
label1: ldloc.0
ret

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:

.maxstack 2
.locals init (!T V_0, !T V_1)
nop
ldloca.s V_1
initobj !T
ldloc.1
box !T
brfalse.s label1
ldloca.s V_1
initobj !T
ldloc.1
br.s label2
label1: call !!0 [mscorlib]System.Activator::CreateInstance<!T>()
label2: stloc.0
br.s label3
label3: ldloc.0
ret

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.

Print | posted on lunedì 6 novembre 2006 01:01 | Filed Under [ Carillon .NET ]

Powered by:
Powered By Subtext Powered By ASP.NET