2010-11-21 28 views
39

मैं एक ऐसा फ़ंक्शन बनाने की कोशिश कर रहा हूं जो एक ऐसा क्रिया बना सके जो पूर्णांक पारित हो जाए। हालांकि मेरा पहला प्रयास मुझे एक त्रुटि दे रहा है "रेफरी या अज्ञात विधि निकाय के अंदर पैरामीटर बाहर "।सी # किसी अज्ञात विधि निकाय के अंदर रेफरी या आउट पैरामीटर का उपयोग नहीं कर सकता

public static class IntEx { 
    public static Action CreateIncrementer(ref int reference) { 
     return() => { 
      reference += 1; 
     }; 
    } 
} 

मुझे समझ में क्यों संकलक यह पसंद नहीं है, लेकिन फिर भी मैं एक अच्छा incrementer कारखाना है कि किसी भी पूर्णांक को इंगित कर सकते हैं प्रदान करने के लिए एक सुंदर तरीका है करना चाहते हैं। एक ही तरीका है मैं यह करने के दिखाई दे रही है की तरह कुछ है निम्नलिखित:

public static class IntEx { 
    public static Action CreateIncrementer(Func<int> getter, Action<int> setter) { 
     return() => setter(getter() + 1); 
    } 
} 

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

+2

क्या यह एक सरल उदाहरण है? एक्स ++ का उपयोग क्यों नहीं करें? इस कक्षा – Gishu

+2

@Gishu की एक और कक्षा बढ़ती स्थिति क्यों है यह एक सरलीकृत उदाहरण है; बड़े उपयोग के मामले को समझाना कठिन होता है, लेकिन यह सब एक एक्शन फैक्ट्री बनाने के लिए नीचे आता है जो मूल्य प्रकारों पर संचालन कर सकता है। असुरक्षित संदर्भ का उपयोग कर प्रबंधित मोड के दिलचस्प कामकाज के लिए –

उत्तर

24

यह संभव नहीं है।

कंपाइलर स्वचालित रूप से जेनरेट किए गए क्लोजर क्लास में फ़ील्ड में अज्ञात विधियों द्वारा उपयोग किए जाने वाले सभी स्थानीय चर और पैरामीटर को बदल देगा।

सीएलआर ref प्रकारों को फ़ील्ड में संग्रहीत करने की अनुमति नहीं देता है।

उदाहरण के लिए, यदि आप स्थानीय चर में एक मान प्रकार को ref पैरामीटर के रूप में पास करते हैं, तो मूल्य का जीवनकाल इसके ढेर फ्रेम से आगे बढ़ेगा।

30

ठीक है, मैं पाया है कि यह वास्तव में संभव संकेत के साथ असुरक्षित संदर्भ में, यदि:

public static class IntEx { 
    unsafe public static Action CreateIncrementer(int* reference) { 
     return() => { 
      *reference += 1; 
     }; 
    } 
} 

हालांकि, कचरा कलेक्टर कहर इस के साथ कचरा संग्रहण के दौरान आपके संदर्भ ले जाकर बरपाने ​​कर सकते हैं, के रूप में निम्नलिखित इंगित करता है:

class Program { 
    static void Main() { 
     new Program().Run(); 
     Console.ReadLine(); 
    } 

    int _i = 0; 
    public unsafe void Run() { 
     Action incr; 
     fixed (int* p_i = &_i) { 
      incr = IntEx.CreateIncrementer(p_i); 
     } 
     incr(); 
     Console.WriteLine(_i); // Yay, incremented to 1! 
     GC.Collect(); 
     incr(); 
     Console.WriteLine(_i); // Uh-oh, still 1! 
    } 
} 

कोई भी मेमोरी में एक विशिष्ट स्थान पर चर को पिन करके इस समस्या को हल कर सकता है। यह निर्माता के लिए निम्न जोड़कर किया जा सकता है:

public Program() { 
     GCHandle.Alloc(_i, GCHandleType.Pinned); 
    } 

कि वस्तु आने-जाने से कचरा कलेक्टर रहता है, इसलिए हम वास्तव में क्या देख रहे हैं। हालांकि फिर आपको पिन को रिहा करने के लिए एक विनाशक जोड़ना होगा, और यह ऑब्जेक्ट के पूरे जीवनकाल में स्मृति को टुकड़ा करता है। वास्तव में कोई आसान नहीं है। यह सी ++ में अधिक समझ में आता है, जहां सामान चारों ओर स्थानांतरित नहीं होता है, और संसाधन प्रबंधन पाठ्यक्रम के बराबर है, लेकिन सी # में इतना नहीं है जहां सभी को स्वचालित होना चाहिए।

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

(और हाँ, कि जिस तरह से मैं इसे सवाल पूछने से पहले काम कर रहा था, लेकिन अभी यह पता लगाने की अगर वहाँ एक तरह से मैं अपने सभी संदर्भ < पूर्णांक > सदस्य चर से छुटकारा पाने सकता था कोशिश कर रहा था और बस नियमित ints का उपयोग ओह ठीक है।)

+4

+1। –

+0

GCHandle.Alloc की कोई आवश्यकता नहीं - केवल निश्चित कथन घुंघराले ब्रेसिज़ का विस्तार करें। –

+0

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

2

यह रनटाइम के लिए एक उपयोगी सुविधा हो सकती है ताकि वे अपने दृढ़ता को रोकने के लिए एक तंत्र के साथ परिवर्तनीय संदर्भों के निर्माण की अनुमति दे सकें; इस तरह की एक सुविधा ने एक इंडेक्सर को सरणी की तरह व्यवहार करने की इजाजत दी होगी (उदाहरण के लिए एक शब्दकोश < इंट 32, प्वाइंट> "myDictionary [5] .X = 9;") के माध्यम से पहुंचा जा सकता है।मुझे लगता है कि ऐसी सुविधा सुरक्षित रूप से प्रदान की जा सकती थी अगर ऐसे संदर्भ अन्य प्रकार की वस्तुओं के लिए कमजोर नहीं हो सकते थे, न ही खेतों के रूप में उपयोग किए जाते थे, न ही संदर्भ के द्वारा पारित किया गया था (चूंकि किसी भी संदर्भ को संदर्भित किया जा सकता है, संदर्भ से पहले गुंजाइश से बाहर हो जाएगा खुद होगा)। दुर्भाग्यवश, सीएलआर ऐसी सुविधा प्रदान नहीं करता है।

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

बीटीडब्लू, मेरी समझ यह है कि जावा में बाय-वैल्यू सेमेन्टिक्स का उपयोग बंद होता है, जबकि .NET में उन संदर्भ-संदर्भ होते हैं। मैं उप-संदर्भ अर्थशास्त्र के लिए कभी-कभी उपयोगों को समझ सकता हूं, लेकिन डिफ़ॉल्ट रूप से संदर्भ का उपयोग करना एक संदिग्ध निर्णय लगता है, जो वीबी 6 के माध्यम से वीबी संस्करणों के लिए डिफ़ॉल्ट बाय-रेफरेंस पैरामीटर-पासिंग सेमेन्टिक्स के उपयोग के समान होता है। यदि कोई फ़ंक्शन कॉल करने के लिए प्रतिनिधि बनाते समय किसी चर के मान को कैप्चर करना चाहता है (उदाहरण के लिए यदि कोई प्रतिनिधि प्रतिनिधि बनने पर एक्स के मान का उपयोग करके माईफंक्शन (एक्स) को कॉल करने के लिए एक प्रतिनिधि चाहता है), क्या यह लैम्ब्डा का उपयोग करना बेहतर है एक अतिरिक्त अस्थायी के साथ, या यह केवल एक प्रतिनिधि कारखाने का उपयोग करना बेहतर है और Lambda अभिव्यक्तियों से परेशान नहीं है।

+1

हाँ, एरिक लिपर्ट ने इसके बारे में एक ब्लॉग लिखा था। http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx। मेरे पास वास्तव में कोड है जो इस व्यवहार पर निर्भर करता है; उदाहरण के लिए फ्रेमकाउंट सदस्य चर की आवश्यकता के बिना OpenGL लूप के लिए एक फ्रेमकाउंट रखना। मुझे सी ++ स्पेक बेहतर पसंद है जो या तो अर्थपूर्ण है। –

+1

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

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