gpt4 book ai didi

c# - 与 lambda 一起使用的弱事件处理程序模型

转载 作者:IT王子 更新时间:2023-10-29 04:01:24 25 4
gpt4 key购买 nike

好的,所以这更像是一个答案而不是一个问题,但是在问了this question之后, 并将 Dustin Campbell 中的各种位汇集在一起, Egor ,还有来自“IObservable/Rx/Reactive framework”的最后一个提示',我想我已经为这个特定问题制定了一个可行的解决方案。它可能会被 IObservable/Rx/Reactive 框架完全取代,但只有经验才能证明这一点。

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

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

确实,如果希望以后能够分离它们,您需要保留对您的 lambda 的引用。但是,如果您只是希望事件处理程序在您的订阅者超出范围时自行分离,那么这个答案适合您。

最佳答案

“那个”答案

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

用法,给定 Vanilla 味的对照 MouseDown事件和特定的 EventHandler<ValueEventArgs> 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

(*这是来自 Rx 的解决方法)

(** 重要的是避免在此处直接调用订阅者对象(例如放置 subscriber.DoSomething(e),或者如果我们在 Subscriber 类中则直接调用 DoSomething(e)。这样做有效地创建了对订阅者,这完全打败了对象……)

注意:在某些情况下,这可以在内存中留下对为 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);
}

详情

我的起点是 Egor的出色回答(请参阅带有评论的版本的链接):

public static void Link(Publisher publisher, Control subscriber) {
var subscriber_weak_ref = new WeakReference(subscriber);
EventHandler<ValueEventArgs<bool>> handler = null;
handler = delegate(object sender, ValueEventArgs<bool> e) {
var subscriber_strong_ref = subscriber_weak_ref.Target as Control;
if (subscriber_strong_ref != null) subscriber_strong_ref.Enabled = e.Value;
else {
((Publisher)sender).EnabledChanged -= handler;
handler = null;
}
};

publisher.EnabledChanged += handler;
}

令我困扰的是事件被硬编码到方法中。所以这意味着对于每个新事件,都有一个新的方法来编写。

我四处摸索并设法想出了这个通用的解决方案:

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);
}

但是该解决方案的问题在于它只是通用的,无法处理标准的 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);
}

然而,正如我暗示的那样here ,这不会编译,因为没有办法将 T 限制为委托(delegate)。

那时,我几乎放弃了。与 C# 规范抗争是没有意义的。

不过昨天发现了Reactive框架中的Observable.FromEvent方法,我没有实现,但是用法有点眼熟,很有意思:

var mousedown = Observable.FromEvent<MouseEventHandler, MouseDownEventArgs>(
h => new MouseEventHandler(h),
h => control.MouseDown += h,
h => control.MouseDown -= h);

这是引起我注意的第一个参数。这是缺少委托(delegate)类型约束的解决方法。我们通过传入将创建委托(delegate)的函数来获取它。

将所有这些放在一起给我们提供了此答案顶部显示的解决方案。

事后思考

我强烈建议您花时间了解响应式(Reactive)框架(或它最终被称为的任何东西)。这非常有趣,而且有点令人兴奋。我怀疑它也会使这样的问题变得完全多余。

到目前为止,我看过的最有趣的东西是Channel9 上的视频。 .

关于c# - 与 lambda 一起使用的弱事件处理程序模型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1747235/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com