具有LINQ和委托约束的WeakEventHandler

时间:2014-08-20 22:04:48

标签: c# linq generics delegates

如何使用LINQ为预定义的系统事件创建C#泛型弱事件处理程序? Daniel Grunwald关于Weak Events in C#的文章的解决方案4显示了一个可重复使用的包装器,即

eventWrapper = WeakEventHandler.Register(
    eventSource,
    (s, eh) => s.Event += eh, // registering code
    (s, eh) => s.Event -= eh, // deregistering code
    this, // event listener
    (me, sender, args) => me.OnEvent(sender, args) // forwarding code
);

但是,尝试将此应用于通用事件(例如UnhandledExceptionHandler)会导致编译错误:无法将类型'System.EventHandler'隐式转换为'System.UnhandledExceptionEventHandler'

        WeakEventHandler.Register(AppDomain.CurrentDomain, 
            (s, eh) => s.UnhandledException += eh, 
            (s, eh) => s.UnhandledException -= eh, 
            this, 
            (me, sender, ea) => me.UnhandledExceptionHandler(sender, ea));

Jacob Carpenter关于Delegate Conversion的文章中的ConvertTo可能提供了线索,但我目前还没有看到如何在Daniel Grunwald的代码中使用它。

2 个答案:

答案 0 :(得分:1)

我不熟悉你在这里尝试做什么,但我做了一些实验,以便编译。

WeakEventHandler可以使用EventHandlerEventHandler<TEventArgs>EventHandler<UnhandledExceptionEventArgs>的事件处理程序的签名与UnhandledExceptionEventHandler的签名匹配。

由于2个签名相同,我们可以使用下面的TransformHandler函数将其中一个转换为另一个。

public static UnhandledExceptionEventHandler TransformHandler(EventHandler<UnhandledExceptionEventArgs> handler)
{
    return new UnhandledExceptionEventHandler(handler);
}

WeakEventHandler<UnhandledExceptionEventArgs>.Register(
    AppDomain.CurrentDomain,
    (s, eh) => s.UnhandledException += TransformHandler(eh),
    (s, eh) => s.UnhandledException -= TransformHandler(eh),
    this,
    (me, sender, ea) => me.UnhandledExceptionHandler(sender, ea)
);

你必须尝试这一点,看看它是否真的能满足你的需要。

答案 1 :(得分:0)

CastDelegate与ConvertDelegate合并为WeakEventHandler就可以了。

/// <summary>
/// Helper class to add weak handlers to events of predefined event handler, i.e. PropertyChagnedEventHandler
/// </summary>
public static class WeakEventHandler<TEventHandler, TEventArgs>
    where TEventHandler : class // delegate
    // where TEventArgs : EventArgs // work with structs not derived from EventArgs, i.e. DependencyPropertyChangedEventArgs
{
    /// <summary>
    /// Registers an predefined event handler that works with a weak reference to the target object.
    /// Access to the event and to the real event handler is done through lambda expressions.
    /// The code holds strong references to these expressions, so they must not capture any
    /// variables!
    /// </summary>
    /// </example>
    public static WeakEventHandler Register<TEventSource, TEventListener>(
        TEventSource senderObject,
        Action<TEventSource, TEventHandler> registerEvent,
        Action<TEventSource, TEventHandler> deregisterEvent,
        TEventListener listeningObject,
        Action<TEventListener, object, TEventArgs> forwarderAction
    )
        where TEventSource : class
        where TEventListener : class
    {
        if (senderObject == null)  throw new ArgumentNullException("senderObject");
        if (listeningObject == null)  throw new ArgumentNullException("listeningObject");
        WeakEventHandler.VerifyDelegate(registerEvent, "registerEvent");
        WeakEventHandler.VerifyDelegate(deregisterEvent, "deregisterEvent");
        WeakEventHandler.VerifyDelegate(forwarderAction, "forwarderAction");

        WeakEventHandler weh = new WeakEventHandler(listeningObject);
        TEventHandler eh = MakeDeregisterCodeAndWeakEventHandler(weh, senderObject, deregisterEvent, forwarderAction);
        registerEvent(senderObject, eh);
        return weh;
    }

    static TEventHandler MakeDeregisterCodeAndWeakEventHandler
        <TEventSource, TEventListener>
        (
            WeakEventHandler weh,
            TEventSource senderObject,
            Action<TEventSource, TEventHandler> deregisterEvent,
            Action<TEventListener, object, TEventArgs> forwarderAction
        )
        where TEventSource : class
        where TEventListener : class
    {
        Action<object, TEventArgs> eventHandler = (sender, args) =>
        {
            TEventListener listeningObject = (TEventListener)weh.listeningReference.Target;
            if (listeningObject != null) forwarderAction(listeningObject, sender, args);
            else weh.Deregister();
        };

        weh.deregisterCode = delegate
        {
            deregisterEvent(senderObject, ConvertDelegate(eventHandler));
        };

        return ConvertDelegate(eventHandler);
    }

    static TEventHandler ConvertDelegate(Delegate source)
    {
        if (source == null) return null;
        Delegate[] delegates = source.GetInvocationList();
        if (delegates.Length == 1) return Delegate.CreateDelegate(typeof(TEventHandler), delegates[0].Target, delegates[0].Method) as TEventHandler;
        for (int i = 0; i < delegates.Length; i++) delegates[i] = Delegate.CreateDelegate(typeof(TEventHandler), delegates[i].Target, delegates[i].Method);
        return Delegate.Combine(delegates) as TEventHandler;
    }
}

示例用法witih PropertyChangedEventHandler如下:

WeakEventHandler<PropertyChangedEventHandler, PropertyChangedEventArgs>.Register(
    textDocument,
    (d, eh) => d.PropertyChanged += eh,
    (d, eh) => d.PropertyChanged -= eh,
    this,
    (me, sender, args) => me.OnPropertyChanged(sender, args)
    );