4

次のジェネリックメソッド用に生成されたMSILコードを調べてみましょう。

public static U BoxValue<T, U>(T value)
  where T : struct, U
  where U : class
{
  return value;
}

見て:

.method public hidebysig static !!U  BoxValue<valuetype .ctor
 ([mscorlib]System.ValueType, !!U) T,class U>(!!T 'value') cil managed
{
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  box        !!T
  IL_0006:  unbox.any  !!U
  IL_000b:  ret
}

ただし、上記のジェネリックコードの場合、より効率的なIL表現は次のようになります。

  IL_0000:  ldarg.0
  IL_0001:  box        !!T
  IL_0006:  ret

制約から、値が参照型にボックス化されていることがわかります。オペコードの後、ILスタックの値はすでにへの有効な参照になり、ボックスを解除せずに使用できるUnbox.anyため、オペコードは完全に冗長です。box!!U

C#3.0コンパイラが制約メタデータを使用してより効率的なジェネリックコードを出力しないのはなぜですか?Unbox.anyは小さなオーバーヘッド(わずか4倍から5倍遅い)を与えますが、このシナリオでより良いコードを出力しないのはなぜですか?

4

2 に答える 2

7

ベリファイアにいくつかの問題があるため、コンパイラがこれを行っているようです。

コンパイラに生成させたいILは検証できないため、C#コンパイラはそれを生成できません(「安全でない」コンテキスト以外のすべてのC#コードは検証可能である必要があります)。

「検証タイプの互換性」のルールは、Ecma仕様のセクション1.8.1.2.3、パートIIIに記載されています。

タイプ「S」は、次のルールを使用して、タイプ「T」または(S:= T)と互換性のある検証であると言われています。

  1. [:=反射的]すべての検証タイプS、S:= S
  2. [:=推移的]すべての検証タイプS、T、およびUで、S:= TおよびT:= Uの場合、S:=U。
  3. S:= T SがTの基本クラスであるか、Tによって実装されたインターフェースであり、Tが値型でない場合。
  4. object:= T(Tがインターフェースタイプの場合)。
  5. S:= T SとTが両方ともインターフェースであり、Tの実装にSの実装が必要な場合
  6. S:=Sがオブジェクトタイプまたはインターフェイスの場合はnull
  7. S []:= T [] S:= Tであり、配列が両方ともベクトル(ゼロベース、ランク1)であるか、どちらもベクトルではなく、両方が同じランクである場合。(このルールは配列の共分散を扱います。)
  8. SとTがメソッドポインタの場合、シグニチャ(戻り型、パラメータ型、呼び出し規約)が同じであれば、S:=Tです。

これらのルールのうち、この場合に適用できるのは#3だけです。

ただし、「U」は「T」の基本クラスではなく、「T」の基本インターフェイスでもないため、#3はコードに適用されません。したがって、「または」チェックはfalseを返します。

これは、ベリファイアを通過する方法でボックス化されたTをUに変換するために、いくつかの命令を実行する必要があることを意味します。

必要なコードを生成して実際に検証できるように、検証ルールを変更する必要があることに同意します。

ただし、技術的には、コンパイラはECMA仕様に基づいて「正しい」ことを実行しています。

Microsoftの誰かにバグを報告する必要があります。

于 2009-11-14T19:20:41.410 に答える
3

これらの制約は奇妙に見えます:

where T : struct, U
where U : class

Tは値型ですが、同時に参照型であるUから継承する必要があります。どのタイプが上記の制約を満たし、このメソッドを呼び出すことができるのだろうか。

于 2009-11-02T13:12:32.803 に答える