C# - 런타임 시점에 이벤트 핸들러를 만들어 Reflection을 이용해 구독하는 방법
간단하게 이벤트 하나를 제공하는 타입을 만들어,
public class TempEventArgs : EventArgs
{
public string Name;
}
public class MyTemp
{
public void Create()
{
Created(this, new TempEventArgs { Name = "MyTEmp" });
}
public event EventHandler<TempEventArgs> Created;
}
Reflection을 이용하면 다음과 같이 이벤트 핸들러를 Reflection으로 추가할 수 있습니다.
using System;
using System.Reflection;
class Program
{
static void Main(string[] args)
{
MyTemp instance = new MyTemp();
MyTempEventTest(instance);
instance.Create();
}
private static void Instance_Created(object sender, TempEventArgs e)
{
Console.WriteLine("Event Fired: " + e.Name);
}
private static void MyTempEventTest(object objTarget)
{
EventInfo ei = typeof(MyTemp).GetEvent("Created", BindingFlags.Public | BindingFlags.Instance);
ei.AddEventHandler(objTarget, (EventHandler<TempEventArgs>)Instance_Created);
}
}
여기까지만 하면 재미가 없으니, 상황을 좀 꼬아서 이벤트 핸들러까지 동적으로 런타임에 만들어 구독을 추가해 보겠습니다. 사실 이렇게까지 만들 일은 거의 없지만 그래도 DynamicMethod의 예제로써,
How to: Define and Execute Dynamic Methods
; https://learn.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/how-to-define-and-execute-dynamic-methods
나름 괜찮으니 ^^ 이를 이용해 다음과 같이 IL 코드를 직접 추가해 이벤트 핸들러를 동적으로 만들 수 있습니다.
static void Main(string[] args)
{
MyTemp instance = new MyTemp();
SubscribeAtRuntime(instance);
instance.Create();
}
private static void SubscribeAtRuntime(MyTemp instance)
{
Type targetType = instance.GetType();
EventInfo ei = targetType.GetEvent("Created", BindingFlags.Public | BindingFlags.Instance);
MethodInfo consoleWriteMethod = typeof(Console).GetMethod("Write", BindingFlags.Public | BindingFlags.Static, null, new Type [] { typeof(object) }, null);
MethodInfo consoleWriteLineMethod = typeof(Console).GetMethod("WriteLine", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(object) }, null);
FieldInfo nameField = typeof(TempEventArgs).GetField("Name", BindingFlags.Public | BindingFlags.Instance);
DynamicMethod proxyMethod = new DynamicMethod(
"Proxy_Created",
typeof(void), new Type[] { typeof(object), typeof(TempEventArgs) });
ILGenerator il = proxyMethod.GetILGenerator();
// Console.Write("Event Fired: " );
il.Emit(OpCodes.Ldstr, "Event Fired: ");
il.Emit(OpCodes.Call, consoleWriteMethod);
// Console.WriteLine(e.Name);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldfld, nameField);
il.Emit(OpCodes.Call, consoleWriteLineMethod);
il.Emit(OpCodes.Ret);
Type eventHandlerType = typeof(EventHandler<TempEventArgs>);
Delegate proxyDelegate = proxyMethod.CreateDelegate(eventHandlerType);
ei.AddEventHandler(instance, proxyDelegate);
}
위에서 DynamicMethod.CreateDelegate 메서드에,
DynamicMethod.CreateDelegate Method
; https://learn.microsoft.com/en-us/dotnet/api/system.reflection.emit.dynamicmethod.createdelegate
전달한 타입은 "System.EventHandler`1[TempEventArgs]"입니다. 그리고 이 타입은 EventInfo 인스턴스를 통해서도 구할 수 있는데, 따라서 다음과 같이 전달하는 것도 가능합니다.
Delegate proxyDelegate = proxyMethod.CreateDelegate(ei.EventHandlerType);
ei.AddEventHandler(instance, proxyDelegate);
(
첨부 파일은 이 글의 예제 코드를 포함합니다.)
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]