क्या C # में इवेंट हैंडलर्स को स्पष्ट रूप से निकालना आवश्यक है


120

मेरे पास एक वर्ग है जो कुछ घटनाओं की पेशकश करता है। उस वर्ग को विश्व स्तर पर घोषित किया गया है, लेकिन उस वैश्विक घोषणा पर नहीं - यह ऐसे तरीकों के रूप में आवश्यक है, जिनकी जरूरत है।

हर बार उस वर्ग को एक विधि की आवश्यकता होती है, यह इंस्टेंट है और इवेंट हैंडलर पंजीकृत हैं। क्या विधि के दायरे से बाहर जाने से पहले इवेंट हैंडलर्स को स्पष्ट रूप से निकालना आवश्यक है?

जब विधि दायरे से बाहर हो जाती है, तो कक्षा का उदाहरण जाता है। क्या इवेंट हैंडलर को उस उदाहरण के साथ पंजीकृत किया जाता है जो दायरे से बाहर हो रहा है, एक मेमोरी फ़ुटप्रिंट निहितार्थ है? (मुझे आश्चर्य है कि अगर इवेंट हैंडलर जीसी को क्लास के उदाहरण को देखने से रोकता है क्योंकि अब संदर्भित नहीं किया जा रहा है।)

जवाबों:


184

आपके मामले में, सब कुछ ठीक है। यह ऐसी वस्तु है जो उन घटनाओं को प्रकाशित करती है जो घटना संचालकों के लक्ष्य को जीवित रखती हैं। तो अगर मेरे पास है:

publisher.SomeEvent += target.DoSomething;

फिर publisherएक संदर्भ है, targetलेकिन दूसरे तरीके से नहीं।

आपके मामले में, प्रकाशक कचरा संग्रहण के लिए पात्र होने जा रहा है (यह मानते हुए कि इसके कोई अन्य संदर्भ नहीं हैं) तो यह तथ्य यह है कि यह घटना हैंडलर लक्ष्यों के लिए एक संदर्भ है अप्रासंगिक है।

मुश्किल मामला तब होता है जब प्रकाशक लंबे समय तक जीवित रहता है लेकिन ग्राहक नहीं बनना चाहते हैं - उस स्थिति में आपको हैंडलर को अनसब्सक्राइब करने की आवश्यकता होती है। उदाहरण के लिए, मान लें कि आपके पास कुछ डेटा ट्रांसफ़र सेवा है, जो आपको बैंडविड्थ परिवर्तनों के बारे में अतुल्यकालिक सूचनाओं की सदस्यता देता है, और स्थानांतरण सेवा ऑब्जेक्ट लंबे समय तक जीवित रहता है। यदि हम ऐसा करते हैं:

BandwidthUI ui = new BandwidthUI();
transferService.BandwidthChanged += ui.HandleBandwidthChange;
// Suppose this blocks until the transfer is complete
transferService.Transfer(source, destination);
// We now have to unsusbcribe from the event
transferService.BandwidthChanged -= ui.HandleBandwidthChange;

(आप वास्तव में इवेंट हैंडलर को लीक नहीं करने के लिए यह सुनिश्चित करने के लिए अंत में ब्लॉक का उपयोग करना चाहते हैं।) यदि हमने सदस्यता समाप्त नहीं की, तो BandwidthUIकम से कम तब तक जीवित रहेंगे जब तक कि स्थानांतरण सेवा नहीं होगी।

व्यक्तिगत रूप से मैं इस पर शायद ही कभी आता हूं - आमतौर पर अगर मैं किसी घटना की सदस्यता लेता हूं, तो उस घटना का लक्ष्य कम से कम तब तक रहता है जब तक कि प्रकाशक - एक फॉर्म तब तक रहेगा जब तक कि बटन उस पर है, उदाहरण के लिए। यह इस संभावित मुद्दे के बारे में जानने के लायक है, लेकिन मुझे लगता है कि कुछ लोगों को इसके बारे में चिंता होती है जब उन्हें ज़रूरत नहीं होती, क्योंकि वे नहीं जानते कि किस तरह से संदर्भों को गोल किया जाता है।

EDIT: यह जोनाथन डिकिंसन की टिप्पणी का जवाब देने के लिए है। सबसे पहले, डेलिगेट के लिए डॉक्स देखें। ईक्ल्स (ऑब्जेक्ट) जो स्पष्ट रूप से समानता का व्यवहार देते हैं।

दूसरी बात, सदस्यता समाप्त करने के लिए यहाँ एक छोटा लेकिन पूरा कार्यक्रम काम कर रहा है:

using System;

public class Publisher
{
    public event EventHandler Foo;

    public void RaiseFoo()
    {
        Console.WriteLine("Raising Foo");
        EventHandler handler = Foo;
        if (handler != null)
        {
            handler(this, EventArgs.Empty);
        }
        else
        {
            Console.WriteLine("No handlers");
        }
    }
}

public class Subscriber
{
    public void FooHandler(object sender, EventArgs e)
    {
        Console.WriteLine("Subscriber.FooHandler()");
    }
}

public class Test
{
    static void Main()
    {
         Publisher publisher = new Publisher();
         Subscriber subscriber = new Subscriber();
         publisher.Foo += subscriber.FooHandler;
         publisher.RaiseFoo();
         publisher.Foo -= subscriber.FooHandler;
         publisher.RaiseFoo();
    }
}

परिणाम:

Raising Foo
Subscriber.FooHandler()
Raising Foo
No handlers

(मोनो और .NET 3.5SP1 पर परीक्षण किया गया।)

आगे संपादित करें:

यह साबित करना है कि एक घटना प्रकाशक को एकत्र किया जा सकता है जबकि एक ग्राहक के संदर्भ अभी भी हैं।

using System;

public class Publisher
{
    ~Publisher()
    {
        Console.WriteLine("~Publisher");
        Console.WriteLine("Foo==null ? {0}", Foo == null);
    }

    public event EventHandler Foo;
}

public class Subscriber
{
    ~Subscriber()
    {
        Console.WriteLine("~Subscriber");
    }

    public void FooHandler(object sender, EventArgs e) {}
}

public class Test
{
    static void Main()
    {
         Publisher publisher = new Publisher();
         Subscriber subscriber = new Subscriber();
         publisher.Foo += subscriber.FooHandler;

         Console.WriteLine("No more refs to publisher, "
             + "but subscriber is alive");
         GC.Collect();
         GC.WaitForPendingFinalizers();         

         Console.WriteLine("End of Main method. Subscriber is about to "
             + "become eligible for collection");
         GC.KeepAlive(subscriber);
    }
}

परिणाम (.NET 3.5SP1 में; मोनो थोड़ा अजीब तरह से व्यवहार करता प्रतीत होता है। कुछ समय में दिखेगा)

No more refs to publisher, but subscriber is alive
~Publisher
Foo==null ? False
End of Main method. Subscriber is about to become eligible for collection
~Subscriber

2
मैं इससे सहमत हूं लेकिन यदि संभव हो तो आप संक्षिप्त रूप से या अधिमानतः उदाहरण के लिए संदर्भित कर सकते हैं कि आपका क्या मतलब है "लेकिन ग्राहक नहीं बनना चाहते हैं"?
पीटर मैकजी

@ जॉन: बहुत सराहना की, यह आम नहीं है, लेकिन जैसा कि आप कहते हैं मैंने देखा है कि लोग इस बारे में अनावश्यक रूप से चिंता करते हैं।
पीटर मैकग

- = काम नहीं करता है। - = एक नए प्रतिनिधि का परिणाम होगा, और प्रतिनिधि लक्ष्य पद्धति का उपयोग करके समानता की जांच नहीं करते हैं, वे एक ऑब्जेक्ट करते हैं। प्रतिनिधि पर (संदर्भ) ()। नया प्रतिनिधि सूची में मौजूद नहीं है: इसका कोई प्रभाव नहीं है (और गलती से कोई त्रुटि नहीं करता है)।
जोनाथन सी डिकिन्सन

2
@ जोनाथन: नहीं, प्रतिनिधि लक्ष्य पद्धति का उपयोग करके समानता की जांच करते हैं। एक संपादन में साबित होगा।
जॉन स्कीट

मैं मान गया। अनाम प्रतिनिधियों से मैं उलझ गया।
जोनाथन सी डिकिंसन

8

आपके मामले में, आप ठीक हैं। मैंने मूल रूप से आपके प्रश्न को पीछे की ओर पढ़ा है, कि एक सब्सक्राइबर गुंजाइश से बाहर जा रहा था, प्रकाशक नहीं । अगर ईवेंट पब्लिशर दायरे से बाहर हो जाता है, तो सब्सक्राइबर (सब्सक्राइबर ही नहीं, बिल्कुल!) के संदर्भ इसके साथ जाते हैं और उन्हें स्पष्ट रूप से हटाने की कोई आवश्यकता नहीं है।

मेरा मूल उत्तर नीचे दिया गया है, अगर आप एक ईवेंट सब्सक्राइबर बनाते हैं और बिना सदस्यता के दायरे से बाहर जाने देते हैं , तो इसके बारे में क्या होगा । यह आपके प्रश्न पर लागू नहीं होता है लेकिन मैं इसे इतिहास के लिए छोड़ दूंगा।

यदि वर्ग अभी भी इवेंट हैंडलर के माध्यम से पंजीकृत है, तो यह अभी भी उपलब्ध है। यह अभी भी एक जीवित वस्तु है। एक इवेंट ग्राफ के बाद एक GC यह जुड़ा हुआ मिलेगा। हां, आप ईवेंट हैंडलर को स्पष्ट रूप से हटाना चाहेंगे।

सिर्फ इसलिए कि वस्तु अपने मूल आवंटन के दायरे से बाहर है इसका मतलब यह नहीं है कि यह जीसी के लिए एक उम्मीदवार है। जब तक एक लाइव संदर्भ रहता है, तब तक यह लाइव है।


1
मुझे विश्वास नहीं है कि कोई भी सदस्यता यहां आवश्यक है - जीसी घटना प्रकाशक से संदर्भों को देखता है , इसे नहीं, और यह वह प्रकाशक है जिसके बारे में हम यहां चिंतित हैं।
जॉन स्कीट

@ जोंन स्कीट: आप सही कह रहे हैं। मैं प्रश्न को पीछे की ओर पढ़ता हूं। मैंने वास्तविकता को प्रतिबिंबित करने के लिए अपना उत्तर सही कर लिया है।
एडी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.