簡體   English   中英

短暫的事件處理程序有什么問題嗎?

[英]Anything wrong with short-lived event handlers?

例如,這樣的事情有什么問題嗎?

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);
        }
    }
}

如果它只是一個簡單的整數變量聲明它會沒問題,因為一旦它超出范圍,后來被垃圾收集器收集,但事件有點......更長壽? 我是否必須手動取消訂閱或采取任何措施以確保其正常工作?

它只是感覺......很奇怪。 不確定它是否正確。 通常當我添加到事件處理程序時,它會持續整個應用程序,所以我不確定在不斷添加和刪除事件處理程序時會發生什么。

從技術上講,這段代碼沒有任何問題。 由於在方法完成后將不會對packet進行任何引用,因此也將(最終)收集訂閱。

作為一種風格問題,我認為這樣做非常不尋常。 更好的解決方案可能是從Send()返回某種結果,並將您的下一個操作基於此。 順序代碼更容易理解。

這真的是基於事件的編程的美妙之處 - 你並不關心誰/什么/如果有人正在傾聽,你只是說發生了什么,讓任何訂閱的人做出相應的反應。

不必手動取消,但作為對象超出范圍,你應該退訂。 該鏈接將使訂戶保持活力(來自@pst)。

當發布事件的對象符合垃圾回收條件時,訂閱該事件的所有對象也有資格進行垃圾回收(前提是沒有其他對它們的引用)。

所以,在你的例子中:

  • packet超出范圍時,它就有資格獲得GC。
  • 所以包含packet_DataNotSent的對象packet_DataNotSent符合GC的條件(除非被其他東西引用)。
  • 如果包含對象packet_DataNotSent 別的引用,它當然不會是GC-ED,但它仍然會自動“unsibscribe”當packet是GC-ED。

我知道如果事件源即Packet類和事件接收器(即事件的處理程序具有生命周期不匹配),則可能會出現內存泄漏。

在這種情況下,您創建的委托作用於此函數,因為事件接收器僅限於此函數。

你可以調用packet.DataNotSent -= packet_DataNotSent; 在執行send方法之后,確保委托是垃圾回收。 閱讀本文

我想你簡化了簡潔的例子,但在你的代碼片段中,沒有必要使用事件。 Packet.Send()只能返回檢查的結果。 如果存在某些異步性(例如,異步操作,將來執行的調度等),並且Packet.Send()不會立即返回,那么您只需要更復雜的方法。

在對象生命周期管理方面,您的事件訂閱不會造成問題,因為事件訂閱會使處理程序保持活動狀態,反之則不然。 也就是說, button1_Click所屬的類將不會被垃圾收集,而對它已訂閱的數據包有實時引用。 由於數據包壽命較短,因此這不是問題。

如果在您的實際使用中, Packet.Send()無法返回結果,那么我很想將一個委托傳遞給數據包而不是訂閱它上面的事件,假設只需要通知一個對象失敗。

事件應該用於以下情況:一個人不知道誰可能有興趣在事情發生或需要采取某些行動時得到通知。 在您的示例中,如果任何人有興趣知道何時發現數據包無法傳遞,則調用構造函數的代碼很容易知道這將是誰。 因此,讓數據包的構造函數接受Action<Packet, PacketResult>委托比為此目的發布事件更好。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM