2010-02-16 16 views
7

मेरे पास .NET प्रतिनिधियों के बारे में एक साधारण सवाल है।सी # प्रतिनिधियों, संदर्भ संकल्प समय

public void Invoke(Action<T> action) 
    { 
     Invoke(() => action(this.Value)); 
    } 

    public void Invoke(Action action) 
    { 
     m_TaskQueue.Enqueue(action); 
    } 

पहले समारोह this.Value के लिए एक संदर्भ encloses: मैं कुछ इस तरह है कहो। रनटाइम के दौरान, जब पहली, जेनेरिक पैरामीटर के साथ विधि कहा जाता है, तो यह किसी अन्य तरीके से this.Value प्रदान करेगा, लेकिन कैसे? ये मेरे मन में आया:

  • मूल्य (struct) द्वारा कॉल - this.Value के वर्तमान मूल्य से पारित कर दिया जाता है, इसलिए यदि m_TaskQueue यह 5 मिनट बाद कार्यान्वित करता है, मूल्य हाल ही में अपनी स्थिति में नहीं जाएगा, यह पहली बार संदर्भित होने पर यह भी होगा।
  • संदर्भ द्वारा कॉल (संदर्भ प्रकार) - तो Value का सबसे हाल ही राज्य कार्रवाई के निष्पादन के दौरान संदर्भित किया जाएगा, लेकिन अगर मैं कार्रवाई के क्रियान्वयन से पहले एक और संदर्भ के लिए this.Value बदलने के लिए, यह अभी भी पुराने संदर्भ
  • की ओर इशारा करते हो जाएगा
  • नाम से कॉल करें (दोनों) - जहां this.Value का मूल्यांकन किया जाएगा जब कार्रवाई कहलाती है। मेरा मानना ​​है कि वास्तविक कार्यान्वयन this का संदर्भ रखेगा, फिर उस प्रतिनिधि के वास्तविक निष्पादन के दौरान Value का मूल्यांकन करें क्योंकि नाम से कोई कॉल नहीं है।

मुझे लगता है कि यह नाम शैली का कॉल होगा लेकिन कोई दस्तावेज नहीं मिला है, इसलिए यह सोच रहा है कि यह एक अच्छी तरह से परिभाषित व्यवहार है या नहीं। यह वर्ग स्कैला या एरलांग में एक अभिनेता की तरह कुछ है इसलिए मुझे इसे थ्रेड सुरक्षित होने की आवश्यकता है। मैं Invoke फ़ंक्शन को Value पर फ़्रेज़ करने के लिए तत्पर नहीं चाहता हूं, जो this ऑब्जेक्ट m_TaskQueue द्वारा सुरक्षित थ्रेड में किया जाएगा।

उत्तर

18

मुझे यह बताकर अपने प्रश्न का उत्तर दें कि हम वास्तव में किस कोड को उत्पन्न करते हैं। मैं आपके भ्रमित नामित अन्य आमंत्रण विधि का नाम बदलूंगा; यह समझना जरूरी नहीं है कि यहां क्या हो रहा है।

class C<T> 
{ 
    public T Value; 

    private class CLOSURE 
    { 
    public Action<T> ACTION; 
    public C<T> THIS; 
    public void METHOD() 
    { 
     this.ACTION(this.THIS.Value); 
    } 
    } 

    public void Invoke(Action<T> action) 
    { 
     CLOSURE closure = new CLOSURE(); 
     closure.THIS = this; 
     closure.ACTION = action; 
     Frob(new Action(closure.METHOD)); 
    } 
    public void Frob(Action action) 
    { // whatever 
    } 
} 

कि आपके प्रश्न का उत्तर:

तुमने कहा

class C<T> 
{ 
    public T Value; 
    public void Invoke(Action<T> action) 
    { 
     Frob(() => action(this.Value)); 
    } 
    public void Frob(Action action) 
    { // whatever 
    } 
} 

संकलक है जैसे कि आप वास्तव में लिखा था कोड उत्पन्न मान लीजिए?

+0

बहुत धन्यवाद, यह बहुत है क्रिस्टल अब स्पष्ट :) –

7

प्रतिनिधिमंडल चर के संदर्भ को संग्रहीत करता है, न कि इसके मूल्य। आप वर्तमान मान तो रखना चाहते हैं (यह मानते हुए यह एक मान प्रकार है) आप इसे की स्थानीय प्रतिलिपि बनाने की जरूरत है:

public void Invoke(Action<T> action) 
{ 
    var localValue = this.Value; 
    Invoke(() => action(localValue)); 
} 

यदि यह एक परिवर्तनशील संदर्भ प्रकार आप एक स्थानीय क्लोन बना सकता है/गहरी प्रतिलिपि ।

+1

अनिवार्य एरिक लिपर्ट प्लग: http://blogs.msdn.com/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-cononsidered-harmful.aspx –

3

असली कुंजी यह याद रखना है कि दायरा व्यंग्यात्मक है; यह कुछ ऐसा है जो संकलक का ख्याल रखता है। तो यह चर पर कब्जा करता है, उनके मान नहीं। चाहे वे मान मूल्य प्रकार या संदर्भ प्रकार हैं, एक और मामला पूरी तरह से है।

हो सकता है कि प्रतिनिधि के व्यवहार में फेरबदल के साथ ही कुछ अधिक चरम उदाहरण में मदद मिलेगी:

var myVariable = "something"; 
Action a =() => Console.WriteLine(myVariable); 
myVariable = "something else entirely" 
a(); 

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

संबंधित मुद्दे