Domanda

    

Questa domanda ha già una risposta qui:

    
            
  •              Pugilato quando si usano i generici in C #                                      2 risposte                          
  •     
    

Perché un metodo generico che vincola la classe T dovrebbe avere istruzioni di inscatolamento nel codice MSIL genera?

Ne sono rimasto piuttosto sorpreso poiché sicuramente poiché T è vincolato a un tipo di riferimento, il codice generato non dovrebbe aver bisogno di eseguire alcun inscatolamento.

Ecco il codice 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;
    }
}

Ecco l'IL generato:

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

Nota la casella !! T istruzioni.

Perché questo viene generato?

Come evitarlo?

È stato utile?

Soluzione

Non devi preoccuparti di alcun peggioramento delle prestazioni dall'istruzione box perché se il suo argomento è un tipo di riferimento, l'istruzione box non fa nulla. Anche se è ancora strano che sia stata persino creata l'istruzione box (forse pigrizia / design più semplice alla generazione del codice?).

Altri suggerimenti

Non sono sicuro del motivo per cui sta insorgendo una boxe. Un modo possibile per evitare il pugilato è non usarlo. Basta ricompilare senza la boxe. Es:

.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
        FixedPBF TestFixedPBF = new FixedPBF();

        TestFixedPBF.SetRefProperty<string>(ref TestField, "test2");
) 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 } }

... se salvi in ??un file recomp_srp.msil puoi semplicemente ricompilare come tale:

ildasm / dll recomp_srp.msil

E funziona bene senza il pugilato da parte mia:

<*>

... ovviamente, l'ho cambiato da protetto a pubblico, dovresti apportare di nuovo la modifica e fornire il resto della tua implementazione.

Credo che questo sia inteso dal design. Non stai vincolando T a una classe specifica, quindi è molto probabile che venga lanciato verso l'oggetto. Ecco perché vedi che IL include la boxe.

Proverei questo codice con dove T: ActualClass

Seguendo un paio di punti. Innanzitutto, questo errore si verifica per entrambi i metodi in una classe generica con vincolo dove T: class e anche metodi generici con lo stesso vincolo ( in una classe generica o non generica). Non si verifica per un metodo non generico (altrimenti identico) che utilizza Object anziché T :

// static T XchgNullCur<T>(ref T addr, T value) where T : class =>
//              Interlocked.CompareExchange(ref addr, val, null) ?? value;
    .locals init (!T tmp)
    ldarg addr
    ldarg val
    ldloca tmp
    initobj !T
    ldloc tmp
    call !!0 Interlocked::CompareExchange<!T>(!!0&, !!0, !!0)
    dup 
    box !T
    brtrue L_001a
    pop 
    ldarg val
L_001a:
    ret 


// static Object XchgNullCur(ref Object addr, Object val) =>
//                   Interlocked.CompareExchange(ref addr, val, null) ?? value;
    ldarg addr
    ldarg val
    ldnull
    call object Interlocked::CompareExchange(object&, object, object)
    dup
    brtrue L_000d
    pop
    ldarg val
L_000d:
    ret

Nota alcuni problemi aggiuntivi con il primo esempio. Invece di semplicemente ldnull abbiamo una chiamata initobj estranea indirizzata inutilmente a una variabile locale in eccesso tmp .

La buona notizia, tuttavia, accennata a qui , è che nulla di tutto ciò conta. Nonostante le differenze nel codice IL generato per i due esempi precedenti, il x64 JIT genera un codice quasi identico per loro. Il seguente risultato è per .NET Framework 4.7.2 modalità di rilascio con ottimizzazione " non soppressa " ;.

 inserisci qui la descrizione dell'immagine

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top