2010-03-27 11 views
6

मैं एक साधारण वर्ग है कि एक StreamWrite शामिल कर रही हूँकक्षा नाशक समस्या

class Logger 
{ 
    private StreamWriter sw; 
    private DateTime LastTime; 
    public Logger(string filename) 
    { 
     LastTime = DateTime.Now; 
     sw = new StreamWriter(filename); 
    } 
    public void Write(string s) 
    { 
     sw.WriteLine((DateTime.Now-LastTime).Ticks/10000+":"+ s); 
     LastTime = DateTime.Now; 
    } 
    public void Flush() 
    { 
     sw.Flush(); 
    } 
    ~Logger() 
    { 
     sw.Close();//Raises Exception! 
    } 
} 

लेकिन जब मैं नाशक में इस StreamWriter बंद, यह एक अपवाद है कि StreamWriter पहले से ही नष्ट कर दिया गया जन्म देती है?

क्यों? और इसे कैसे काम करना है कि जब लॉगर क्लास हटा दी जाती है, तो स्ट्रीमवाइटर हटाने से पहले बंद हो जाता है?

धन्यवाद!

+3

विनाशकों के साथ डिस्पोजेबल वस्तुओं को लागू करने के लिए एक सही पैटर्न है। यह नहीं है जैसा कि आपने पाया है, अगर आपको पैटर्न गलत लगता है, तो आपको फाइनलज़र थ्रेड पर क्रैश को डिबग करने का आनंद मिलता है। पैटर्न पर पढ़ें और इसे सही ढंग से कार्यान्वित करें। http://msdn.microsoft.com/en-us/magazine/cc163392.aspx –

उत्तर

16

अपनी खुद की नाशक (उर्फ finalizer) लेखन सभी मामलों में से 99.99% में गलत है। यह सुनिश्चित करने के लिए उनकी आवश्यकता है कि आपकी कक्षा एक ऑपरेटिंग सिस्टम संसाधन जारी करती है जिसे स्वचालित रूप से .NET ढांचे द्वारा प्रबंधित नहीं किया जाता है और आपकी कक्षा के उपयोगकर्ता द्वारा ठीक से जारी नहीं किया गया था।

यह अपने खुद के कोड में पहला ऑपरेटिंग सिस्टम संसाधन आवंटित करने के लिए होने से शुरू होता है। हमेशा किसी प्रकार की पी/आमंत्रण की आवश्यकता होती है। यह बहुत शायद ही कभी आवश्यक है, यह उस पर ध्यान देने के लिए माइक्रोसॉफ्ट में काम कर रहे .NET प्रोग्रामर का काम था।

उन्होंने स्ट्रीमवाइटर के मामले में किया था। कई परतों के माध्यम से, यह एक फ़ाइल हैंडल के चारों ओर एक रैपर है, जिसे CreateFile() के साथ बनाया गया है। जिस वर्ग ने हैंडल बनाया वह भी वह है जो फाइनलाइज़र लिखने के लिए ज़िम्मेदार है। माइक्रोसॉफ्ट कोड, तुम्हारा नहीं।

इस तरह की कक्षा हमेशा आईडीस्पोजेबल लागू करती है, जिससे कक्षा के उपयोगकर्ता को यह काम पूरा करने के लिए अंतिम रूप देने के बजाय कक्षा को छोड़ने का मौका मिलता है। StreamWriter IDISposable लागू करता है।

बेशक

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

IDisposable खुद को लागू करने से है कि सहायता प्राप्त करें।अपने वर्ग के उपयोगकर्ता अब निपटान कॉल कर सकते हैं या कथन का उपयोग का उपयोग करें, सिर्फ फ्रेमवर्क वर्गों से किसी के साथ वह होगा की तरह:

class Logger : IDisposable { 
    private StreamWriter sw; 
    public void Dispose() { 
     sw.Dispose(); // Or sw.Close(), same thing 
    } 
    // etc... 
    } 

ठीक है, कि क्या आप सभी मामलों में से 99.9% में करना चाहिए। लेकिन यहाँ नहीं। मोटे तौर पर भ्रमित करने के जोखिम पर: यदि आप आईडीस्पोजेबल लागू करते हैं, तो आपके वर्ग के उपयोगकर्ता को अपना निपटान() विधि कॉल करने का उचित अवसर भी होना चाहिए। लॉगर प्रकार वर्ग को छोड़कर, आमतौर पर यह समस्या का अधिकतर नहीं है। यह बहुत संभावना है कि आपका उपयोगकर्ता आखिरी संभव पल में कुछ लॉग करना चाहता है। ताकि वह AppDomain से अनचाहे अपवाद लॉग कर सके। उदाहरण के लिए अनचाहे अपवाद।

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

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

Fwiw, ढांचे में एक और वर्ग है जिसमें एक ही समस्या है। थ्रेड क्लास चार ऑपरेटिंग सिस्टम हैंडल का उपयोग करती है लेकिन आईडीस्पोजेबल को लागू नहीं करती है। कॉलिंग थ्रेड। डिस्प्ले() वास्तव में अजीब है, इसलिए माइक्रोसॉफ्ट ने इसे लागू नहीं किया। इससे बहुत असामान्य मामलों में समस्याएं पैदा होती हैं, आपको एक ऐसा प्रोग्राम लिखना होगा जो बहुत सारे धागे बनाता है लेकिन क्लास ऑब्जेक्ट्स बनाने के लिए कभी भी नए ऑपरेटर का उपयोग नहीं करता है। यह किया गया है।

अंतिम लेकिन कम से कम नहीं: एक लॉगर लिखना ऊपर वर्णित जैसा कि काफी मुश्किल है। Log4net एक लोकप्रिय समाधान है। एनएलओजी एक बेहतर मूसट्रैप है, एक लाइब्रेरी जो जावा पोर्ट की तरह महसूस करने के बजाए डॉटनेट की तरह महसूस करती है और काम करती है।

+0

जिज्ञासा से बाहर, नेट वास्तव में धागे से पहले थ्रेड हैंडल के साथ कुछ भी करता है। चार्टर्ड, या इसे समाप्त होने के बाद? मैं समझता हूं कि मैनेज्ड थ्रेड आईडी आवंटित किए गए हैं, और इसलिए इसे अंतिम रूप से पुनर्प्राप्त करना होगा, लेकिन ओएस हैंडल वास्तव में कुछ भी करते हैं? – supercat

5

विनाशकों (ए.के.ए. फाइनलाइजर्स) को किसी विशेष क्रम में चलाने की गारंटी नहीं है। the documentation देखें:

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

इसके बजाय IDisposable लागू करें।

2

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

एक अतिरिक्त चेतावनी के रूप में, पता है कि finalizer अपने स्वयं के धागे, आप अप्रबंधित एपीआई है कि धागे की आत्मीयता उपयोग कर रहे हैं जो आप कर सकते हैं ट्रिप में चलता हो। ये परिदृश्य IDisposable और अच्छा, व्यवस्थित सफाई के साथ बहुत साफ हैं।

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