2009-09-09 6 views
11

Ktoś ma pojęcia, dlaczego ogólny sposób, który ogranicza T do klasy miałby instrukcje bokserskie w generuje kod MSIL?Dlaczego metoda generyczna ze zmienną T: class powoduje boksowanie?

Byłem bardzo zaskoczony tym, ponieważ na pewno, ponieważ T jest ograniczony do typu referencyjnego wygenerowany kod nie musi wykonywać żadnego boksu.

Oto kod 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; 
    } 
} 

Oto generowane IL:

.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 
} 

Wskazówka pole !! T instrukcje.

Ktoś ma pojęcie, dlaczego jest generowany?

Ktoś ma pomysły, jak tego uniknąć?

Dzięki Phil

+1

Jon jest obecnie zgaduję :-) – Peter

+7

Znalazłem twoją odpowiedź i był to duplikat! Świetne pytanie, nawiasem mówiąc :) Proszę zobacz http://stackoverflow.com/questions/646517/boxing-when-using-generics-in-c- –

+3

Istotą odpowiedzi, którą podjąłem jest to, że instrukcja boksu na odniesienie typ jest efektywnie nop. Dzięki temu kompilator może swobodnie emitować instrukcje boksu, które mogą być usuwane przez JIT dla zamkniętych typów konstrukcyjnych, które zostały utworzone z typem odniesienia jako typowy argument typu. W twoim przypadku (ponieważ 'T' jest ograniczony jako typ odniesienia) żadna z dwóch wysłanych instrukcji boksu nie zostanie uruchomiona. –

Odpowiedz

2

Nie musisz się martwić o jakiekolwiek pogorszenie wydajności z instrukcji box, ponieważ jeśli jej argument jest typem odniesienia, instrukcja box nic nie robi. Chociaż wciąż jest dziwne, że została stworzona instrukcja box (może lenistwo/łatwiejszy projekt przy generowaniu kodu?).

0

Wierzę, że to jest zamierzone przez projekt. Nie ograniczasz T do konkretnej klasy, więc najprawdopodobniej nie rzuca ona jej na obiekt. Stąd dlaczego widzisz, że IL zawiera boks.

chciałbym spróbować tego kodu z where T: ActualClass

+3

Jeśli robisz T: ActualClass, po co zawracać sobie głowy rodzajami leków? –

+0

Ponieważ możesz ograniczyć T do wyższych poziomów ... jak iSomeInterface ... –

+1

Chris, jeśli T byłby obiektem, czy nie byłby już zapakowany przed pchnięciem na stos? Dlaczego więc musiałaby zostać wykonana operacja boksu? Oczekuję od operatora == sprawdzenia równości referencyjnej, jeśli T był obiektem, więc nie wymagałoby to również operacji un/boxing. – Phil

1

Nie jestem pewien, dlaczego każdy boks jest ocurring. Jednym z możliwych sposobów uniknięcia boksowania jest nieużywanie go. Tylko rekompiluj bez boksowania. 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 

} 

} 

... jeśli zapiszesz do pliku recomp_srp.msil można po prostu skompilować jako takie:

ildasm/dll recomp_srp.msil

i działa OK bez boksu na mój koniec:

 FixedPBF TestFixedPBF = new FixedPBF(); 

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

... oczywiście, zmieniłem go od chronione publicznego, trzeba by ponownie dokonać zmiany i podać resztę realizacji.

Powiązane problemy