私はフレームワークを書いています。その1つの機能には、イベントをリモートで処理することが含まれます(詳細は現時点では関係ないと思います)。ただし、必要なのは、ターゲットオブジェクトの参照とイベント名を指定して、イベントハンドラーを任意のイベントにフックする機能です。
イベントを処理するメソッドは、パラメーターをマーシャリングするか、そうでない場合はハンドラーコードにプロキシし、リターンタイプをイベントハンドラーのリターンタイプにキャストします。事実上、ハンドラーをラップします。
イベントハンドラーには任意のタイプのパラメーターがいくつも必要になる可能性があるため、イベントをオンデマンドでフックするリクエストを処理する唯一の方法は、リフレクション/エミット(コード生成)を使用してビルドすることだと思いますカスタムプロキシメソッド。私はこの質問を投稿して、その仮定を確認し、誰かが他の/より良いアイデアを持っているかどうかを確認します。
現在、私はを作成しDynamicMethod
、次にこの種のことを行います。動作します-ほぼ-ですが、この動的プロキシメソッドが呼び出すラッパーメソッドは静的である必要があります(宣言はほとんどpublic static object GenericHandler(object[] parameters)
です)。なりたくない。したがって、私の現在のタスクは、このメソッドを使用して型全体を動的に作成することですが、ラッパーを呼び出すオブジェクトのインスタンスを格納するフィールドも作成することです。しかし、それは重要なことではありません(私は思います)-動的コードを作成する必要があるという私の仮定が正しいかどうかを確認したいと思います。しかし、参考のために:
// obj = target object, eventName = event name
var evtInfo = obj.GetType().GetEvent(eventName);
var handlerType = evtInfo.EventHandlerType;
var eventInvokeMethod = handlerType.GetMethod("Invoke");
var paramTypes = eventInvokeMethod.GetParameters().Select(p => p.ParameterType).ToArray();
var returnType = eventInvokeMethod.ReturnType;
var handlerMethod = new DynamicMethod(
"evtHandler_" + eventName,
MethodAttributes.Static | MethodAttributes.Public,
CallingConventions.Standard,
returnType,
paramTypes,
typeof(my container class).Module,
false);
var il = handlerMethod.GetILGenerator();
// locals: [0] = parameter array
il.DeclareLocal(typeof(object[]));
// create an appropriately-sized array objects, for the parameters
il.Emit(OpCodes.Ldc_I4, (int)paramTypes.Count());
il.Emit(OpCodes.Newarr, typeof(object));
il.Emit(OpCodes.Stloc_0);
for (int i = 0; i < paramTypes.Length; i++)
{
// for the array: load the parameter into the same index of the array
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldc_I4, i);
il.Emit(OpCodes.Ldarg, i);
if (!paramTypes[i].IsClass)
{
// box the value first..
il.Emit(OpCodes.Box, paramTypes[i]);
}
il.Emit(OpCodes.Stelem_Ref);
}
// call our generic handler with the array of parameters, then convert the return
// value to the target type via unboxing or a cast
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Call, wrapper.GetMethodInfo());
if (!returnType.IsClass)
{
il.Emit(OpCodes.Unbox_Any, returnType);
}
else
{
il.Emit(OpCodes.Castclass, returnType);
}
il.Emit(OpCodes.Ret);
応答がなく、ラッパータイプが機能している上記の理論が得られた場合は、更新されたコードを投稿します。ただし、このメソッドの代替案が存在する場合は、ぜひお知らせください。