例如,这样的事情有什么问题吗?
private void button1_Click(object sender, EventArgs e)
{
Packet packet = new Packet();
packet.DataNotSent += packet_DataNotSent;
packet.Send();
}
private void packet_DataNotSent(object sender, EventArgs e)
{
Console.WriteLine("Packet wasn't sent.");
}
class Packet
{
public event EventHandler DataNotSent;
public void Send()
{
if (DataNotSent != null)
{
DataNotSent(null, EventArgs.Empty);
}
}
}
如果它只是一个简单的整数变量声明它会没问题,因为一旦它超出范围,后来被垃圾收集器收集,但事件有点......更长寿?我是否必须手动取消订阅或采取任何措施以确保其正常工作?
感觉......很奇怪。不确定它是否正确。通常当我添加到事件处理程序时,它会持续整个应用程序,所以我不确定在不断添加和删除事件处理程序时会发生什么。
答案 0 :(得分:6)
从技术上讲,此代码没有任何问题。由于在方法完成后将不会对packet
进行任何引用,因此也将(最终)收集订阅。
作为一种风格问题,我认为这样做非常不寻常。更好的解决方案可能是从Send()
返回某种结果,并将您的下一步操作作为基础。顺序代码更容易理解。
答案 1 :(得分:2)
这真的是基于事件的编程的美妙之处 - 你真的不关心谁/什么/如果有人正在倾听,你只是说发生了什么事情,让任何订阅的人做出相应的反应。
你没有拥有来手动取消订阅,但是应该取消订阅,因为对象超出了范围。该链接将使订户保持活跃状态(来自@pst)。
答案 2 :(得分:1)
当发布事件的对象符合垃圾回收条件时,订阅该事件的所有对象也有资格进行垃圾回收(前提是没有其他引用)。
所以,在你的例子中:
packet
超出范围时,它就有资格使用GC。packet_DataNotSent
的对象也有资格获得GC(除非被其他人引用)。packet_DataNotSent
的对象被其他内容引用,它当然不会被GC编辑,但在packet
时它仍会自动“取消订阅” GC-编答案 3 :(得分:1)
据我所知,如果事件源(即Packet类和事件接收器,即事件的处理程序具有生命周期不匹配),则可能会出现内存泄漏。
在这种情况下,您创建的委托范围限定为此功能,因为事件接收器仅限于此功能。
您可以拨打{{1}的电话在执行send方法之后,确保委托是垃圾回收。请read this
答案 4 :(得分:1)
我想你简化了简洁示例,但在你的代码片段中,没有必要使用事件。 Packet.Send()
只能返回已检查的结果。如果存在某些异步性(例如,异步操作,将来执行的调度等),并且Packet.Send()
不会立即返回,您只需要更复杂的方法。
就对象生命周期管理而言,事件订阅不会造成问题,因为事件订阅会使处理程序保持活动状态,反之则不然。也就是说,button1_Click
所属的类将不会被垃圾收集,而对它已订阅的数据包有实时引用。由于数据包寿命较短,因此不会出现问题。
如果在您的实际使用中,Packet.Send()
无法返回结果,那么我很想将一个委托传递给数据包而不是订阅它上面的事件,假设只需要通知一个对象失败。
答案 5 :(得分:0)
事件应该用于那些不知道谁可能有兴趣在事情发生或需要采取某些行动时得到通知的情况。在您的示例中,如果任何人有兴趣知道何时发现数据包无法传递,则调用构造函数的代码很容易知道这将是谁。因此,让数据包的构造函数接受Action<Packet, PacketResult>
委托比为此目的发布事件更好。