.net NET中的弱事件?
如果对象A侦听来自对象B的事件,则对象B将使对象A保持活动状态。 是否有标准的弱事件实现可以防止这种情况? 我知道WPF有一些机制,但我正在寻找一些与WPF无关的东西。.net NET中的弱事件?,.net,events,garbage-collection,weak-references,.net,Events,Garbage Collection,Weak References,如果对象A侦听来自对象B的事件,则对象B将使对象A保持活动状态。 是否有标准的弱事件实现可以防止这种情况? 我知道WPF有一些机制,但我正在寻找一些与WPF无关的东西。 我猜想解决方案应该在某些地方使用弱引用。使用您认为事件管理资源清理的地方,应该处理这个问题。当对象A被释放时,它应该将自己注销为对象B事件的侦听器…DidItWith.NET博客中的Dustin Campbell检查了创建弱事件处理程序的几次失败尝试,然后继续显示一个有效、有效、轻量级的实现: 不过,在理想情况下,微软会将这一概
我猜想解决方案应该在某些地方使用弱引用。
使用您认为事件管理资源清理的地方,应该处理这个问题。当对象A被释放时,它应该将自己注销为对象B事件的侦听器…
DidItWith.NET博客中的Dustin Campbell检查了创建弱事件处理程序的几次失败尝试,然后继续显示一个有效、有效、轻量级的实现: 不过,在理想情况下,微软会将这一概念引入语言本身。比如:Foo.Clicked += new weak EventHandler(...);
如果您觉得此功能对您很重要,请。我重新打包了Dustin Campbell的实现,以便在不使用泛型处理程序的情况下,更容易将其扩展到不同的事件类型。我想这可能对某些人有用 学分:
Ed Ball提供的一个非常方便的委托转换函数,可以在源代码中找到链接 处理程序和两个重载EventHander和PropertyChangedEventHandler:
/// Basic weak event management.
///
/// Weak allow objects to be garbage collected without having to unsubscribe
///
/// Taken with some minor variations from:
/// http://diditwith.net/2007/03/23/SolvingTheProblemWithEventsWeakEventHandlers.aspx
///
/// use as class.theEvent +=new EventHandler<EventArgs>(instance_handler).MakeWeak((e) => class.theEvent -= e);
/// MakeWeak extension methods take an delegate to unsubscribe the handler from the event
///
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
namespace utils {
/// <summary>
/// Delegate of an unsubscribe delegate
/// </summary>
public delegate void UnregisterDelegate<H>(H eventHandler) where H : class;
/// <summary>
/// A handler for an event that doesn't store a reference to the source
/// handler must be a instance method
/// </summary>
/// <typeparam name="T">type of calling object</typeparam>
/// <typeparam name="E">type of event args</typeparam>
/// <typeparam name="H">type of event handler</typeparam>
public class WeakEventHandlerGeneric<T, E, H>
where T : class
where E : EventArgs
where H : class {
private delegate void OpenEventHandler(T @this, object sender, E e);
private delegate void LocalHandler(object sender, E e);
private WeakReference m_TargetRef;
private OpenEventHandler m_OpenHandler;
private H m_Handler;
private UnregisterDelegate<H> m_Unregister;
public WeakEventHandlerGeneric(H eventHandler, UnregisterDelegate<H> unregister) {
m_TargetRef = new WeakReference((eventHandler as Delegate).Target);
m_OpenHandler = (OpenEventHandler)Delegate.CreateDelegate(typeof(OpenEventHandler), null, (eventHandler as Delegate).Method);
m_Handler = CastDelegate(new LocalHandler(Invoke));
m_Unregister = unregister;
}
private void Invoke(object sender, E e) {
T target = (T)m_TargetRef.Target;
if (target != null)
m_OpenHandler.Invoke(target, sender, e);
else if (m_Unregister != null) {
m_Unregister(m_Handler);
m_Unregister = null;
}
}
/// <summary>
/// Gets the handler.
/// </summary>
public H Handler {
get { return m_Handler; }
}
/// <summary>
/// Performs an implicit conversion from <see cref="PR.utils.WeakEventHandler<T,E>"/> to <see cref="System.EventHandler<E>"/>.
/// </summary>
/// <param name="weh">The weh.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator H(WeakEventHandlerGeneric<T, E, H> weh) {
return weh.Handler;
}
/// <summary>
/// Casts the delegate.
/// Taken from
/// http://jacobcarpenters.blogspot.com/2006/06/cast-delegate.html
/// </summary>
/// <param name="source">The source.</param>
/// <returns></returns>
public static H CastDelegate(Delegate source) {
if (source == null) return null;
Delegate[] delegates = source.GetInvocationList();
if (delegates.Length == 1)
return Delegate.CreateDelegate(typeof(H), delegates[0].Target, delegates[0].Method) as H;
for (int i = 0; i < delegates.Length; i++)
delegates[i] = Delegate.CreateDelegate(typeof(H), delegates[i].Target, delegates[i].Method);
return Delegate.Combine(delegates) as H;
}
}
#region Weak Generic EventHandler<Args> handler
/// <summary>
/// An interface for a weak event handler
/// </summary>
/// <typeparam name="E"></typeparam>
public interface IWeakEventHandler<E> where E : EventArgs {
EventHandler<E> Handler { get; }
}
/// <summary>
/// A handler for an event that doesn't store a reference to the source
/// handler must be a instance method
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="E"></typeparam>
public class WeakEventHandler<T, E> : WeakEventHandlerGeneric<T, E, EventHandler<E>>, IWeakEventHandler<E>
where T : class
where E : EventArgs {
public WeakEventHandler(EventHandler<E> eventHandler, UnregisterDelegate<EventHandler<E>> unregister)
: base(eventHandler, unregister) { }
}
#endregion
#region Weak PropertyChangedEvent handler
/// <summary>
/// An interface for a weak event handler
/// </summary>
/// <typeparam name="E"></typeparam>
public interface IWeakPropertyChangedEventHandler {
PropertyChangedEventHandler Handler { get; }
}
/// <summary>
/// A handler for an event that doesn't store a reference to the source
/// handler must be a instance method
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="E"></typeparam>
public class WeakPropertyChangeHandler<T> : WeakEventHandlerGeneric<T, PropertyChangedEventArgs, PropertyChangedEventHandler>, IWeakPropertyChangedEventHandler
where T : class {
public WeakPropertyChangeHandler(PropertyChangedEventHandler eventHandler, UnregisterDelegate<PropertyChangedEventHandler> unregister)
: base(eventHandler, unregister) {}
}
#endregion
/// <summary>
/// Utilities for the weak event method
/// </summary>
public static class WeakEventExtensions {
private static void CheckArgs(Delegate eventHandler, Delegate unregister) {
if (eventHandler == null) throw new ArgumentNullException("eventHandler");
if (eventHandler.Method.IsStatic || eventHandler.Target == null) throw new ArgumentException("Only instance methods are supported.", "eventHandler");
}
private static object GetWeakHandler(Type generalType, Type[] genericTypes, Type[] constructorArgTypes, object[] constructorArgs) {
var wehType = generalType.MakeGenericType(genericTypes);
var wehConstructor = wehType.GetConstructor(constructorArgTypes);
return wehConstructor.Invoke(constructorArgs);
}
/// <summary>
/// Makes a property change handler weak
/// </summary>
/// <typeparam name="E"></typeparam>
/// <param name="eventHandler">The event handler.</param>
/// <param name="unregister">The unregister.</param>
/// <returns></returns>
public static PropertyChangedEventHandler MakeWeak(this PropertyChangedEventHandler eventHandler, UnregisterDelegate<PropertyChangedEventHandler> unregister) {
CheckArgs(eventHandler, unregister);
var generalType = typeof (WeakPropertyChangeHandler<>);
var genericTypes = new[] {eventHandler.Method.DeclaringType};
var constructorTypes = new[] { typeof(PropertyChangedEventHandler), typeof(UnregisterDelegate<PropertyChangedEventHandler>) };
var constructorArgs = new object[] {eventHandler, unregister};
return ((IWeakPropertyChangedEventHandler) GetWeakHandler(generalType, genericTypes, constructorTypes, constructorArgs)).Handler;
}
/// <summary>
/// Makes a generic handler weak
/// </summary>
/// <typeparam name="E"></typeparam>
/// <param name="eventHandler">The event handler.</param>
/// <param name="unregister">The unregister.</param>
/// <returns></returns>
public static EventHandler<E> MakeWeak<E>(this EventHandler<E> eventHandler, UnregisterDelegate<EventHandler<E>> unregister) where E : EventArgs {
CheckArgs(eventHandler, unregister);
var generalType = typeof(WeakEventHandler<,>);
var genericTypes = new[] { eventHandler.Method.DeclaringType, typeof(E) };
var constructorTypes = new[] { typeof(EventHandler<E>), typeof(UnregisterDelegate<EventHandler<E>>) };
var constructorArgs = new object[] { eventHandler, unregister };
return ((IWeakEventHandler<E>)GetWeakHandler(generalType, genericTypes, constructorTypes, constructorArgs)).Handler;
}
}
}
///基本弱事件管理。
///
///弱允许在不取消订阅的情况下对对象进行垃圾收集
///
///与以下内容略有不同:
/// http://diditwith.net/2007/03/23/SolvingTheProblemWithEventsWeakEventHandlers.aspx
///
///用作class.theEvent+=新事件处理程序(实例处理程序)。MakeWeak((e)=>class.theEvent-=e);
///MakeWeak扩展方法使用委托从事件中取消订阅处理程序
///
使用制度;
使用System.Collections.Generic;
使用系统组件模型;
使用System.Linq;
运用系统反思;
使用系统文本;
命名空间utils{
///
///取消订阅代理的代理
///
公共委托void unregisteredelegate(H eventHandler),其中H:class;
///
///不存储对源的引用的事件的处理程序
///处理程序必须是实例方法
///
///调用对象的类型
///事件参数的类型
///事件处理程序的类型
公共类弱电
T:在哪里上课
其中E:EventArgs
H:class在哪里{
私有委托void OpenEventHandler(T@this,对象发送者,E);
私有委托void LocalHandler(对象发送方,E);
私人WeakReference m_TargetRef;
私有OpenEventHandler m_OpenHandler;
私人H m_处理器;
私人注销委托m_注销;
public-eventhandlergeneric(H-eventHandler,unregister-delegate-unregister){
m_TargetRef=new WeakReference((eventHandler作为委托).Target);
m_OpenHandler=(OpenEventHandler)Delegate.CreateDelegate(typeof(OpenEventHandler),null,(eventHandler作为Delegate.Method);
m_Handler=CastDelegate(新的LocalHandler(Invoke));
m_Unregister=取消注册;
}
私有void调用(对象发送方,E){
T target=(T)m_TargetRef.target;
如果(目标!=null)
m_OpenHandler.Invoke(目标、发送方、e);
else if(m_Unregister!=null){
m_注销(m_处理程序);
m_Unregister=null;
}
}
///
///获取处理程序。
///
公共H处理器{
获取{return m_Handler;}
}
///
///执行从到的隐式转换。
///
///weh。
///转换的结果。
公共静态隐式算子H{
返回weh.Handler;
}
///
///强制转换委托。
///取自
/// http://jacobcarpenters.blogspot.com/2006/06/cast-delegate.html
///
///消息来源。
///
公共静态委托(委托源){
if(source==null)返回null;
Delegate[]delegates=source.GetInvocationList();
如果(delegates.Length==1)
将Delegate.CreateDelegate(typeof(H),Delegate[0]。目标,Delegate[0]。方法)返回为H;
for(int i=0;i
using System.ComponentModel;
using NUnit.Framework;
using System.Collections.Generic;
using System;
namespace utils.Tests {
[TestFixture]
public class WeakEventTests {
#region setup/teardown
[TestFixtureSetUp]
public void SetUp() {
testScenarios.Add(SetupTestGeneric);
testScenarios.Add(SetupTestPropChange);
}
[TestFixtureTearDown]
public void TearDown() {
}
#endregion
#region tests
private List<Action<bool>> testScenarios = new List<Action<bool>>();
private IEventSource source;
private WeakReference sourceRef;
private IEventConsumer consumer;
private WeakReference consumerRef;
private IEventConsumer consumer2;
private WeakReference consumerRef2;
[Test]
public void ConsumerSourceTest() {
foreach(var a in testScenarios) {
a(false);
ConsumerSourceTestMethod();
}
}
private void ConsumerSourceTestMethod() {
Assert.IsFalse(consumer.eventSet);
source.Fire();
Assert.IsTrue(consumer.eventSet);
}
[Test]
public void ConsumerLinkTest() {
foreach (var a in testScenarios) {
a(false);
ConsumerLinkTestMethod();
}
}
private void ConsumerLinkTestMethod() {
consumer = null;
GC.Collect();
Assert.IsFalse(consumerRef.IsAlive);
Assert.IsTrue(source.InvocationCount == 1);
source.Fire();
Assert.IsTrue(source.InvocationCount == 0);
}
[Test]
public void ConsumerLinkTestDouble() {
foreach (var a in testScenarios) {
a(true);
ConsumerLinkTestDoubleMethod();
}
}
private void ConsumerLinkTestDoubleMethod() {
consumer = null;
GC.Collect();
Assert.IsFalse(consumerRef.IsAlive);
Assert.IsTrue(source.InvocationCount == 2);
source.Fire();
Assert.IsTrue(source.InvocationCount == 1);
consumer2 = null;
GC.Collect();
Assert.IsFalse(consumerRef2.IsAlive);
Assert.IsTrue(source.InvocationCount == 1);
source.Fire();
Assert.IsTrue(source.InvocationCount == 0);
}
[Test]
public void ConsumerLinkTestMultiple() {
foreach (var a in testScenarios) {
a(true);
ConsumerLinkTestMultipleMethod();
}
}
private void ConsumerLinkTestMultipleMethod() {
consumer = null;
consumer2 = null;
GC.Collect();
Assert.IsFalse(consumerRef.IsAlive);
Assert.IsFalse(consumerRef2.IsAlive);
Assert.IsTrue(source.InvocationCount == 2);
source.Fire();
Assert.IsTrue(source.InvocationCount == 0);
}
[Test]
public void SourceLinkTest() {
foreach (var a in testScenarios) {
a(false);
SourceLinkTestMethod();
}
}
private void SourceLinkTestMethod() {
source = null;
GC.Collect();
Assert.IsFalse(sourceRef.IsAlive);
}
[Test]
public void SourceLinkTestMultiple() {
SetupTestGeneric(true);
foreach (var a in testScenarios) {
a(true);
SourceLinkTestMultipleMethod();
}
}
private void SourceLinkTestMultipleMethod() {
source = null;
GC.Collect();
Assert.IsFalse(sourceRef.IsAlive);
}
#endregion
#region test helpers
public void SetupTestGeneric(bool both) {
source = new EventSourceGeneric();
sourceRef = new WeakReference(source);
consumer = new EventConsumerGeneric((EventSourceGeneric)source);
consumerRef = new WeakReference(consumer);
if (both) {
consumer2 = new EventConsumerGeneric((EventSourceGeneric)source);
consumerRef2 = new WeakReference(consumer2);
}
}
public void SetupTestPropChange(bool both) {
source = new EventSourcePropChange();
sourceRef = new WeakReference(source);
consumer = new EventConsumerPropChange((EventSourcePropChange)source);
consumerRef = new WeakReference(consumer);
if (both) {
consumer2 = new EventConsumerPropChange((EventSourcePropChange)source);
consumerRef2 = new WeakReference(consumer2);
}
}
public interface IEventSource {
int InvocationCount { get; }
void Fire();
}
public class EventSourceGeneric : IEventSource {
public event EventHandler<EventArgs> theEvent;
public int InvocationCount {
get { return (theEvent != null)? theEvent.GetInvocationList().Length : 0; }
}
public void Fire() {
if (theEvent != null) theEvent(this, EventArgs.Empty);
}
}
public class EventSourcePropChange : IEventSource {
public event PropertyChangedEventHandler theEvent;
public int InvocationCount {
get { return (theEvent != null) ? theEvent.GetInvocationList().Length : 0; }
}
public void Fire() {
if (theEvent != null) theEvent(this, new PropertyChangedEventArgs(""));
}
}
public interface IEventConsumer {
bool eventSet { get; }
}
public class EventConsumerGeneric : IEventConsumer {
public bool eventSet { get; private set; }
public EventConsumerGeneric(EventSourceGeneric sourceGeneric) {
sourceGeneric.theEvent +=new EventHandler<EventArgs>(source_theEvent).MakeWeak((e) => sourceGeneric.theEvent -= e);
}
public void source_theEvent(object sender, EventArgs e) {
eventSet = true;
}
}
public class EventConsumerPropChange : IEventConsumer {
public bool eventSet { get; private set; }
public EventConsumerPropChange(EventSourcePropChange sourcePropChange) {
sourcePropChange.theEvent += new PropertyChangedEventHandler(source_theEvent).MakeWeak((e) => sourcePropChange.theEvent -= e);
}
public void source_theEvent(object sender, PropertyChangedEventArgs e) {
eventSet = true;
}
}
#endregion
}
}
public Listener(object target, Delegate handler)
{
this._target = new WeakReference(target);
this._handler = new WeakReference((object) handler);
}
public void Invoke(object sender, E e)
{
T target = (T)m_TargetRef.Target;
if (target != null)
m_OpenHandler(target, sender, e);
public class EventSource
{
public event EventHandler<EventArgs> Fired
}
}
public class EventSubscriber
{
public void OnEventFired(object sender, EventArgs) { ; }
}
public class Program {
public void Main()
{
var source = new EventSource();
var subscriber = new EventSubscriber();
source.Fired += new WeakEventHandler<EventSubscriber, EventArgs>(subscriber.OnEventFired, handler => source.Fired -= handler);
}
}