7

Microsoft IL では、値の型でメソッドを呼び出すには、間接参照が必要です。「il」という名前の ILGenerator があり、現在、スタックの一番上に Nullable があるとします。値があるかどうかを確認したい場合は、次を出力できます。

var local = il.DeclareLocal(typeof(Nullable<int>));
il.Emit(OpCodes.Stloc, local);
il.Emit(OpCodes.Ldloca, local);
var method = typeof(Nullable<int>).GetMethod("get_HasValue");
il.EmitCall(OpCodes.Call, method, null);

ただし、それをローカル変数として保存することをスキップして、次のように、既にスタックにある変数のアドレスでメソッドを呼び出すだけでよいでしょう。

il.Emit(/* not sure */);
var method = typeof(Nullable<int>).GetMethod("get_HasValue");
il.EmitCall(OpCodes.Call, method, null);

命令の ldind ファミリ (特に ldind_ref) は有望に見えますが、これが値のボックス化を引き起こすかどうかを知るための十分なドキュメントが見つかりません。

私は C# コンパイラの出力を見てきましたが、ローカル変数を使用してこれを実現しているため、最初の方法が唯一の方法である可能性があります。誰にも良いアイデアはありますか?

**** 編集: 追記 ****

次のプログラムの行をコメントアウトした場合のように、メソッドを直接呼び出そうとしても機能しません (「操作によりランタイムが不安定になる可能性があります」というエラーが表示されます)。行のコメントを外すと、期待どおりに機能し、「True」が返されることがわかります。

var m = new DynamicMethod("M", typeof(bool), Type.EmptyTypes);
var il = m.GetILGenerator();
var ctor = typeof(Nullable<int>).GetConstructor(new[] { typeof(int) });
il.Emit(OpCodes.Ldc_I4_6);
il.Emit(OpCodes.Newobj, ctor);
//var local = il.DeclareLocal(typeof(Nullable<int>));
//il.Emit(OpCodes.Stloc, local);
//il.Emit(OpCodes.Ldloca, local);
var getValue = typeof(Nullable<int>).GetMethod("get_HasValue");
il.Emit(OpCodes.Call, getValue);
il.Emit(OpCodes.Ret);
Console.WriteLine(m.Invoke(null, null));

したがって、値型であるため、スタック上の値を使用してメソッドを単純に呼び出すことはできません (ただし、参照型の場合は可能です)。

私が達成したい (またはそれが可能かどうかを知りたい) のは、コメントアウトされている 3 行を置き換えることですが、一時的なローカルを使用せずにプログラムを動作させ続けることです。

4

4 に答える 4

2

私はそれを考え出した!幸いなことに、オペコードについて読んでいて、値のアドレスunboxをプッシュしていることに気付きました。実際の値をプッシュします。そのため、ローカル変数に格納せずに値型でメソッドを呼び出してからそのアドレスをロードするには、単に. を続けます。あなたの最後の例を使用して:unbox.anyboxunbox

var m = new DynamicMethod("M", typeof(bool), Type.EmptyTypes);
var il = m.GetILGenerator();
var ctor = typeof(Nullable<int>).GetConstructor(new[] { typeof(int) });
il.Emit(OpCodes.Ldc_I4_6);
il.Emit(OpCodes.Newobj, ctor);
il.Emit(OpCodes.Box, typeof(Nullable<int>)); // box followed by unbox
il.Emit(OpCodes.Unbox, typeof(Nullable<int>));
var getValue = typeof(Nullable<int>).GetMethod("get_HasValue");
il.Emit(OpCodes.Call, getValue);
il.Emit(OpCodes.Ret);
Console.WriteLine(m.Invoke(null, null));

これの欠点は、ボックス化によってボックス化されたオブジェクトのメモリ割り当てが発生するため、(既に割り当てられている) ローカル変数を使用するよりも少し遅くなることです。ただし、必要なすべてのローカル変数を決定、宣言、および参照する必要がなくなります。

于 2016-03-18T05:08:36.207 に答える
2

変数が既にスタックにある場合は、先に進んでメソッド呼び出しを発行できます。

コンストラクターは、変数を型付きの形式でスタックにプッシュしていないようです。IL を少し掘り下げた後、変数を構築した後に変数を使用する方法が 2 つあります。

コンストラクターを呼び出す前に参照を評価スタックに格納する変数をロードし、コンストラクターを呼び出した後にその変数を次のように再度ロードできます。

DynamicMethod method = new DynamicMethod("M", typeof(bool), Type.EmptyTypes);
ILGenerator il = method.GetILGenerator();
Type nullable = typeof(Nullable<int>);
ConstructorInfo ctor = nullable.GetConstructor(new Type[] { typeof(int) });
MethodInfo getValue = nullable.GetProperty("HasValue").GetGetMethod();
LocalBuilder value = il.DeclareLocal(nullable);         

// load the variable to assign the value from the ctor to
il.Emit(OpCodes.Ldloca_S, value);
// load constructor args
il.Emit(OpCodes.Ldc_I4_6);
il.Emit(OpCodes.Call, ctor);
il.Emit(OpCodes.Ldloca_S, value);

il.Emit(OpCodes.Call, getValue);
il.Emit(OpCodes.Ret);
Console.WriteLine(method.Invoke(null, null));

他のオプションは、あなたが示した方法でそれを行うことです。私が見ることができる唯一の理由は、ctor メソッドが void を返すため、他のメソッドのように値をスタックに置かないことです。新しいオブジェクトがスタックにない場合に Setloc を呼び出すことができるのは奇妙に思えます。

于 2008-09-16T20:02:13.673 に答える
1

オプションをさらに検討した後、それができないと仮定するのは正しいと思います。MSIL 命令のスタック動作を調べると、op がそのオペランドをスタックに残していないことがわかります。これは「スタック エントリのアドレスを取得する」操作の要件になるため、存在しないと確信しています。

これにより、dup+box または stloc+ldloca のいずれかが残ります。あなたが指摘したように、後者はおそらくより効率的です。

@greg: 多くの命令は結果をスタックに残しますが、オペランドをスタックに残す命令はありません。これは、「スタック要素アドレスを取得する」命令に必要です。

于 2008-09-18T07:10:03.560 に答える
0

OPが要求していることを実行するクラスを作成しました... C#コンパイラが生成するILコードは次のとおりです。

  IL_0008:  ldarg.0
  IL_0009:  ldarg.1
  IL_000a:  newobj     instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)
  IL_000f:  stfld      valuetype [mscorlib]System.Nullable`1<int32> ConsoleApplication3.Temptress::_X
  IL_0014:  nop
  IL_0015:  ret
于 2012-06-04T15:06:08.773 に答える