好的,所以这更像是一个答案,而不是一个问题,但是在询问this question之后,将Dustin Campbell,Egor中的各个位以及最后一个提示从'IObservable/Rx/Reactive framework',我想我已经为这个特殊问题制定了可行的解决方案。它可能完全被IObservable / Rx / Reactive框架所取代,但只有经验才会显示出来。
我故意创造了一个新问题,给我空间来解释我如何达到这个解决方案,因为它可能不会立即显而易见。
有许多相关的问题,大多数人告诉你,如果你想在以后分离它们,你就不能使用内联lambda:
确实如果你希望以后能够分离它们,你需要保留对lambda的引用。但是,如果您只是希望事件处理程序在订阅者超出范围时自行分离,则此答案适合您。
答案 0 :(得分:33)
(如果您想了解我如何使用此解决方案,请阅读以下内容)
使用,给定一个包含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类中,则直接调用subscriber.DoSomething(e)或直接调用DoSomething(e)。这样做有效地创建了对订阅者,完全击败了对象...)
注意:在某些情况下,这个CAN会在内存中留下对lambdas创建的包装类的引用,但它们只会权衡字节,所以我不会太烦恼。
实现:
//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限制为委托。
那时,我几乎放弃了。尝试与C#规范作斗争没有意义。
然而,昨天,我从Reactive框架中发现了Observable.FromEvent方法,我没有实现,但用法似乎有点熟悉,而且非常有趣:
var mousedown = Observable.FromEvent<MouseEventHandler, MouseDownEventArgs>(
h => new MouseEventHandler(h),
h => control.MouseDown += h,
h => control.MouseDown -= h);
这是引起我注意的第一个论点。这是缺少委托类型约束的解决方法。我们通过传递将创建委托的函数来接受它。
将所有这些放在一起为我们提供了答案顶部显示的解决方案。
我完全建议花时间了解反应框架(或者最终被调用的内容)。它非常有趣,而且有点令人费解。我怀疑它也会使这样的问题变得多余。
到目前为止,我见过的最有趣的内容是Channel9上的视频。
答案 1 :(得分:5)
如果您转向CodePlex,那么有一个名为Sharp Observation的项目,其中作者构建了一个良好的弱委托提供程序,在MSIL中实现。快速,灵活,易于使用: e.g。
Action<int,int> myDelegate = new Action<int,int>( aMethodOnMyClass );
myDelegate.MakeWeak();
就这么简单!
答案 2 :(得分:3)
我一直在寻找解决方案很长一段时间,大多数都使用讨厌的反思,但Benjohl的答案很棒。我已经调整它以添加对非泛型EventHandler,DependencyPropertyChangedEventArgs的支持,它不会从EventArgs继承并允许您自己手动取消注册事件。我会对人们的想法非常感兴趣,特别是Benjohl。
/// <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;
}
}
}
答案 3 :(得分:2)
http://puremsil.wordpress.com/2010/05/03/generic-weak-event-handlers/