发生事件时调用实例方法

时间:2011-11-14 13:10:41

标签: .net-assembly

这是我的情况。我需要订阅一个在编译时我不知道的类型的事件。所以,我试图动态订阅这种类型。这不像创建EventHandler类型的委托那么简单,因为它并不总是正确的类型。

所以,这是我第一次进入Reflection.Emit命名空间,我需要一些帮助。这就是我到目前为止所做的:

private Delegate CreateDynamicClosedDelegate(Type eventHandlerType)
{
    var handler = new DynamicMethod(string.Empty, null, GetDelegateParameterTypes(eventHandlerType));

    ILGenerator ilgen = handler.GetILGenerator();

    var onTargetWindowClosedMethodInfo = GetType().GetMethod("OnTargetWindowClosed", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

    // this.OnTargetWindowClosed
    ilgen.Emit(OpCodes.Ldarg_0);
    ilgen.Emit(OpCodes.Call, onTargetWindowClosedMethodInfo);
    ilgen.Emit(OpCodes.Pop);
    ilgen.Emit(OpCodes.Ret);

    return handler.CreateDelegate(eventHandlerType);
}

如您所见,我正在为特定事件类型(在运行时确定)创建委托。我想我几乎就在那里,我只需要得到#34;这个。"一部分。

如果我理解正确的话,ilgen.Emit(OpCodes.Ldarg_0);应该在堆栈上加载实例。但是,由于它是一个静态事件处理程序,我认为第一个参数是sender,它不是包含该方法的实例。

最后,我正在尝试制作此代码:

window.Closed += (sender, e) => this.OnTargetWindowClosed();

订阅活动超出范围(这很容易),但我该如何创建此方法:

this.OnTargetWindowClosed();

1 个答案:

答案 0 :(得分:2)

如果要使用DynamicMethod在堆栈上指定实例,则必须使用其他构造函数重载。所有者类型是您实例的类型。

public DynamicMethod(string name, Type returnType, Type[] parameterTypes, Type owner);

创建委托时,必须使用此重载,目标参数是您的实例。

public Delegate CreateDelegate(Type delegateType, object target);

在您的情况下,它看起来像这样:

private Delegate CreateDynamicClosedDelegate(Type eventHandlerType)
{
    var handler = new DynamicMethod(string.Empty, null, GetDelegateParameterTypes(eventHandlerType), this.GetType());

    ILGenerator ilgen = handler.GetILGenerator();

    var onTargetWindowClosedMethodInfo = GetType().GetMethod("OnTargetWindowClosed", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

    // this.OnTargetWindowClosed
    ilgen.Emit(OpCodes.Ldarg_0);
    ilgen.Emit(OpCodes.Call, onTargetWindowClosedMethodInfo);
    ilgen.Emit(OpCodes.Pop);
    ilgen.Emit(OpCodes.Ret);

    return handler.CreateDelegate(eventHandlerType, this);
}

如果你正在使用silverlight,你可以这样做:

public class Dict : Dictionary<int, WindowLogic>
{
    public WindowLogic Get(int key)
    {
        return this[key];
    }
}

public class WindowLogic : IDisposable
{
    static public readonly Dict Instances = new Dict();

    static private int s_increment = 0;

    private int _increment;

    public WindowLogic()
    {
        lock (Instances)
        {
            Instances.Add(_increment = ++s_increment, this);
        }
    }

    private Delegate CreateDynamicClosedDelegate(Type eventHandlerType)
    {
        var handler = new DynamicMethod(string.Empty, null, GetDelegateParameterTypes(eventHandlerType));

        ILGenerator ilgen = handler.GetILGenerator();

        var onTargetWindowClosedMethodInfo = GetType().GetMethod("OnTargetWindowClosed", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

        // this.OnTargetWindowClosed
        ilgen.Emit(OpCodes.Ldsfld, typeof(WindowLogic).GetField("Instances", BindingFlags.Static | BindingFlags.Public));
        ilgen.Emit(OpCodes.Ldc_I4, _increment);
        ilgen.Emit(OpCodes.Call, typeof(Dict).GetMethod("Get"));
        ilgen.Emit(OpCodes.Call, onTargetWindowClosedMethodInfo);
        ilgen.Emit(OpCodes.Ret);

        return handler.CreateDelegate(eventHandlerType);
    }

    public void OnTargetWindowClosed()
    {
        throw new NotImplementedException();
    }

    private Type[] GetDelegateParameterTypes(Type eventHandlerType)
    {
        throw new NotImplementedException();
    }

    public void Dispose()
    {
        Instances.Remove(_increment);
    }
}