2009-04-29 22 views
9

अनाम विधियों के बारे में एक अच्छी बात यह है कि मैं वेरिएबल्स संदर्भ में स्थानीय चर का उपयोग कर सकता हूं। क्या कोई कारण है कि यह आउट-पैरामीटर और फ़ंक्शन परिणामों के लिए क्यों काम नहीं करता है?अनाम विधियों का दायरा

function ReturnTwoStrings (out Str1 : String) : String; 
begin 
    ExecuteProcedure (procedure 
        begin 
         Str1 := 'First String'; 
         Result := 'Second String'; 
        end); 
end; 

पाठ्यक्रम का बहुत कृत्रिम उदाहरण, लेकिन मैं कुछ परिस्थितियों में भाग गया जहां यह उपयोगी होता।

जब मैं इसे संकलित करने का प्रयास करता हूं, तो संकलक शिकायत करता है कि वह "प्रतीकों को कैप्चर नहीं कर सकता"। इसके अलावा, जब मैंने ऐसा करने की कोशिश की तो मुझे एक आंतरिक त्रुटि मिली।

संपादित मैं सिर्फ महसूस किया कि यह सामान्य मापदंडों के लिए

... (List : TList) 

की तरह है कि के रूप में समस्या पैदा करने वाले अन्य मामलों के रूप में काम करता है न? कौन गारंटी देता है कि जब भी अनाम विधि निष्पादित की जाती है तो संदर्भ अभी भी एक जीवित वस्तु को इंगित कर रहा है?

+0

संदर्भित पैरामीटर के बजाय पॉइंटर का उपयोग करें। – MajidTaheri

उत्तर

20

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

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

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

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

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

स्थिति संदर्भ द्वारा पारित पैरामीटर के साथ स्थिति अलग है क्योंकि उम्मीदें कॉलर अलग हैं। एक प्रोग्रामर यह कर रहा है:

procedure GetSomeString(out s: string); 
// ... 
GetSomeString(s); 
यदि GetSomeString s चर में पारित करने के लिए एक संदर्भ रखने के लिए थे

अत्यंत आश्चर्य होगा दूसरी ओर:।

procedure AddObject(obj: TObject); 
// ... 
AddObject(TObject.Create); 

यह आश्चर्य की बात नहीं है, क्योंकि बहुत नाम का तात्पर्य यह है कि कि AddObject एक संदर्भ रहता है, कुछ राज्यव्यापी स्टोर में पैरामीटर जोड़ना। चाहे वह राज्यव्यापी स्टोर बंद होने के रूप में है या नहीं, AddObject विधि का कार्यान्वयन विवरण है।

+0

आप ढेर के बारे में क्यों बात करते हैं? कैप्चर किए गए वेरिएबल स्टैक में स्टोर नहीं करते हैं, बल्कि छुपे ऑब्जेक्ट में, जो इंटरफ़ेस लागू करता है। अर्थात। var एम, एन: पूर्णांक; - अगर अज्ञात विधि में केवल एन का उपयोग किया जाता है तो एम ढेर हो जाता है और एन छुपे ऑब्जेक्ट का एक क्षेत्र होगा। यह ढेर पर दिखाई नहीं देगा। क्या मैं कुछ गलत समझता हूं? – Alex

+0

@Alexander: बैरी स्थिति का वर्णन कर रहा था कि क्या होगा जब इसे कैप्चर करने और var पैरामीटर और फ़ंक्शन परिणामों को कैप्चर करने की अनुमति दी गई थी। चूंकि इसकी अनुमति नहीं है, स्टैक ओवरराइट वाली स्थिति नहीं होती है। –

+0

+1 @ बैरी: मैं कर सकता था उससे कहीं ज्यादा बेहतर स्पष्टीकरण। –

3

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

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

+0

अपने पहले बिंदु के बारे में: यह हर स्थानीय चर के लिए सच है, है ना? और दूसरे बिंदु पर: क्या होगा यदि मैं फिर भी कार्य परिणाम उत्पन्न करने के लिए अज्ञात विधि चाहता हूं? मैं आसानी से स्थानीय चर का उपयोग करके इसे अनुकरण कर सकता हूं, उस अज्ञात विधि में उस स्थानीय चर का उपयोग करें और फिर इसे बाद में परिणाम देने के लिए असाइन करें। – jpfollenius

+0

बस मेरी बात स्पष्ट करने के लिए: यदि मैं एक प्रतिनिधि के रूप में anonmouy विधि का उपयोग करता हूं, तो यह वही समस्या होगी, है ना? – jpfollenius

+1

नहीं, यह विधि के भीतर स्थानीय चर के लिए सच नहीं है। वे (मान लेंगे कि डेल्फी सी # की तरह है, वैसे भी) उन्हें ढेर पर रखकर पकड़ा जा सकता है - स्थानीय चर के किसी भी संदर्भ वास्तव में उस क्षेत्र में सभी स्थानीय चर के लिए "कंटेनर" के माध्यम से जाना होगा। संकलक विधि के भीतर स्थानीय चर के लिए ऐसा करने में सक्षम है क्योंकि यह जानता है कि कौन से चर कैप्चर किए जाएंगे - यह कोड * कॉलिंग * में इस विधि के स्थानीय चर के लिए नहीं कर सकता है। –

6

समस्या यह है कि आपका स्ट्र 1 वैरिएबल रिटर्नटवोस्ट्रिंग्स द्वारा "स्वामित्व" नहीं है, ताकि आपकी अनाम विधि इसे कैप्चर नहीं कर सके।

कारण यह कैप्चर नहीं कर सकता है, यह है कि संकलक अंतिम मालिक को नहीं जानता (कहीं भी रिटर्न टॉउस्ट्रिंग्स को कॉल करने के लिए कॉल स्टैक में) ताकि यह निर्धारित न हो कि इसे कहां से कैप्चर करना है।

संपादित करें: (जोड़ा Smasher की एक टिप्पणी के बाद)

गुमनाम तरीकों के मूल है कि वे चर (न कि उसका मान) पर कब्जा है।

एलन बाउर (कोडगियर) थोड़ा और about variable capturing in his blog बताता है।

एक C# question about circumventing your problem भी है।

+0

+1, बहुत तकनीकी कारण हालांकि – jpfollenius

0

मैं इसे एक अलग उत्तर में डाल रहा हूं क्योंकि आपका EDIT आपका प्रश्न वास्तव में अलग करता है।

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

आपका संपादन इंगित करता है कि आपको मूल्य प्रकार, संदर्भ प्रकार और var, Out, const और कोई पैरामीटर चिह्नित करने के प्रभाव के बारे में पुनर्विचार करने की आवश्यकता है।

चलो मान प्रकार की चीज़ पहले करें।

मूल्य प्रकारों के मूल्य स्टैक पर रहते हैं और एक प्रति-कार्य-कार्य व्यवहार करते हैं। (मैं उस पर एक उदाहरण शामिल करने का प्रयास करूंगा)।

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

जब आपके पास, var या const है, तो कोई प्रति नहीं होती है: विधि वास्तविक मान को पारित करेगी। Var के लिए, यह उस वास्तविक मान को बदलने की अनुमति देगा, क्योंकि यह उसमें अनुमति नहीं देगा। बाहर के लिए, आप वास्तविक मान को पढ़ने में सक्षम नहीं होंगे, लेकिन फिर भी वास्तविक मान लिखने में सक्षम होंगे।

संदर्भ प्रकारों के मान ढेर पर रहते हैं, इसलिए यदि आपके पास बाहर, var, const या कोई पैरामीटर अंकन नहीं है तो: यदि आप कुछ बदलते हैं, तो आप ढेर पर मान बदलते हैं।

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

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

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

यह संदर्भ पैरामीटर के साथ हमेशा एक समस्या है, भले ही आप अज्ञात विधियों का उपयोग करें या नहीं।

उदाहरण इस के लिए:

procedure TMyClass.AddObject(Value: TObject); 
begin 
    FValue := Value; 
end; 

procedure TMyClass.DoSomething(); 
begin 
    ShowMessage(FValue.ToString()); 
end; 

कौन गारंटी देता है कि जब कोई DoSomething कहता है, कि उदाहरण जहां FValue अंक अभी भी मौजूद है के लिए? उत्तर यह है कि जब आप FValue की मृत्यु हो जाती है तो आपको DoSomething को कॉल करके स्वयं को गारंटी देनी चाहिए। आपके संपादन के लिए समान है: अंतर्निहित उदाहरण की मृत्यु होने पर आपको अनाम विधि को कॉल नहीं करना चाहिए।

यह उन क्षेत्रों में से एक है जहां संदर्भ गिना जाता है या कचरा एकत्रित समाधान जीवन को आसान बनाते हैं: वहां का उदाहरण जीवित रखा जाएगा जब तक कि इसका अंतिम संदर्भ समाप्त नहीं हो जाता है (जो आपके मूल रूप से अपेक्षाकृत अधिक समय तक जीवित रहने का कारण बन सकता है!)।

तो, आपके संपादन के साथ, आपका प्रश्न वास्तव में अज्ञात तरीकों से संदर्भ टाइप किए गए पैरामीटर और आजीवन प्रबंधन का उपयोग करने के प्रभावों में बदल जाता है।

उम्मीद है कि मेरा उत्तर आपको उस क्षेत्र में जाने में मदद करता है।

- जेरोइन

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