C# 短期事件处理程序有什么问题吗?

C# 短期事件处理程序有什么问题吗?,c#,events,C#,Events,例如,像这样的东西有什么问题吗 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

例如,像这样的东西有什么问题吗

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);
        }
    }
}
如果它只是一个简单的整型变量声明,那就没问题了,因为一旦它超出范围,垃圾收集器就会收集它,但事件有点。。长寿?我是否必须手动取消订阅或采取任何措施以确保其正常工作


只是感觉。。奇怪的不确定它是否正确。通常,当我添加到事件处理程序时,它会持续整个应用程序,因此我不确定不断添加和删除事件处理程序时会发生什么。

这才是基于事件编程的妙处——你根本不在乎谁/什么/如果有人在听,你只是说发生了什么事,让任何人做出相应的反应


您不必手动取消订阅,但应在对象超出范围时取消订阅。该链接将使订阅服务器保持活动状态(从@pst)。

当发布事件的对象符合垃圾收集条件时,订阅该事件的所有对象也符合垃圾收集条件(前提是没有其他引用)

因此,在您的示例中:

  • 数据包
    超出范围时,它就有资格获得GC
  • 因此,包含
    packet\u DataNotSent
    的对象也可以用于GC(除非被其他对象引用)
  • 如果包含
    packet\u DataNotSent
    的对象被其他对象引用,它当然不会被GC-ed,但当
    packet
    被GC-ed时,它仍会自动“unsibscribe”

从技术上讲,此代码没有问题。由于在方法完成后将不会有任何对
数据包的引用
,因此订阅也将(最终)被收集


就风格而言,我认为这样做很不寻常。更好的解决方案可能是从
Send()
返回某种结果,并以此为基础执行下一步操作。顺序代码更容易理解。

我知道,如果事件源(即数据包类)和事件接收器(即事件的处理程序)的生存期不匹配,可能会导致内存泄漏

在这种情况下,您创建的委托的作用域是此函数,因为事件接收器的作用域仅限于此函数。

您可以调用
packet.DataNotSent-=packet\u DataNotSent。请

为了简洁起见,我想您已经简化了示例,但是在您的代码片段中,不需要使用事件
Packet.Send()。如果存在某种异步性(例如异步操作、为将来执行而安排的时间等),并且
Packet.Send()
不会立即返回,则只需要使用更复杂的方法

就对象生命周期管理而言,事件订阅不会造成问题,因为事件订阅将使处理程序保持活动状态,但反之亦然。也就是说,
button1\u Click
所属的类不会被垃圾收集,因为它订阅了一个数据包的实时引用。因为数据包的寿命很短,所以这不会是一个问题


如果在您的实际使用中,
Packet.Send()
无法返回结果,那么我会尝试向数据包传递委托,而不是订阅数据包上的事件,假设只有一个对象需要被通知失败。

事件应用于不知道在发生某些事情或需要执行某些操作时谁有兴趣被通知的场景。在您的示例中,如果有人想知道数据包何时被发现无法交付,那么调用构造函数的代码很容易知道谁将无法交付。因此,最好让数据包的构造函数接受一个
操作
委托,而不是为此发布一个事件。

在哪里将eventhandler与事件分离?为什么每次单击按钮时都要附加相同的事件处理程序?您知道可以将事件作为操作而不是事件处理程序?如果这是Packet类的用法,我倾向于更改Send()方法以返回Send操作的结果,并去掉事件处理程序。但是,我想您可能希望使Send()操作异步,在这种情况下,您需要一个委托来执行回调。这有点误导性——“链接”将使订户保持活动状态。您能否详细说明答案的第二部分?我总是对其他选择持开放态度,我同意你的观点,这种风格有点脆弱。您是否会让Send返回一个布尔值(或枚举等),指示发送是否成功,并立即处理它?返回值的问题是,我希望它是异步的,我觉得被困在使用事件中了。@RyanPeschel,如果你想异步地处理事情(而不是使用C#5),那么使用事件或回调委托可能是最好的选择。是的,如果我需要指示失败,我会返回
bool
。如果我需要更多数据,也可以使用一些自定义类型。我认为这确实是一种处理同步和异步场景的干净方法。()@Polity:谢谢你的精彩文章!它可读性很强,写得也很好。我已经在使用任务,但我发现它提供了信息。从任务返回值的问题在于它需要阻塞,直到值返回为止(最好使用同步代码!嗯,差不多了)。@Ryan Peschel:那不是真的。任务只意味着“一些工作”。如何或何时完成工作