Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 用于lambdas的弱事件处理程序模型_C#_.net_Events_Memory Leaks_Lambda - Fatal编程技术网

C# 用于lambdas的弱事件处理程序模型

C# 用于lambdas的弱事件处理程序模型,c#,.net,events,memory-leaks,lambda,C#,.net,Events,Memory Leaks,Lambda,好的,这与其说是一个问题,不如说是一个答案,但在提问之后,我总结了来自“”的各种信息,还有来自“”的最后一个提示,我认为我已经为这个特定问题找到了一个可行的解决方案。它可能被IObservable/Rx/Reactive框架完全取代,但只有经验才能证明这一点 我特意提出了一个新的问题,给我空间来解释我是如何得到这个解决方案的,因为它可能不会立即显现出来 有许多相关的问题,大多数都告诉您,如果您希望以后能够分离它们,就不能使用内联lambda: 确实,如果您希望以后能够分离它们,则需

好的,这与其说是一个问题,不如说是一个答案,但在提问之后,我总结了来自“”的各种信息,还有来自“”的最后一个提示,我认为我已经为这个特定问题找到了一个可行的解决方案。它可能被IObservable/Rx/Reactive框架完全取代,但只有经验才能证明这一点

我特意提出了一个新的问题,给我空间来解释我是如何得到这个解决方案的,因为它可能不会立即显现出来

有许多相关的问题,大多数都告诉您,如果您希望以后能够分离它们,就不能使用内联lambda:

确实,如果您希望以后能够分离它们,则需要保留对lambda的引用。但是,如果您只想在订阅服务器超出范围时让事件处理程序自行分离,则此答案适用于您。

答案 (如果您想了解我是如何获得此解决方案的,请阅读下面的更多内容)

用法,给定一个控件,该控件具有一个普通的
MouseDown
事件和一个特定的
EventHandler ValueEvent
事件:

// for 'vanilla' events
SetAnyHandler<Subscriber, MouseEventHandler, MouseEventArgs>(
    h => (o,e) => h(o,e), //don't ask me, but it works*.
    h => control.MouseDown += h,
    h => control.MouseDown -= h,
    subscriber,
    (s, e) => s.DoSomething(e));  //**See note below

// for generic events
SetAnyHandler<Subscriber, ValueEventArgs>(
    h => control.ValueEvent += h,
    h => control.ValueEvent -= h,
    subscriber,
    (s, e) => s.DoSomething(e));  //**See note below
//用于“普通”事件
SetAnyHandler(
h=>(o,e)=>h(o,e),//别问我,但它是有效的*。
h=>control.MouseDown+=h,
h=>control.MouseDown-=h,
用户,
(s,e)=>s.DoSomething(e))//**见下文注释
//对于一般事件
SetAnyHandler(
h=>control.ValueEvent+=h,
h=>control.ValueEvent-=h,
用户,
(s,e)=>s.DoSomething(e))//**见下文注释
(*这是来自的解决方法)

(**避免在此处直接调用subscriber对象非常重要(例如,放置subscriber.DoSomething(e),或者如果我们在subscriber类中,则直接调用DoSomething(e)。这样做会有效地创建对subscriber的引用,从而完全破坏该对象…)

注意:在某些情况下,这可能会在内存中留下对为lambda创建的包装类的引用,但它们只重字节,所以我不太在意

实施:

//This overload handles any type of EventHandler
public static void SetAnyHandler<S, TDelegate, TArgs>(
    Func<EventHandler<TArgs>, TDelegate> converter, 
    Action<TDelegate> add, Action<TDelegate> remove,
    S subscriber, Action<S, TArgs> action)
    where TArgs : EventArgs
    where TDelegate : class
    where S : class
{
    var subs_weak_ref = new WeakReference(subscriber);
    TDelegate handler = null;
    handler = converter(new EventHandler<TArgs>(
        (s, e) =>
        {
            var subs_strong_ref = subs_weak_ref.Target as S;
            if(subs_strong_ref != null)
            {
                action(subs_strong_ref, e);
            }
            else
            {
                remove(handler);
                handler = null;
            }
        }));
    add(handler);
}

// this overload is simplified for generic EventHandlers
public static void SetAnyHandler<S, TArgs>(
    Action<EventHandler<TArgs>> add, Action<EventHandler<TArgs>> remove,
    S subscriber, Action<S, TArgs> action)
    where TArgs : EventArgs
    where S : class
{
    SetAnyHandler<S, EventHandler<TArgs>, TArgs>(
        h => h, add, remove, subscriber, action);
}
//此重载处理任何类型的EventHandler
公共静态void SetAnyHandler(
Func转换器,
操作添加,操作删除,
S订户,行动(行动)
其中targets:EventArgs
TDelegate:类在哪里
在哪里上课
{
var subs_弱_ref=新的WeakReference(订户);
TDelegate handler=null;
handler=转换器(新的EventHandler)(
(s,e)=>
{
var subs_strong_ref=subs_Slong_ref.目标为S;
if(subs\u strong\u ref!=null)
{
行动(子项强参考,e);
}
其他的
{
移除(处理器);
handler=null;
}
}));
添加(处理程序);
}
//对于通用EventHandler,此重载已简化
公共静态void SetAnyHandler(
操作添加,操作删除,
S订户,行动(行动)
其中targets:EventArgs
在哪里上课
{
SetAnyHandler(
h=>h,添加、删除、订阅、操作);
}
细节 我的出发点是作者的精彩回答(请参阅链接,以获取带有评论的版本):

公共静态无效链接(发布服务器、控制订阅服务器){
var subscriber\u弱\u ref=新的WeakReference(订户);
EventHandler=null;
handler=委托(对象发送方,ValueEventArgs e){
var subscriber_strong_ref=作为控制的subscriber_Slong_ref.目标;
如果(订户\u strong\u ref!=null)订户\u strong\u ref.Enabled=e.值;
否则{
((发布者)发送者).EnabledChanged-=处理程序;
handler=null;
}
};
publisher.EnabledChanged+=处理程序;
}
困扰我的是事件被硬编码到方法中,这意味着对于每个新事件,都有一个新的方法要编写

我胡思乱想,终于想出了这个通用的解决方案:

private static void SetAnyGenericHandler<S, T>(
     Action<EventHandler<T>> add,     //to add event listener to publisher
     Action<EventHandler<T>> remove,  //to remove event listener from publisher
     S subscriber,                    //ref to subscriber (to pass to action)
     Action<S, T> action)             //called when event is raised
    where T : EventArgs
    where S : class
{
    var subscriber_weak_ref = new WeakReference(subscriber);
    EventHandler<T> handler = null;
    handler = delegate(object sender, T e)
    {
        var subscriber_strong_ref = subscriber_weak_ref.Target as S;
        if(subscriber_strong_ref != null)
        {
            Console.WriteLine("New event received by subscriber");
            action(subscriber_strong_ref, e);
        }
        else
        {
            remove(handler);
            handler = null;
        }
    };
    add(handler);
}
private static void SetAnyGenericHandler(
操作添加,//将事件侦听器添加到发布服务器
操作remove,//从发布服务器中删除事件侦听器
S订阅服务器,//引用到订阅服务器(传递到操作)
Action Action)//在引发事件时调用
其中T:EventArgs
在哪里上课
{
var subscriber\u弱\u ref=新的WeakReference(订户);
EventHandler=null;
handler=委托(对象发送方,TE)
{
var subscriber_strong_ref=subscriber_Slong_ref.目标为S;
if(订户\u强\u参考!=null)
{
Console.WriteLine(“订户收到的新事件”);
行动(订户强参考,e);
}
其他的
{
移除(处理器);
handler=null;
}
};
添加(处理程序);
}
然而,该解决方案的问题在于它只是泛型的,不能处理标准winforms MouseUp、MouseDown等

所以我试着让它更加通用:

private static void SetAnyHandler<T, R>(
    Action<T> add,      //to add event listener to publisher
    Action<T> remove,   //to remove event listener from publisher
    Subscriber subscriber,  //ref to subscriber (to pass to action)
    Action<Subscriber, R> action) 
    where T : class
{
    var subscriber_weak_ref = new WeakReference(subscriber);
    T handler = null;
    handler = delegate(object sender, R e) //<-compiler doesn't like this line
    {
        var subscriber_strong_ref = subscriber_weak_ref.Target as Subscriber;
        if(subscriber_strong_ref != null)
        {
            action(subscriber_strong_ref, e);
        }
        else
        {
            remove(handler);
            handler = null;
        }
    };
    remove(handler);
}
private static void SetAnyHandler(
操作添加,//将事件侦听器添加到发布服务器
操作remove,//从发布服务器中删除事件侦听器
订阅服务器订阅服务器,//引用到订阅服务器(传递到操作)
行动(行动)
T:在哪里上课
{
var subscriber\u弱\u ref=新的WeakReference(订户);
T handler=null;
handler=delegate(对象发送者,re)//新的MouseEventHandler(h),
h=>control.MouseDown+=h,
h=>control.MouseDown-=h);
这是引起我注意的第一个参数。这是缺少委托类型约束的解决方法。我们通过传入将创建委托的函数来解决它

把所有这些放在一起,我们就得到了答案顶部所示的解决方案

事后考虑 我建议大家花点时间了解一下
private static void SetAnyHandler<T, R>(
    Action<T> add,      //to add event listener to publisher
    Action<T> remove,   //to remove event listener from publisher
    Subscriber subscriber,  //ref to subscriber (to pass to action)
    Action<Subscriber, R> action) 
    where T : class
{
    var subscriber_weak_ref = new WeakReference(subscriber);
    T handler = null;
    handler = delegate(object sender, R e) //<-compiler doesn't like this line
    {
        var subscriber_strong_ref = subscriber_weak_ref.Target as Subscriber;
        if(subscriber_strong_ref != null)
        {
            action(subscriber_strong_ref, e);
        }
        else
        {
            remove(handler);
            handler = null;
        }
    };
    remove(handler);
}
var mousedown = Observable.FromEvent<MouseEventHandler, MouseDownEventArgs>(
      h => new MouseEventHandler(h),
      h => control.MouseDown += h,
      h => control.MouseDown -= h);
Action<int,int> myDelegate = new Action<int,int>( aMethodOnMyClass );
myDelegate.MakeWeak();
/// <summary>
/// Weakly registers for events using <see cref="WeakReference"/>.
/// </summary>
public sealed class WeakEvent
{
    private Action removeEventHandler;

    /// <summary>
    /// Initializes a new instance of the <see cref="WeakEvent"/> class.
    /// </summary>
    /// <param name="removeEventHandler">The remove event handler function.</param>
    private WeakEvent(Action removeEventHandler)
    {
        this.removeEventHandler = removeEventHandler;
    }

    /// <summary>
    /// Weakly registers the specified subscriber to the the given event of type 
    /// <see cref="EventHandler"/>.
    /// </summary>
    /// <example>
    /// Application application;
    /// WeakEvent.Register{TextBox, TextChangedEventArgs>(
    ///     this,
    ///     eventHandler => textBox.TextChanged += eventHandler,
    ///     eventHandler => textBox.TextChanged -= eventHandler,
    ///     (sender, e) => this.OnTextChanged(sender, e));
    /// </example>
    /// <typeparam name="S">The type of the subscriber.</typeparam>
    /// <param name="subscriber">The subscriber.</param>
    /// <param name="addEventhandler">The add eventhandler.</param>
    /// <param name="removeEventHandler">The remove event handler function.</param>
    /// <param name="action">The event execution function.</param>
    public static WeakEvent Register<S>(
        S subscriber,
        Action<EventHandler> addEventhandler,
        Action<EventHandler> removeEventHandler,
        Action<S, EventArgs> action)
        where S : class
    {
        return Register<S, EventHandler, EventArgs>(
            subscriber,
            eventHandler => (sender, e) => eventHandler(sender, e),
            addEventhandler,
            removeEventHandler,
            action);
    }

    /// <summary>
    /// Weakly registers the specified subscriber to the the given event of type 
    /// <see cref="EventHandler{T}"/>.
    /// </summary>
    /// <example>
    /// Application application;
    /// WeakEvent.Register{TextBox, TextChangedEventArgs>(
    ///     this,
    ///     eventHandler => textBox.TextChanged += eventHandler,
    ///     eventHandler => textBox.TextChanged -= eventHandler,
    ///     (sender, e) => this.OnTextChanged(sender, e));
    /// </example>
    /// <typeparam name="S">The type of the subscriber.</typeparam>
    /// <typeparam name="TEventArgs">The type of the event arguments.</typeparam>
    /// <param name="subscriber">The subscriber.</param>
    /// <param name="addEventhandler">The add eventhandler.</param>
    /// <param name="removeEventHandler">The remove event handler function.</param>
    /// <param name="action">The event execution function.</param>
    public static WeakEvent Register<S, TEventArgs>(
        S subscriber, 
        Action<EventHandler<TEventArgs>> addEventhandler, 
        Action<EventHandler<TEventArgs>> removeEventHandler,
        Action<S, TEventArgs> action)
        where S : class
        where TEventArgs : EventArgs
    {
        return Register<S, EventHandler<TEventArgs>, TEventArgs>(
            subscriber,
            eventHandler => eventHandler, 
            addEventhandler, 
            removeEventHandler, 
            action);
    }

    /// <summary>
    /// Weakly registers the specified subscriber to the the given event.
    /// </summary>
    /// <example>
    /// TextBox textbox;
    /// WeakEvent.Register{TextBox, TextChangedEventHandler, TextChangedEventArgs>(
    ///     this,
    ///     eventHandler => (sender, e) => eventHandler(sender, e),
    ///     eventHandler => textBox.TextChanged += eventHandler,
    ///     eventHandler => textBox.TextChanged -= eventHandler,
    ///     (sender, e) => this.OnTextChanged(sender, e));
    /// </example>
    /// <typeparam name="S">The type of the subscriber.</typeparam>
    /// <typeparam name="TEventHandler">The type of the event handler.</typeparam>
    /// <typeparam name="TEventArgs">The type of the event arguments.</typeparam>
    /// <param name="subscriber">The subscriber.</param>
    /// <param name="getEventHandler">The get event handler function.</param>
    /// <param name="addEventHandler">The add event handler function.</param>
    /// <param name="removeEventHandler">The remove event handler function.</param>
    /// <param name="action">The event execution function.</param>
    public static WeakEvent Register<S, TEventHandler, TEventArgs>(
        S subscriber, 
        Func<EventHandler<TEventArgs>, TEventHandler> getEventHandler,
        Action<TEventHandler> addEventHandler, 
        Action<TEventHandler> removeEventHandler,
        Action<S, TEventArgs> action)
        where S : class
        where TEventHandler : class
        where TEventArgs : EventArgs

    {
        WeakReference weakReference = new WeakReference(subscriber);

        TEventHandler eventHandler = null;
        eventHandler = getEventHandler(
            new EventHandler<TEventArgs>(
                (sender, e) =>
                {
                    S subscriberStrongRef = weakReference.Target as S;

                    if (subscriberStrongRef != null)
                    {
                        action(subscriberStrongRef, e);
                    }
                    else
                    {
                        removeEventHandler(eventHandler);
                        eventHandler = null;
                    }
                }));

        addEventHandler(eventHandler);

        return new WeakEvent(() => removeEventHandler(eventHandler));
    }

    public static WeakEvent Register<S>(
        S subscriber,
        Action<DependencyPropertyChangedEventHandler> addEventHandler,
        Action<DependencyPropertyChangedEventHandler> removeEventHandler,
        Action<S, DependencyPropertyChangedEventArgs> action)
        where S : class
    {
        WeakReference weakReference = new WeakReference(subscriber);

        DependencyPropertyChangedEventHandler eventHandler = null;
        eventHandler = new DependencyPropertyChangedEventHandler(
            (sender, e) =>
            {
                S subscriberStrongRef = weakReference.Target as S;

                if (subscriberStrongRef != null)
                {
                    action(subscriberStrongRef, e);
                }
                else
                {
                    removeEventHandler(eventHandler);
                    eventHandler = null;
                }
            });

        addEventHandler(eventHandler);

        return new WeakEvent(() => removeEventHandler(eventHandler));
    }

    /// <summary>
    /// Manually unregisters this instance from the event.
    /// </summary>
    public void Unregister()
    {
        if (this.removeEventHandler != null)
        {
            this.removeEventHandler();
            this.removeEventHandler = null;
        }
    }
}