我试图通过开发一个通用的弱事件侦听器来节省时间,我可以在要执行的操作中传递它。在取消注册之前,事情似乎运行良好。这似乎将它们全部取消注册。我感到困惑的是为什么,这与为 IWeakEventListener 参数传递此参数有何不同?
public class GenericWeakEventListener : IWeakEventListener
{
#region EventAction
/// <summary>
/// Action to take for the event
/// </summary>
private Action<Type, object, EventArgs> _eventAction;
/// <summary>
/// Gets or sets the action to take for the event
/// </summary>
//[DataMember]
public Action<Type, object, EventArgs> EventAction
{
get
{
return _eventAction;
}
private set
{
if (EventAction != value)
{
_eventAction = value;
}
}
}
#endregion EventAction
#region Constructors
public GenericWeakEventListener(Action<Type, object, EventArgs> action)
{
EventAction = action;
}
#endregion Constructors
#region Public Methods
public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
if (EventAction != null)
{
EventAction(managerType, sender, e);
}
return true;
}
#endregion Public Methods
}
编辑:
这是侦听器代码:
public class SomeClient
{
public int ID { get; set; }
private Timer timer = null;
private Timer timer2 = null;
public SomeClient(int id, SomeService service)
{
ID = id;
//EventHandler<GenericEventArgs<string>> d = (o, s) => Console.WriteLine("Client {0}: {1}", ID, s.Item);
if (service != null) SomeEventChangedEventManager.AddListener(service, new GenericWeakEventListener((t, s, e) => { Console.WriteLine("SomeEvent: " + ID); }));
timer = new Timer { AutoReset = true, Interval = 1000 };
SomeTimerElapsedEventManager.AddListener(timer, new GenericWeakEventListener((t, s, e) => { Console.WriteLine("SomeTimer: " + ID); }));
timer.Start();
}
}
这是来自发布者的代码:
public class SomeService
{
public event EventHandler<GenericEventArgs<string>> SomeEvent;
public SomeService()
{
System.Timers.Timer timer = new Timer { AutoReset = true, Interval = 1000 };
timer.Elapsed += (sender, args) => { if (SomeEvent != null) SomeEvent(this, new GenericEventArgs<string>(Guid.NewGuid().ToString())); };
timer.Start();
}
}
这是来自 main 方法的代码:
public static void Main(string[] args)
{
SomeService service = new SomeService();
List<SomeClient> clients = new List<SomeClient>();
// Build clients
for (int x = 0; x < 5; x++)
{
clients.Add(new SomeClient(x + 1, service));
}
System.Timers.Timer timer = new Timer { AutoReset = true, Interval = 5000 };
timer.Elapsed += (s, a) =>
{
if (clients.Count == 0)
{
return;
}
Console.WriteLine("Removingrn");
clients.RemoveAt(0);
GC.Collect();
};
timer.Start();
Console.ReadLine();
}
这是输出:某事件: 1某事件: 2某事: 3某事: 4某事件: 5有时计时器:2有时计时器:3定时器: 4定时器: 1有时计时器:5某事件: 1某事件: 2某事: 3某事: 4某事件: 5定时器: 1有时计时器:2有时计时器:3定时器: 4有时计时器:5某事件: 1某事件: 2某事: 3某事: 4某事件: 5有时计时器:2有时计时器:3定时器: 4有时计时器:5定时器: 1某事件: 1某事件: 2某事: 3某事: 4某事件: 5定时器: 1有时计时器:2有时计时器:3有时计时器:5定时器: 4某事件: 1某事件: 2某事: 3某事: 4某事件: 5删除
如果没有通用的弱事件侦听器,则输出继续,没有 1,然后没有 2,依此类推。
从操作编辑编辑:知道了。以下是我认为正在发生的事情:
您正在声明由 SomeClient 拥有的 lambda,并包含实例变量 ID 的外部闭包。这使得 lambda(通过 SomeEventChangedEventManager 作为委托传递给 SomeService 上的事件(依赖于该 ID 实例的持续存在。
当您删除该 SomeClient 实例时,该 lambda 所需的 ID 变量将超出范围并被 GC 处理。但是,我没有看到此代码的任何部分从 SomeService 的 SomeEvent 处理程序中删除此 lambda。因此,lambda 作为对匿名委托的引用保留在内存中,但它所依赖的其他数据现在已消失。这会导致运行时引发异常,该异常以某种方式被吞噬,并且不会导致整个程序爆炸。
但是,基本上按照附加处理程序委托的顺序执行处理程序委托的事件(这是他们通常会告诉您忽略的实现细节(已停止执行,因为其中一个处理程序已抛出。这使得删除第一个客户端似乎已经删除了所有这些客户端,而实际上这些客户端的处理程序只是没有执行,因为第一个处理程序出错。
修复是双重的:
定义 lambda 并将其存储为 SomeClient 的实例变量。这允许您保留对它的引用,这很重要,因为在确定相等性时不会在语义上比较委托,因此以下代码不起作用:
SomeEvent += (a,b,c) => Foo(a,b,c); //the following line will not remove the handler added in the previous line, //because the two lambdas are compiled into differently-named methods //and so this is a different reference to a different method. SomeEvent -= (a,b,c) => Foo(a,b,c);
在SomeClient上实现IDisposable和/或终结器。从列表中删除客户端时,由 GC 调用的处置器/终结器应从 SomeEvent 侦听器中删除此实例的 lambda(可能通过管理器上的 RemoveListener(( 方法(。由于您保留了对委托的引用,该引用完全指向添加的内容,因此处理程序将被删除,因此不会执行,也不会出错。