2009-07-16 16 views
28

यह कक्षा StreamWriter का उपयोग करती है और इसलिए IDisposable लागू करती है।क्या मैं सही ढंग से IDISposable लागू कर रहा हूँ?

public class Foo : IDisposable 
{ 
    private StreamWriter _Writer; 

    public Foo (String path) 
    { 
     // here happens something along the lines of: 
     FileStream fileWrite = File.Open (path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite); 
     _Writer = new StreamWriter (fileWrite, new ASCIIEncoding()); 
    } 

    public void Dispose() 
    { 
     Dispose (true); 
     GC.SuppressFinalize (this); 
    } 

    ~Foo() 
    { 
     Dispose (false); 
    } 

    protected virtual void Dispose (bool disposing) 
    { 
     if (_Disposed) { 
      return; 
     } 
     if (disposing) { 
      _Writer.Dispose(); 
     } 
     _Writer = null; 
     _Disposed = true; 
    } 
    private bool _Disposed; 
} 

}

वहाँ वर्तमान कार्यान्वयन से संबंधित कोई समस्या है? यानी, क्या मुझे अंतर्निहित FileStream मैन्युअल रूप से जारी करना होगा? Dispose(bool) सही ढंग से लिखा है?

+1

जांचना और यह सुनिश्चित करना न भूलें कि अस्वीकरण कॉल करने से पहले _Writer गैर-शून्य है। आपका निपटान एकाधिक कॉल w/o विफलता को सहन करने में सक्षम होना चाहिए। – user7116

+1

दरअसल वह पहले _Disposed जांचता है, तो यह ठीक है (हालांकि थ्रेड-सुरक्षित नहीं है)। –

+0

@mafutrct: अद्यतन उत्तर उपलब्ध है। – AnthonyWJones

उत्तर

39

यदि आपकी कक्षा सीधे अप्रबंधित संसाधनों का उपयोग नहीं करती है तो आपको IDISposable कार्यान्वयन के इस व्यापक संस्करण का उपयोग करने की आवश्यकता नहीं है।

एक साधारण

public virtual void Dispose() 
{ 

    _Writer.Dispose(); 
} 

पर्याप्त होगा।

यदि आपका उपभोक्ता आपके ऑब्जेक्ट को डिस्प्ले करने में विफल रहता है तो इसे सामान्य रूप से कॉल करने के लिए जीसीड किया जाएगा, _Writer द्वारा आयोजित ऑब्जेक्ट भी जीसीएड होगा और इसमें फाइनलजर होगा ताकि यह अभी भी इसे साफ कर सके अप्रबंधित संसाधन ठीक से।

संपादित

मैट द्वारा प्रदान की लिंक और दूसरों मैं निष्कर्ष है कि मेरा उत्तर यहाँ खड़ा पर आए हैं पर कुछ शोध किया करने के बाद। यहाँ कारण है कि: -

डिस्पोजेबल कार्यान्वयन "पैटर्न" के पीछे आधार यह एक दाय वर्ग पर (है कि मैं संरक्षित आभासी निपटान (bool मतलब), SuppressFinalize आदि marlarky) है कि एक उप-वर्ग पराक्रम पकड़ एक अप्रबंधित संसाधन पर।

हालांकि असली दुनिया में हमारे अधिकांश विशाल .NET डेवलपर्स कभी भी एक अप्रबंधित संसाधन के पास कहीं भी नहीं जाते हैं। यदि आपको "" को मापना था, तो आपके द्वारा .NET कोडिंग के प्रकार के लिए आप किस संभाव्यता के साथ आएंगे?

मान लें कि मेरे पास एक व्यक्ति प्रकार है (जो तर्क के लिए अपने खेतों में से एक में एक डिस्पोजेबल प्रकार है और इसलिए खुद को डिस्पोजेबल होना चाहिए)। अब मेरे पास ग्राहक, कर्मचारी इत्यादि हैं। क्या यह वास्तव में मेरे लिए "पैटर्न" के साथ व्यक्ति वर्ग को अव्यवस्थित करना उचित है यदि कोई व्यक्ति व्यक्ति को विरासत में ले लेता है और एक अप्रबंधित संसाधन रखना चाहता है?

कभी-कभी हम डेवलपर्स ऐसी परिस्थितियों की सापेक्ष संभावना के संबंध में कुछ सामान्य ज्ञान के बिना सभी संभावित परिस्थितियों के लिए कोड करने के प्रयास में चीजों को जटिल बना सकते हैं।

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

पुhew, मेरी छाती को दूर करने में खुशी हुई। ;)

+3

यह केवल तभी पर्याप्त होगा जब वर्ग को सील कर दिया गया हो। –

+0

@ मैट: क्या आप विस्तृत कर सकते हैं? – AnthonyWJones

+0

ऐसा करने का मेरा उत्तर पोस्ट किया गया। –

0

यदि आप स्ट्रीमवाइटर खोलते हैं, तो आपको इसे भी निपटाना होगा, या आपके पास रिसाव होगा।

+0

क्या आपका मतलब _FileStream_ है? – mafu

+7

आप शायद फ़ाइलस्ट्रीम का मतलब है, लेकिन स्ट्रीमवाइटर का निपटान वास्तव में फ़ाइलस्ट्रीम का निपटान भी करता है। –

+0

धन्यवाद, बहुत बुरा मैं जवाब के हिस्से के रूप में आपकी टिप्पणी को ऊपर नहीं उठा सकता। – mafu

16

आपको Finalize (विनाशक) विधि की आवश्यकता नहीं है क्योंकि आपके पास कोई अप्रबंधित वस्तुएं नहीं हैं।हालांकि, आपको Foo से विरासत प्राप्त करने वाली कक्षा में अप्रबंधित वस्तुओं, और इसलिए एक अंतिमकर्ता है, तो आपको कॉल को GC.SuppressFinalize पर रखना चाहिए।

यदि आप कक्षा को सील कर देते हैं, तो आप जानते हैं कि अप्रबंधित वस्तुएं कभी समीकरण में प्रवेश नहीं कर रही हैं, इसलिए protected virtual Dispose(bool) ओवरलोड, या GC.SuppressFinalize जोड़ने की आवश्यकता नहीं है।

संपादित करें:

@ इस के लिए AnthonyWJones की आपत्ति है कि यदि आप जानते हैं कि उपवर्गों अप्रबंधित वस्तुओं को संदर्भित नहीं होगा, पूरे Dispose(bool) और GC.SuppressFinalize अनावश्यक है। लेकिन अगर ऐसा है तो आपको वास्तव में के बजाय कक्षा internal बनाना चाहिए, और Dispose() विधि virtual होना चाहिए। यदि आप जानते हैं कि आप क्या कर रहे हैं, तो माइक्रोसॉफ्ट के सुझाए गए पैटर्न का पालन न करें, लेकिन आपको उन्हें तोड़ने से पहले नियमों को जानना और समझना चाहिए!

+0

क्या यह मानता है कि विरासत वर्ग सही तरीके से लागू करता है और अपने अंतिमकर्ता में केवल अप्रबंधित क्लीन अप कोड नहीं छोड़ा है? – AnthonyWJones

+0

नहीं, यह ऐसी कोई धारणा नहीं करता है, यह केवल उत्तराधिकारी वर्ग को निपटान (बूल) ओवरराइड करके सही ढंग से निपटान को लागू करने की अनुमति देता है। –

+0

मुझे खेद है कि यह सिर्फ मुझे कोई समझ नहीं रहा है। यह एक सुपर क्लास में कोड को शामिल करने के लिए ओओ प्रकृति के पूरी तरह से विपरीत लगता है, यदि एक उप वर्ग एक्स कर सकता है। निश्चित रूप से आप इसे अप्रबंधित संसाधनों को प्रबंधित करने के लिए उप-वर्ग में छोड़ देंगे __it उपयोग__, फाइनलाइजर्स और किसी भी आईडीस्पोज़ेबल कार्यान्वयन को स्वयं। यह नहीं मानना ​​चाहिए कि बेसप्रेस द्वारा SuppressFinalize को बुलाया जाएगा। यह क्यों होना चाहिए – AnthonyWJones

4

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

मैं दो छोटे सुझाव हालांकि,

यह "m_Disposed" अगर आपने पहले अपना कामयाब संसाधनों पर Dispose कहा जाता है परीक्षण करने के लिए चर होना आवश्यक नहीं है की है। आप केवल जब तक आवश्यक है के लिए

protected virtual void Dispose (bool disposing) 
{ 
    if (disposing) { 
     if (_Writer != null) 
      _Writer.Dispose(); 
    } 
    _Writer = null; 
} 

फ़ाइलें खोलें तो जैसे समान कोड का उपयोग कर सकता है,। तो आपके उदाहरण में, आप File.Exists का उपयोग कर कन्स्ट्रक्टर में फ़ाइल के अस्तित्व के लिए परीक्षण करेंगे और फिर जब आपको फ़ाइल को पढ़ने/लिखने की आवश्यकता होगी, तो फिर आप इसे खोलेंगे और इसका उपयोग करेंगे।

इसके अलावा, अगर आप एक फाइल करने के लिए पाठ लिखने के लिए केवल weant, File.WriteAllText या File.OpenText या यहाँ तक कि File.AppendText पर एक नज़र जो पाठ के उद्देश्य से है विशेष रूप से ASCIIEncoding साथ फाइल की है।

इसके अलावा, हाँ आप .NET Dispose पैटर्न को सही ढंग से कार्यान्वित कर रहे हैं।

+0

आपकी पोस्ट के दूसरे भाग के बारे में: फ़ाइल को हर समय खुला रहता है।'फू' वास्तव में एक लॉगर है। :) – mafu

+4

** @ mafutrct: ** फ़ाइल को हर समय खोलने के लिए अच्छा अभ्यास नहीं है। फ़ंक्शन में जो फ़ाइल को लिखता है; फ़ाइल खोलें, इसे लिखें, और फ़ंक्शन से बाहर निकलने से पहले इसे बंद करें। – awe

1

आपको यह जांचना चाहिए कि _Writernull नहीं है, इसे निपटाने का प्रयास करने से पहले। (यह संभावना है कि यह कभी null होगा, लेकिन सिर्फ मामले में प्रतीत होता है न!)

protected virtual void Dispose(bool disposing) 
{ 
    if (!_Disposed) 
    { 
     if (disposing && (_Writer != null)) 
     { 
      _Writer.Dispose(); 
     } 
     _Writer = null; 
     _Disposed = true; 
    } 
} 
0

मैं सब कुछ के साथ सहमत अन्य टिप्पणी में कहा, लेकिन यह भी कहना है कि होगा;

  1. आपको किसी भी मामले में _Writer = null को सेट करने की आवश्यकता नहीं है।

  2. यदि आप ऐसा करने जा रहे हैं, तो संभवतया इसे स्थानांतरित करना सबसे अच्छा है, जहां निपटान है। यह शायद एक बड़ा मतभेद नहीं बनाता है, लेकिन आम तौर पर आप अंतिम वस्तुओं द्वारा निपटाए जाने पर प्रबंधित वस्तुओं के साथ खेलना नहीं चाहते हैं (जिसे आपको इस मामले में वैसे भी आवश्यकता नहीं है, जैसा कि अन्य ने बताया है)।

3

मेरे पास इस तरह के कई वर्ग हैं - और मेरी सिफारिश है जब भी संभव हो कक्षा को सील कर दें। IDisposable + विरासत काम कर सकती है, लेकिन 99% बार आपको इसकी आवश्यकता नहीं है - और इसमें गलतियों को करना आसान है।

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

सिर्फ कार्य करें:

public sealed class Foo : IDisposable 
{ 
    private StreamWriter _Writer; 

    public Foo(string path) 
    { 
      FileStream fileWrite = File.Open (path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite); 
      try { 
       _Writer = new StreamWriter (fileWrite, new ASCIIEncoding()); 
      } catch { 
       fileWrite.Dispose(); 
       throw; 
      } 
    } 

    public void Dispose() 
    { 
     _Writer.Dispose(); 
    } 
} 

नोट कोशिश ... पकड़ने; तकनीकी रूप से StreamWriter कन्स्ट्रक्टर अपवाद फेंक सकता है, इस मामले में यह FileStream का स्वामित्व कभी नहीं लेता है और आपको इसे स्वयं निपटाना होगा।

यदि आप वास्तव में कई IDisposable एस का उपयोग कर रहे हैं, तो उस कोड को C++/CLI में डालने पर विचार करें: जो आपके लिए वह बॉयलरप्लेट कोड बनाता है (यह पारदर्शी रूप से मूल और प्रबंधित वस्तुओं दोनों के लिए उचित निर्धारक विनाश तकनीक का उपयोग करता है)।

विकिपीडिया सी के लिए एक सभ्य IDisposable नमूना है ++ (वास्तव में, यदि आप कई IDisposable का है, सी ++ वास्तव में ज्यादा सरल से सी # है): Wikipedia: C++/CLI Finalizers and automatic variables

उदाहरण के लिए, C++/CLI में एक "सुरक्षित" डिस्पोजेबल कंटेनर को लागू करने की तरह दिखता है ...

public ref class MyDisposableContainer 
{ 
    auto_handle<IDisposable> kidObj; 
    auto_handle<IDisposable> kidObj2; 
public: 

    MyDisposableContainer(IDisposable^ a,IDisposable^ b) 
      : kidObj(a), kidObj2(b) 
    { 
     Console::WriteLine("look ma, no destructor!"); 
    } 
}; 

इस कोड को सही ढंग से भी एक कस्टम IDisposable कार्यान्वयन जोड़े बिना kidObj और kidObj2 निपटाने जाएगा, और यह करने के लिए मजबूत है अपवादों को या तो कार्यान्वयन का निपटान (ऐसा नहीं होना चाहिए, लेकिन अभी भी), और IDisposable सदस्यों या मूल संसाधनों के चेहरे में बनाए रखने के लिए सरल है।

नहीं कि मैं सी ++/सीएलआई का एक बड़ा प्रशंसक हूं, लेकिन भारी संसाधन-उन्मुख कोड के लिए इसे सी # आसानी से हराया गया है, और इसमें प्रबंधित और देशी कोड दोनों के साथ बिल्कुल शानदार इंटरऑप है - संक्षेप में, सही गोंद कोड में; -)। मैं सी # में अपने कोड का 9 0% लिखता हूं, लेकिन सभी इंटरऑप जरूरतों के लिए सी ++/सीएलआई का उपयोग करता हूं (esp। अगर आप किसी भी Win32 फ़ंक्शन को कॉल करना चाहते हैं - मार्शलए और अन्य जगहों पर अन्य इंटरऑप विशेषताएं भयानक और पूरी तरह से समझ में नहीं आती हैं)।

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