2009-09-09 7 views
11

Quelqu'un peut-il savoir pourquoi une méthode générique qui contraint T à classer aurait des instructions de boxing dans le code MSIL? J'ai été assez surpris par cela puisque sûrement T étant contraint à un type de référence, le code généré ne devrait pas avoir besoin d'effectuer de boxe.Pourquoi la méthode générique avec la contrainte de T: class aboutit à la boxe?

Voici le code C#:

protected void SetRefProperty<T>(ref T propertyBackingField, T newValue) where T : class 
{ 
    bool isDifferent = false; 

    // for reference types, we use a simple reference equality check to determine 
    // whether the values are 'equal'. We do not use an equality comparer as these are often 
    // unreliable indicators of equality, AND because value equivalence does NOT indicate 
    // that we should share a reference type since it may be a mutable. 

    if (propertyBackingField != newValue) 
    { 
     isDifferent = true; 
    } 
} 

Voici l'IL généré:

.method family hidebysig instance void SetRefProperty<class T>(!!T& propertyBackingField, !!T newValue) cil managed 
{ 
    .maxstack 2 
    .locals init (
     [0] bool isDifferent, 
     [1] bool CS$4$0000) 
    L_0000: nop 
    L_0001: ldc.i4.0 
    L_0002: stloc.0 
    L_0003: ldarg.1 
    L_0004: ldobj !!T 
    L_0009: box !!T 
    L_000e: ldarg.2 
    L_000f: box !!T 
    L_0014: ceq 
    L_0016: stloc.1 
    L_0017: ldloc.1 
    L_0018: brtrue.s L_001e 
    L_001a: nop 
    L_001b: ldc.i4.1 
    L_001c: stloc.0 
    L_001d: nop 
    L_001e: ret 
} 

Notez la boîte !! T instructions.

Quelqu'un a-t-il une idée de la raison pour laquelle cela est généré?

Quelqu'un a des idées pour éviter cela?

Merci, Phil

+1

Jon est sorti je suppose :-) – Peter

+7

J'ai trouvé ta réponse et c'était une copie! Grande question, au fait :) S'il vous plaît voir http://stackoverflow.com/questions/646517/boxing-when-using-generics-in-c –

+3

L'essentiel de la réponse que j'ai liée est qu'une instruction de boxe sur une référence le type est effectivement un nop. Cela permet au compilateur d'émettre librement des instructions de boxe qui peuvent être supprimées par le JIT pour les types construits fermés qui ont été créés avec un type de référence en tant qu'argument de type générique. Dans votre cas (puisque 'T' est contraint comme type de référence), aucune des deux instructions de boxe qui ont été émises ne sera exécutée. –

Répondre

2

Vous n'avez pas à vous soucier des performances-dégradations de l'instruction box car si son argument est un type de référence, l'instruction box ne fait rien. Bien qu'il soit encore étrange que l'instruction box ait même été créée (peut-être paresseux/conception plus facile à la génération de code?).

0

je crois que cela est prévu par la conception. Vous n'êtes pas en train de contraindre T à une classe spécifique, il est donc très probable qu'il ne soit pas converti en objet. D'où la raison pour laquelle vous voyez l'IL inclure la boxe.

Je voudrais essayer ce code avec lequel T: ActualClass

+3

Si vous faites T: ActualClass, pourquoi s'embêter avec les génériques? –

+0

Parce que vous pouvez contraindre T à des niveaux plus élevés ... comme iSomeInterface ... –

+1

Chris, si T était un objet, n'aurait-il pas déjà été mis en boîte avant de pousser sur la pile? Pourquoi alors une opération de boxe doit-elle être effectuée? Je m'attendrais à ce que l'opérateur == vérifie l'égalité de référence si T était un objet, donc cela ne nécessiterait pas non plus d'opérations de Un/Boxing. – Phil

1

Je ne sais pas pourquoi la boxe est une ocurring. Une façon possible d'éviter la boxe est de ne pas l'utiliser. Juste recompiler sans la boxe. Ex:

.assembly recomp_srp 
{ 
    .ver 1:0:0:0 
} 

.class public auto ansi FixedPBF 
{ 

.method public instance void .ctor() cil managed 
{ 

} 

.method hidebysig public instance void SetRefProperty<class T>(!!T& propertyBackingField, !!T newValue) cil managed 
{ 
    .maxstack 2  
     .locals init (bool isDifferent, bool CS$4$0000) 

     ldc.i4.0 
     stloc.0 
     ldarg.1 
     ldobj !!T 
     ldarg.2 
     ceq 
     stloc.1 
     ldloc.1 
     brtrue.s L_0001 
     ldc.i4.1 
     stloc.0 
     L_0001: ret 

} 

} 

... si vous enregistrez un fichier recomp_srp.msil vous pouvez simplement recompiler en tant que tel:

ildasm/dll recomp_srp.msil

et il fonctionne OK sans la boxe sur ma fin:

 FixedPBF TestFixedPBF = new FixedPBF(); 

     TestFixedPBF.SetRefProperty<string>(ref TestField, "test2"); 

... Bien sûr, je l'ai changé de protection pour le public, vous devez faire le changement de nouveau et de fournir le reste de votre mise en œuvre.

Questions connexes