2009-12-18 16 views
43

मुझे लगता है मैं बंद की एक बहुत सभ्य समझ है, उन्हें कैसे उपयोग करने के लिए, और जब वे उपयोगी हो सकता है। लेकिन मुझे समझ में नहीं आता कि वे वास्तव में स्मृति में दृश्यों के पीछे कैसे काम करते हैं। कुछ उदाहरण कोड:दृश्यों के पीछे बंद कैसे काम करते हैं? (सी #)

public Action Counter() 
{ 
    int count = 0; 
    Action counter =() => 
    { 
     count++; 
    }; 

    return counter; 
} 

आम तौर पर, यदि {count} बंद द्वारा कब्जा कर लिया नहीं गया था, इसके जीवन चक्र काउंटर() विधि के दायरे वाला हो जाएगा, और यह पूर्ण होने के बाद यह ढेर के बाकी के साथ दूर जाना होगा काउंटर के लिए आवंटन()। क्या होता है जब यह बंद हो जाता है? काउंटर() के इस कॉल के लिए पूरे स्टैक आवंटन के आसपास छड़ी है? क्या यह ढेर में {count} की प्रतिलिपि बनाता है? क्या यह वास्तव में ढेर पर आवंटित नहीं होता है, लेकिन संकलक द्वारा बंद होने के रूप में मान्यता प्राप्त है और इसलिए हमेशा ढेर पर रहता है?

इस विशेष प्रश्न के लिए, मैं मुख्य रूप से यह कैसे सी # में काम करता है में रुचि हूँ, लेकिन अन्य भाषाओं कि बंद का समर्थन के खिलाफ तुलना करने के लिए विरोध नहीं किया जाएगा।

+0

शानदार सवाल। मुझे यकीन नहीं है, लेकिन हाँ, आप सी # में स्टैक फ्रेम को चारों ओर रख सकते हैं। जनरेटर इसे हर समय उपयोग करते हैं (डेटा संरचनाओं के लिए चीज LINQ) जो हुड के नीचे उपज पर भरोसा करते हैं। उम्मीद है कि मैं निशान से बाहर नहीं हूँ। अगर मैं हूं, तो मैं एक बड़ा सौदा सीखूंगा। –

+2

उपज विधि को एक मशीन के साथ एक अलग वर्ग में बदल देती है। ढेर को चारों ओर नहीं रखा जाता है, लेकिन स्टैक स्थिति को कंपाइलर से उत्पन्न कक्षा – thecoop

+0

@ थेकोप में कक्षा राज्य में स्थानांतरित किया जाता है, क्या आपके पास यह एक लिंक है? –

उत्तर

31

कंपाइलर (रनटाइम के विपरीत) एक और वर्ग/प्रकार बनाता है। आपके बंद होने के साथ फ़ंक्शन और आपके द्वारा बंद/घुमाए गए/कब्जे वाले किसी भी चर को उस कक्षा के सदस्यों के रूप में आपके कोड में फिर से लिखा जाता है। .Net में एक बंद इस छिपे हुए वर्ग के एक उदाहरण के रूप में लागू किया गया है।

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

+4

इस – Greg

+5

का उदाहरण देखने के लिए परावर्तक के साथ प्रश्न में कोड का निरीक्षण करें ... बस देखो आपके समाधान में सबसे उग्र नामित वर्ग। – Will

+2

क्या इसका मतलब है कि एक बंद होने के परिणामस्वरूप एक नया ढेर आवंटन होगा, भले ही मूल्य बंद हो जाए तो एक आदिम है? – Matt

45

आपका तीसरे अनुमान सही है। कंपाइलर इस प्रकार कोड उत्पन्न करेगा:

private class Locals 
{ 
    public int count; 
    public void Anonymous() 
    { 
    this.count++; 
    } 
} 

public Action Counter() 
{ 
    Locals locals = new Locals(); 
    locals.count = 0; 
    Action counter = new Action(locals.Anonymous); 
    return counter; 
} 

समझ में आता है?

इसके अलावा, आप तुलना के लिए कहा। वीबी और जेस्क्रिप्ट दोनों एक ही तरीके से बंद बनाते हैं।

+14

पर हमें विश्वास क्यों करना चाहिए? ओह यह सही है ... – ChaosPandion

0

धन्यवाद @HenkHolterman। चूंकि इसे एरिक द्वारा पहले ही समझाया गया था, इसलिए मैंने यह लिंक दिखाने के लिए लिंक जोड़ा कि संकलक किस वास्तविक वर्ग को बंद करने के लिए उत्पन्न करता है। मैं यह जोड़ना चाहता हूं कि सी # कंपाइलर द्वारा डिस्प्ले क्लास के निर्माण से मेमोरी लीक हो सकती है। उदाहरण के लिए फ़ंक्शन के अंदर एक इंट वेरिएबल होता है जिसे लैम्ब्डा अभिव्यक्ति द्वारा पकड़ा जाता है और वहां एक और स्थानीय चर होता है जो केवल एक बड़े बाइट सरणी का संदर्भ रखता है। कंपाइलर एक डिस्प्ले क्लास इंस्टेंस बनाएगा जो दोनों चर और ite int और बाइट सरणी के संदर्भों को धारण करेगा। लेकिन जब तक लैम्ब्डा का संदर्भ नहीं दिया जाता है तब तक बाइट सरणी कचरा नहीं होगी।

0

एरिक लिपर्ट का जवाब वास्तव में बिंदु को हिट करता है। हालांकि सामान्य रूप से स्टैक फ्रेम और कैप्चर करने की तस्वीर बनाने के लिए यह अच्छा लगेगा। ऐसा करने के लिए यह थोड़ा और जटिल उदाहरण देखने में मदद करता है। यहाँ

public class Scorekeeper { 
    int swish = 7; 

    public Action Counter(int start) 
    { 
     int count = 0; 
     Action counter =() => { count += start + swish; } 
     return counter; 
    } 
} 

और मैं क्या सोचते बराबर होगा (अगर हम भाग्यशाली हैं एरिक Lippert कि क्या यह वास्तव में सही है या नहीं पर टिप्पणी करेंगे) है:

यहाँ कैप्चरिंग कोड है

private class Locals 
{ 
    public Locals(Scorekeeper sk, int st) 
    { 
     this.scorekeeper = sk; 
     this.start = st; 
    } 

    private Scorekeeper scorekeeper; 
    private int start; 

    public int count; 

    public void Anonymous() 
    { 
    this.count += start + scorekeeper.swish; 
    } 
} 

public class Scorekeeper { 
    int swish = 7; 

    public Action Counter(int start) 
    { 
     Locals locals = new Locals(this, start); 
     locals.count = 0; 
     Action counter = new Action(locals.Anonymous); 
     return counter; 
    } 
} 

बिंदु यह है कि स्थानीय वर्ग पूरे स्टैक फ्रेम के लिए विकल्प देता है और प्रत्येक बार काउंटर विधि का आह्वान किया जाता है। आम तौर पर ढेर फ्रेम में 'इस', प्लस विधि तर्क, साथ ही स्थानीय चर के संदर्भ भी शामिल हैं। (जब नियंत्रण ब्लॉक दर्ज किया जाता है तो स्टैक फ्रेम भी प्रभावी रूप से विस्तारित होता है।)

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

इस पर आधारित, हम निम्नलिखित मानसिक मॉडल का उपयोग कर सकते हैं: ढेर फ्रेम ढेर पर (ढेर के बजाय) पर रखा जाता है, जबकि ढेर में केवल ढेर पर मौजूद स्टैक फ्रेम में पॉइंटर्स होते हैं। लैम्ब्डा विधियों में ढेर के फ्रेम में एक सूचक होता है। यह प्रबंधित स्मृति का उपयोग करके किया जाता है, इसलिए फ्रेम ढेर पर चारों ओर चिपक जाता है जब तक इसकी आवश्यकता नहीं होती है।

स्पष्ट रूप से संकलक केवल ढेर का उपयोग करके इसे कार्यान्वित कर सकता है जब ढेर वस्तु को लैम्ब्डा बंद करने के लिए आवश्यक है।

मुझे इस मॉडल के बारे में क्या पसंद है यह 'उपज रिटर्न' के लिए एक एकीकृत तस्वीर प्रदान करता है। हम एक इटरेटर विधि (उपज रिटर्न का उपयोग करके) के बारे में सोच सकते हैं जैसे कि यह ढेर फ्रेम के ढेर के दौरान उपयोग के लिए, कॉलर में स्थानीय चर में संग्रहीत ढेर पर संदर्भित पॉइंटर बनाया गया था।

+0

यह सही नहीं है; बाहरी 'स्कीश' को बाहरी वर्ग 'स्कोरकीपर' से कैसे पहुंचाया जा सकता है? क्या होता है यदि 'स्टार्ट' उत्परिवर्तित हो जाता है? लेकिन इस बिंदु पर अधिक: स्वीकार्य उत्तर के साथ आठ वर्षीय प्रश्न का उत्तर देने में क्या मूल्य है? –

+0

यदि आप जानना चाहते हैं कि असली कोडेजन क्या है, तो ILDASM या आईएल-टू-सोर्स डिससेबलर का उपयोग करें। –

+0

इसके बारे में सोचने के लिए एक बेहतर तरीका है "स्टैक फ्रेम" के बारे में सोचना बंद करना कुछ मौलिक है। ढेर केवल एक डेटा संरचना है जिसका उपयोग दो चीजों को लागू करने के लिए किया जाता है: ** सक्रियण ** और ** निरंतरता **। यही है: विधि के सक्रियण से जुड़े मूल्य क्या हैं, और इस विधि के बाद कौन सा कोड चलाना है? लेकिन स्टैक सक्रियण/निरंतरता जानकारी संग्रहीत करने के लिए केवल एक उपयुक्त डेटा संरचना है ** यदि विधि सक्रियण लाइफटाइम तार्किक रूप से एक स्टैक ** बनाते हैं। –

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