अनाम विधि को C # में अनसब्सक्राइब करें


222

क्या किसी घटना से किसी अनाम विधि को हटाना संभव है?

अगर मैं इस तरह की घटना के लिए सदस्यता लेता हूं:

void MyMethod()
{
    Console.WriteLine("I did it!");
}

MyEvent += MyMethod;

मैं इस तरह से सदस्यता ले सकता हूं:

MyEvent -= MyMethod;

लेकिन अगर मैं एक अनाम विधि का उपयोग करके सदस्यता लेता हूं:

MyEvent += delegate(){Console.WriteLine("I did it!");};

क्या इस अनाम पद्धति को बंद करना संभव है? यदि हां, तो कैसे?


4
के रूप में क्यों : यदि आप ऐसा नहीं कर सकते stackoverflow.com/a/25564492/23354
मार्क Gravell

जवाबों:


230
Action myDelegate = delegate(){Console.WriteLine("I did it!");};

MyEvent += myDelegate;


// .... later

MyEvent -= myDelegate;

बस प्रतिनिधि के आसपास एक संदर्भ रखें।


141

एक तकनीक अनाम विधि को धारण करने के लिए एक चर घोषित करना है जो तब अनाम विधि के अंदर उपलब्ध होगी। यह मेरे लिए काम कर गया क्योंकि वांछित व्यवहार घटना को संभालने के बाद सदस्यता समाप्त करना था।

उदाहरण:

MyEventHandler foo = null;
foo = delegate(object s, MyEventArgs ev)
    {
        Console.WriteLine("I did it!");
        MyEvent -= foo;
    };
MyEvent += foo;

1
इस तरह के कोड का उपयोग करके, Resharper एक संशोधित क्लोजर तक पहुंचने के बारे में शिकायत करता है ... क्या यह दृष्टिकोण विश्वसनीय है? मेरा मतलब है, क्या हमें यकीन है कि अनाम विधि के शरीर के अंदर 'फू' चर वास्तव में अनाम विधि का संदर्भ देता है?
ब्लेड 13

7
मुझे अपने डब का उत्तर मिला, और यह है कि 'फू' वास्तव में अनाम विधि के संदर्भ का संदर्भ देगा। कैप्चर किए गए वैरिएबल को संशोधित किया गया है, क्योंकि अनाम विधि द्वारा असाइन किए जाने से पहले इसे कैप्चर किया गया है।
ब्लेड वाइज

2
ठीक वैसा ही मुझे चाहिए! मुझे = null याद आ रही थी। (MyEventHandler foo = डेलीगेट {... MyEvent- = foo;}; MyEvent_ = foo; काम नहीं किया ...)
TDaver

यदि आप इसे एक सरणी के रूप में घोषित करते हैं, तो Resharper 6.1 शिकायत नहीं करता है। थोड़ा अजीब लगता है, लेकिन मैं इस पर अपने टूल्स पर आँख बंद करके भरोसा करने जा रहा हूं: MyEventHandler [] foo = {null}; foo [0] = ... {... MyEvent - = foo [0]; }; MyEvent + = foo [0];
माइक पोस्ट

21

स्मृति से, विनिर्देश स्पष्ट रूप से व्यवहार की गारंटी नहीं देता है जब यह गुमनाम तरीकों से निर्मित प्रतिनिधियों की समानता की बात आती है।

यदि आपको सदस्यता समाप्त करने की आवश्यकता है, तो आपको या तो "सामान्य" विधि का उपयोग करना चाहिए या प्रतिनिधि को कहीं और रखना चाहिए, ताकि आप उसी प्रतिनिधि के साथ सदस्यता समाप्त कर सकें, जिसकी आपने सदस्यता ली थी।


मैं जॉन, तुम क्या करते हो? मुझे समझ नहीं आ रहा है। "जे सी" द्वारा उजागर समाधान ठीक से काम नहीं करेगा?
एरिक ओउलेट

@EricOuellet: यह उत्तर मूल रूप से "प्रतिनिधि को कहीं और बनाए रखने का एक कार्यान्वयन है ताकि आप उसी प्रतिनिधि के साथ सदस्यता समाप्त कर सकें जो आपने सदस्यता लिया था"।
जॉन स्कीट

जॉन, मुझे क्षमा करें, मैंने आपके उत्तर को कई बार यह जानने की कोशिश की कि आपका क्या मतलब है और जहां "जे सी" समाधान सदस्यता और सदस्यता समाप्त करने के लिए एक ही प्रतिनिधि का उपयोग नहीं करता है, लेकिन मैं इसे खत्म नहीं कर सकता। आप मुझे एक लेख पर इंगित कर सकते हैं जो समझाता है कि आप क्या कह रहे हैं? मैं आपकी प्रतिष्ठा के बारे में जानता हूं और मैं वास्तव में यह समझना चाहता हूं कि आपके लिए क्या मतलब है, कुछ भी जो आप लिंक कर सकते हैं वह वास्तव में सराहना होगी।
एरिक ओउलेट

1
मैंने पाया: msdn.microsoft.com/en-us/library/ms366768.aspx लेकिन वे अनाम का उपयोग नहीं करने की सलाह देते हैं लेकिन वे यह नहीं कहते कि कोई बड़ी समस्या है?
एरिक ओउलेट

मुझे यह मिल गया ... बहुत बहुत धन्यवाद (माइकल ब्लोम
Eric Ouellet

16

3.0 में छोटा किया जा सकता है:

MyHandler myDelegate = ()=>Console.WriteLine("I did it!");
MyEvent += myDelegate;
...
MyEvent -= myDelegate;

15

चूँकि C # 7.0 स्थानीय फ़ंक्शंस फ़ीचर जारी किया गया है, J c द्वारा सुझाया गया दृष्टिकोण वास्तव में साफ-सुथरा हो जाता है।

void foo(object s, MyEventArgs ev)
{
    Console.WriteLine("I did it!");
    MyEvent -= foo;
};
MyEvent += foo;

तो, ईमानदारी से, आपके पास एक चर के रूप में एक अनाम फ़ंक्शन नहीं है। लेकिन मुझे लगता है कि आपके मामले में इसका उपयोग करने की प्रेरणा स्थानीय कार्यों पर लागू हो सकती है।


1
पठनीयता को और बेहतर बनाने के लिए, आप MyEvent + = foo को स्थानांतरित कर सकते हैं; फू की घोषणा से पहले होने वाली लाइन।
मार्क ज़ुकोवस्की

9

किसी भी प्रतिनिधि को एक संदर्भ रखने के बजाय आप कॉल करने के लिए घटना की मंगलाचरण सूची को वापस देने के लिए अपनी कक्षा में साधन कर सकते हैं। मूल रूप से आप ऐसा कुछ लिख सकते हैं (यह मानते हुए कि MyEvent को MyClass के अंदर घोषित किया गया है):

public class MyClass 
{
  public event EventHandler MyEvent;

  public IEnumerable<EventHandler> GetMyEventHandlers()  
  {  
      return from d in MyEvent.GetInvocationList()  
             select (EventHandler)d;  
  }  
}

तो आप MyClass के बाहर से पूरी मंगलाचरण सूची तक पहुँच सकते हैं और अपने इच्छित किसी भी हैंडलर को अनसब्सक्राइब कर सकते हैं। उदाहरण के लिए:

myClass.MyEvent -= myClass.GetMyEventHandlers().Last();

मैंने यहाँ इस tecnique के बारे में एक पूरी पोस्ट लिखी है


2
क्या इसका मतलब यह है कि अगर वे मेरे बाद सदस्यता लेते हैं तो मैं गलती से एक अलग मिसाल (यानी मुझे नहीं) अनसब्सक्राइब कर सकता हूं?
डंबलड

@dumbledad निश्चित रूप से यह अंतिम पंजीकृत एक को डीरेगिस्टर करेगा। यदि आप किसी विशिष्ट अनाम प्रतिनिधि को गतिशील रूप से अनसब्सक्राइब करना चाहते हैं, तो आपको इसे किसी तरह पहचानने की आवश्यकता है। मैं फिर एक संदर्भ रखने का सुझाव देता हूं :)
LuckyLikey

यह बहुत अच्छा है, आप क्या कर रहे हैं, लेकिन मैं एक मामले की कल्पना नहीं कर सकता जहां यह उपयोगी हो सकता है। लेकिन मैं वास्तव में ओपी के प्रश्न को हल नहीं करता हूं। -> +1। IMHO, किसी को केवल अनाम प्रतिनिधियों का उपयोग नहीं करना चाहिए, यदि उन्हें बाद में डीरजिस्टर किया जाना चाहिए। इन्हें रखना बेवकूफी है -> बेहतर उपयोग विधि। मंगलाचरण सूची में सिर्फ कुछ प्रतिनिधि को हटाना बहुत यादृच्छिक और बेकार है। यदि मैं गलत हूं तो मुझे सही करों। :)
LuckyLikey

6

लंगड़ा दृष्टिकोण की तरह:

public class SomeClass
{
  private readonly IList<Action> _eventList = new List<Action>();

  ...

  public event Action OnDoSomething
  {
    add {
      _eventList.Add(value);
    }
    remove {
      _eventList.Remove(value);
    }
  }
}
  1. ईवेंट जोड़ने / हटाने के तरीकों को ओवरराइड करें।
  2. उन घटना संचालकों की सूची रखें।
  3. जब आवश्यक हो, उन सभी को साफ़ करें और दूसरों को फिर से जोड़ें।

यह काम नहीं कर सकता है या सबसे कुशल तरीका हो सकता है, लेकिन काम पूरा करना चाहिए।


14
अगर आपको लगता है कि यह लंगड़ा है, तो इसे पोस्ट न करें।
जेरी निक्सन

2

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


2

एक सरल उपाय:

बस अपने आप को पैरामीटर के रूप में ईवेंटहैंड चर पास करें। इवेंट यदि आपके पास ऐसा मामला है कि आप मल्टीथ्रेडिंग के कारण मूल निर्मित चर तक नहीं पहुंच सकते हैं, तो आप इसका उपयोग कर सकते हैं:

MyEventHandler foo = null;
foo = (s, ev, mehi) => MyMethod(s, ev, foo);
MyEvent += foo;

void MyMethod(object s, MyEventArgs ev, MyEventHandler myEventHandlerInstance)
{
    MyEvent -= myEventHandlerInstance;
    Console.WriteLine("I did it!");
}

क्या होगा यदि MyEvent को दो बार लागू किया गया है, इससे पहले कि MyEvent -= myEventHandlerInstance;वह भागा हो? यदि यह संभव है, तो आपको एक त्रुटि होगी। लेकिन यकीन है कि अगर यह मामला है im नहीं।
LuckyLikey

0

अगर आप इस प्रतिनिधि के साथ किसी वस्तु का संदर्भ चाहते हैं, तो आप Delegate.CreateDelegate (प्रकार, ऑब्जेक्ट लक्ष्य, MethodInfo methodInfo) का उपयोग कर सकते हैं। नेट प्रतिनिधि को लक्ष्य और methodInfo द्वारा बराबर मानता है।


0

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

इस उदाहरण में, मुझे DataGridViews के एक सेट के लिए मर्जकॉल्यूमिनेशन पैरामीटर को शामिल करने के लिए एक अनाम विधि का उपयोग करना होगा।

सही में सेट किए गए सक्षम पैरामीटर के साथ मर्जकॉल्यूमिनेशन विधि का उपयोग करके घटना को सक्षम करता है जबकि इसे गलत अक्षमता के साथ उपयोग करता है।

static Dictionary<DataGridView, PaintEventHandler> subscriptions = new Dictionary<DataGridView, PaintEventHandler>();

public static void MergeColumns(this DataGridView dg, bool enable, params ColumnGroup[] mergedColumns) {

    if(enable) {
        subscriptions[dg] = (s, e) => Dg_Paint(s, e, mergedColumns);
        dg.Paint += subscriptions[dg];
    }
    else {
        if(subscriptions.ContainsKey(dg)) {
            dg.Paint -= subscriptions[dg];
            subscriptions.Remove(dg);
        }
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.