2009-11-18 17 views
10

मैं एक RAII कक्षा बना रहा था जो System.Windows.Form नियंत्रण में लेता है, और इसके कर्सर को सेट करता है। और विनाशक में यह कर्सर को उस चीज़ पर वापस सेट करता है जो यह था।क्या आरएआईआई सी # में उपयोग करने के लिए सुरक्षित है? और अन्य कचरा भाषा एकत्रित करना?

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

+2

FYI करें, सी # सी के रूप में ही अर्थ में विनाशकर्ता ++ जरूरत नहीं है। इसमें फाइनलाइज़र होते हैं, लेकिन उन्हें कचरा संग्रहण समय पर बुलाया जाता है, न कि जब वस्तु दायरे से बाहर हो जाती है। –

उत्तर

19

यह एक बहुत ही बुरा विचार है।

अंतिम नहीं कहा जाता है जब चर के दायरे से बाहर निकलते हैं। वस्तु को कचरा इकट्ठा करने से पहले उन्हें किसी बिंदु पर बुलाया जाता है, जो बाद में बहुत लंबा हो सकता है।

इसके बजाय, आप IDisposable लागू करना चाहते हैं, और उसके बाद कॉल करने का उपयोग कर सकते हैं:

using (YourClass yc = new YourClass()) 
{ 
    // Use yc in here 
} 

कि Dispose स्वचालित रूप से कॉल करेंगे।

सी # में अंतिम रूप से बहुत कम आवश्यक हैं - वे केवल तभी आवश्यक होते हैं जब आप सीधे स्वयं एक अप्रबंधित संसाधन (जैसे एक विंडोज हैंडल) हो। अन्यथा, आपके पास आमतौर पर कुछ प्रबंधित रैपर वर्ग (उदा। FileStream) होती है, यदि इसे एक की आवश्यकता होती है तो अंतिमकर्ता होगा।

नोट है कि आप केवल किसी भी इस बात का जरूरत है आप संसाधन हैं जब साफ किया जा करने के लिए - नेट में सबसे कक्षाएं IDisposable को लागू नहीं है।

संपादित करें: बस के बारे में घोंसले टिप्पणी का जवाब करने के लिए, मैं मानता हूँ यह थोड़ा बदसूरत हो सकता है, लेकिन आप using बयान बहुत अक्सर मेरे अनुभव में की जरूरत नहीं होनी चाहिए। आप भी कर सकते हैं घोंसला usings खड़ी इस तरह, इस मामले में जहां आप दो सीधे आसन्न लोगों मिल गया है में:

using (TextReader reader = File.OpenText("file1.txt")) 
using (TextWriter writer = File.CreateText("file2.txt")) 
{ 
    ... 
} 
+0

क्या यह सुनिश्चित करने के लिए पर्याप्त 'उपयोग' का उपयोग कर रहा है कि विनाशक को नीचे दिए गए @ जेरेडपार के रूप में बुलाया जाएगा? अगर यह गारंटी देता है, तो मैं इसके बजाय पहचान क्यों लागू करूं? –

+0

आईडीआईज़ेप द्वारा मेरा मतलब है IDISposable –

+0

@ नेट नागरिक - नहीं, यह विनाशक/अंतिमकर्ता को नहीं बुलाएगा। यह सिर्फ आईडीस्पोज़ेबल कॉल करेगा। यह अंतिमकरण को प्रभावित नहीं करता है (जब तक आप पाठ्यक्रम का निपटान करने में जीसी.SuppressFinalize कहते हैं!) –

1

IDisposable पैटर्न का उपयोग करें यदि आप सी # में आरए II-तरह की बातें करना चाहता हूँ

11

आप जानते हैं, बहुत से स्मार्ट लोग कहते हैं, "अगर आप सी # में आरएआईआई लागू करना चाहते हैं तो IDISposable का उपयोग करें, और मैं इसे अभी नहीं खरीदता हूं। मुझे पता है कि मैं यहां अल्पसंख्यक हूं, लेकिन जब मैं देखता हूं "उपयोग (ब्लाह) {foo (blah);}" मैं स्वचालित रूप से सोचता हूं "ब्लाह में एक अप्रबंधित संसाधन होता है जिसे फू के रूप में जल्द ही आक्रामक तरीके से साफ करने की आवश्यकता होती है (या फेंकता है) ताकि कोई और उस संसाधन का उपयोग कर सके। " मुझे नहीं लगता कि "ब्ला में कुछ भी दिलचस्प नहीं है लेकिन कुछ अर्थात् महत्वपूर्ण उत्परिवर्तन होने की आवश्यकता है, और हम चरित्र '}' द्वारा उस अर्थात् महत्वपूर्ण ऑपरेशन का प्रतिनिधित्व करने जा रहे हैं - कुछ ढेर जैसे कुछ उत्परिवर्तन होना चाहिए पॉप या कुछ ध्वज रीसेट या जो कुछ भी होना है।

मैं कहता हूं कि यदि आपके पास कोई अर्थपूर्ण रूप से महत्वपूर्ण ऑपरेशन है जो कुछ पूरा होने पर किया जाना है, तो हमारे पास इसके लिए एक शब्द है और शब्द "आखिरकार" है। यदि ऑपरेशन महत्वपूर्ण है, तो इसे एक बयान के रूप में प्रस्तुत किया जाना चाहिए जिसे आप वहां देख सकते हैं और दाएं-घुंघराले-ब्रेस के अदृश्य दुष्प्रभाव पर ब्रेकपॉइंट डाल सकते हैं।

तो चलिए अपने विशेष ऑपरेशन के बारे में सोचें।आप प्रतिनिधित्व करते हैं करना चाहते हैं:

var oldPosition = form.CursorPosition; 
form.CursorPosition = newPosition; 
blah; 
blah; 
blah; 
form.CursorPosition = oldPosition; 

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

अब आपके पास एक निर्णय बिंदु है। अगर ब्लाह फेंकता है तो क्या होगा? अगर ब्लाह फेंकता है तो कुछ अप्रत्याशित हुआ। अब आपको पता नहीं है कि राज्य "फॉर्म" क्या है; हो सकता है कि यह "फॉर्म" में कोड हो जो फेंक दिया हो। यह कुछ उत्परिवर्तन के माध्यम से आधा रास्ते हो सकता था और अब कुछ पूरी तरह से पागल राज्य में है।

उस स्थिति को देखते हुए, आप क्या करना चाहते हैं?

1) किसी और को समस्या पेंट करें। कुछ मत करो। उम्मीद है कि कॉल स्टैक पर कोई और जानता है कि इस स्थिति से कैसे निपटें। कारण यह है कि फॉर्म पहले से ही खराब स्थिति में है; तथ्य यह है कि उसका कर्सर सही जगह पर नहीं है इसकी चिंताओं में से कम से कम है। ऐसा कुछ न करें जो पहले से ही नाजुक है, यह एक बार अपवाद की सूचना दी गई है।

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

3) जटिल तर्क लिखें जो (2) की समस्याओं को संभालता है - अपवाद को पकड़ता है, और उसके बाद कर्सर की स्थिति को एक अलग प्रयास-पकड़-अंत में ब्लॉक में रीसेट करने का प्रयास करता है जो नए अपवाद को दबाता है और फिर से फेंकता है मूल अपवाद यह सही होने के लिए मुश्किल हो सकता है, लेकिन आप इसे कर सकते हैं।

सचमुच, मेरा विश्वास यह है कि सही उत्तर लगभग हमेशा (1) होता है। अगर कुछ गड़बड़ गलत हो गई है, तो आप सुरक्षित रूप से तर्क नहीं दे सकते कि नाजुक राज्य के लिए और क्या उत्परिवर्तन कानूनी हैं। यदि आप नहीं जानते कि अपवाद को कैसे संभालना है तो यूपी दें।

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

(3) बहुत सारे काम की तरह लगता है।

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

+5

'आखिरकार' अव्यवस्था है। लोग क्या चाहते हैं आरएआईआई व्यक्त करने का एक संक्षिप्त तरीका है। सी #/.NET का डिजाइन विशेष रूप से विशेष मामलों ('लॉक', 'उपयोग' के लिए विशेष संरचना प्रदान करने की कोशिश करने के बजाय, आरएआईआई की भारी उपयोगिता में कारक नहीं था। इसी कारण से लोग .NET में आरएआईआई पर निर्भर नहीं होने का प्रयास करते हैं लेकिन कभी-कभी यह एक जोड़ा ऑपरेशन व्यक्त करने का सबसे स्वाभाविक तरीका है। –

+0

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

+2

मैं @ कोनराड से सहमत हूं। इसके अलावा, मुझे 'अंततः' सुंदर अप्राकृतिक लगता है। यदि आपके पास आपके फ़ंक्शन से बाहर निकलने की कोशिश के अंदर रिटर्न स्टेटमेंट है, तो अधिकांश लोग कोड को तुरंत वापस आने की उम्मीद करते हैं। लेकिन यह बदले में अंत में कूद जाएगा और फिर वापस आ जाएगा। इसी तरह यदि आप आखिरकार ब्लॉक को देख रहे हैं, तो आप अगली पंक्ति को आखिरकार निष्पादित करने की उम्मीद करते हैं, लेकिन ऊपर वर्णित परिदृश्य में यह कभी-कभी वापस भी आ सकता है। यह गोटो की तरह है लेकिन छिपे हुए कोड के साथ। –

5

संपादित करें: स्पष्ट रूप से एरिक और मैं इस उपयोग पर असहमति पर हैं।: ओ

आप कुछ इस तरह का उपयोग कर सकते हैं:

public sealed class CursorApplier : IDisposable 
{ 
    private Control _control; 
    private Cursor _previous; 

    public CursorApplier(Control control, Cursor cursor) 
    { 
     _control = control; 
     _previous = _control.Cursor; 
     ApplyCursor(cursor); 
    } 

    public void Dispose() 
    { 
     ApplyCursor(_previous); 
    } 

    private void ApplyCursor(Cursor cursor) 
    { 
     if (_control.Disposing || _control.IsDisposed) 
      return; 

     if (_control.InvokeRequired) 
      _control.Invoke(new MethodInvoker(() => _control.Cursor = cursor)); 
     else 
      _control.Cursor = cursor; 
    } 
} 

// then... 

using (new CursorApplier(control, Cursors.WaitCursor)) 
{ 
    // some work here 
} 
+3

एरिक के जवाब पर टिप्पणियों से: "दूसरी ओर उपयोग का बयान आरआईएए से काफी साफ है" - मैं असहमत हूं। जबकि 'उपयोग' निश्चित रूप से अधिक * स्पष्ट * है, आरएआईआई सी ++ में (या कहें) वीबी 6 में ऐसी स्थापित मुहावरे है कि इसका उपयोग अप्रत्याशित छिपी हुई कार्रवाई के बजाय एक अपेक्षा बन गया है। बशर्ते ऑब्जेक्ट का विनाशक पूरी तरह से अनुचित, अनियंत्रित और अप्रत्याशित कुछ करके कम से कम आश्चर्य के सिद्धांत का उल्लंघन नहीं करता है, इसका उपयोग 'उपयोग' के रूप में स्पष्ट और साफ है, और कम वर्बोज़ (क्योंकि * प्रत्येक * ब्लॉक एक निहित है ' अपने स्थानीय स्टैक चर के ऊपर 'का उपयोग कर) –

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