T:クラスの制約を持つジェネリックメソッドがボクシングを行うのはなぜですか? [複製]

StackOverflow https://stackoverflow.com/questions/1400414

  •  05-07-2019
  •  | 
  •  

質問

    

この質問にはすでに回答があります:

         

Tをクラスに制約する一般的なメソッドでは、MSILコードの生成にボクシング命令が含まれるのはなぜですか?

確かにTは参照型に制限されているため、生成されたコードはボクシングを実行する必要がないため、これには非常に驚きました。

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

生成された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<*>)
    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 
}

ボックス!! T の指示に注意してください。

これが生成される理由

これを避ける方法

役に立ちましたか?

解決

引数が参照型である場合、 box 命令は何もしないため、 box 命令のパフォーマンスの低下について心配する必要はありません。 box 命令が作成されたのはまだ奇妙ですが(コード生成時の怠zyさやデザインのしやすさなど)。

他のヒント

ボクシングが発生している理由がわかりません。ボクシングを回避する1つの可能な方法は、ボクシングを使用しないことです。ボクシングなしで再コンパイルするだけです。例:

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

...ファイルrecomp_srp.msilに保存する場合は、次のように単純に再コンパイルできます。

ildasm / dll recomp_srp.msil

そして、私の終わりにボクシングなしで正常に実行されます:

<*>

...もちろん、保護から公開に変更しました。変更を元に戻し、残りの実装を提供する必要があります。

これは設計によるものです。 Tを特定のクラスに制限していないので、ほとんどの場合、オブジェクトにキャストします。したがって、ILにボクシングが含まれているのはなぜですか。

T:ActualClassでこのコードを試します

いくつかの点をフォローアップします。まず、このバグは、制約 where T:class を持つ generic class の両方のメソッドと、同じ制約を持つ genericメソッドで発生します(ジェネリッククラスまたは非ジェネリッククラス)。 T

の代わりに Object を使用する(他の点では同一の)非汎用メソッドでは発生しません。
// 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

最初の例でいくつかの追加の問題に注意してください。単に ldnull する代わりに、余分なローカル変数 tmp を無意味にターゲットとする無関係な initobj 呼び出しがあります。

ただし、こちらで示唆されているように、これは重要ではありません。上記の2つの例で生成されたILコードには違いがありますが、 x64 JIT はほぼ同じコードを生成します。次の結果は、.NET Framework 4.7.2 リリースモードで、最適化&quot; not-suppressed&quot;を使用した場合です。

ここに画像の説明を入力してください

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top