base
を使用してC#では唯一の即時ベースで動作します。ベースベースのメンバーにアクセスすることはできません。
ILで実行できることについて、他の人が私にパンチを打つように見えます。
しかし、私はコードgenをやったやり方にはいくつかの利点があると思うので、とにかく投稿します。
私が違ったのは、C#コンパイラを使ってオーバーロード解決と汎用引数の置換を行うための式ツリーを使用することです。
そのようなものは複雑であり、あなたがそれを手助けすることができればあなた自身を複製する必要はありません。 あなたのケースでは、コードは次のように動作します:
var del =
CreateNonVirtualCall<Program, BaseClass, Action<ThirdClass>>
(
x=>x.SayNo()
);
あなたは一度だけ、それをコンパイルする必要がなるようにあなたはおそらく、読み取り専用の静的フィールドにデリゲートを保存するとよいでしょう。
あなたは3つのジェネリック引数を指定する必要があります。
所有者のタイプを - これは、あなたが「CreateNonVirtualCall」を使用していなかった場合のコードを呼び出していたクラスです。
ベースクラス - これは、あなたが
デリゲート型から非仮想呼び出しを作りたいクラスです。これは、 "this"引数に追加のパラメータを指定して呼び出されるメソッドのシグニチャを表す必要があります。これを排除することは可能ですが、code genメソッドでもっと多くの作業が必要になります。
このメソッドは、単一の引数、呼び出しを表すラムダを取ります。それはコールでなければならず、コールでなければなりません。あなたがコードgenを拡張したいなら、より複雑なものをサポートすることができます。
簡潔にするため、ラムダ本体はラムダパラメータにしかアクセスできないように制限されており、ラムダ本体をラムダパラメータに直接渡すことができます。すべての式タイプをサポートするようにメソッド本体のコードgenを拡張すると、この制限を取り除くことができます。しかし、それはいくつかの仕事を取るだろう。戻ってきたデリゲートで何でもできますので、制限はそれほど大きなものではありません。
このコードは完全ではありません。それはより多くの検証を使用することができ、式ツリーの制限のために "ref"または "out"パラメータでは機能しません。
voidメソッド、値を返すメソッド、および汎用メソッドを使用してサンプルケースでテストしていました。私は、しかし、あなたは動作しないいくつかのエッジケースを見つけることができると確信しています。いずれの場合においても
が、ここではILゲンコードです:あなたはBasClassを継承するためにあなたのクラスのいずれかのために
public static TDelegate CreateNonVirtCall<TOwner, TBase, TDelegate>(Expression<TDelegate> call) where TDelegate : class
{
if (! typeof(Delegate).IsAssignableFrom(typeof(TDelegate)))
{
throw new InvalidOperationException("TDelegate must be a delegate type.");
}
var body = call.Body as MethodCallExpression;
if (body.NodeType != ExpressionType.Call || body == null)
{
throw new ArgumentException("Expected a call expression", "call");
}
foreach (var arg in body.Arguments)
{
if (arg.NodeType != ExpressionType.Parameter)
{
//to support non lambda parameter arguments, you need to add support for compiling all expression types.
throw new ArgumentException("Expected a constant or parameter argument", "call");
}
}
if (body.Object != null && body.Object.NodeType != ExpressionType.Parameter)
{
//to support a non constant base, you have to implement support for compiling all expression types.
throw new ArgumentException("Expected a constant base expression", "call");
}
var paramMap = new Dictionary<string, int>();
int index = 0;
foreach (var item in call.Parameters)
{
paramMap.Add(item.Name, index++);
}
Type[] parameterTypes;
parameterTypes = call.Parameters.Select(p => p.Type).ToArray();
var m =
new DynamicMethod
(
"$something_unique",
body.Type,
parameterTypes,
typeof(TOwner)
);
var builder = m.GetILGenerator();
var callTarget = body.Method;
if (body.Object != null)
{
var paramIndex = paramMap[((ParameterExpression)body.Object).Name];
builder.Emit(OpCodes.Ldarg, paramIndex);
}
foreach (var item in body.Arguments)
{
var param = (ParameterExpression)item;
builder.Emit(OpCodes.Ldarg, paramMap[param.Name]);
}
builder.EmitCall(OpCodes.Call, FindBaseMethod(typeof(TBase), callTarget), null);
if (body.Type != typeof(void))
{
builder.Emit(OpCodes.Ret);
}
var obj = (object) m.CreateDelegate(typeof (TDelegate));
return obj as TDelegate;
}
つもりでした(SecondClassを言うのは)? –
いいえ。追加または変更するクラスはもうありません。 – henry000