क्या मुझे स्ट्रीम ऑब्जेक्ट के लिए बंद () या डिस्पोज़ () करना चाहिए?


151

जैसे वर्गों Stream, StreamReader, StreamWriterआदि लागू IDisposableइंटरफ़ेस। इसका मतलब है, हम Dispose()इन वर्गों की वस्तुओं पर विधि कह सकते हैं। उन्होंने भी एक publicविधि को परिभाषित किया है Close()। अब जो मुझे भ्रमित करता है, जैसा कि एक बार वस्तुओं के साथ करने के बाद मुझे क्या कॉल करना चाहिए? अगर मैं दोनों को बुलाऊँ तो क्या होगा?

मेरा वर्तमान कोड यह है:

using (Stream responseStream = response.GetResponseStream())
{
   using (StreamReader reader = new StreamReader(responseStream))
   {
      using (StreamWriter writer = new StreamWriter(filename))
      {
         int chunkSize = 1024;
         while (!reader.EndOfStream)
         {
            char[] buffer = new char[chunkSize];
            int count = reader.Read(buffer, 0, chunkSize);
            if (count != 0)
            {
               writer.Write(buffer, 0, count);
            }
         }
         writer.Close();
      }
      reader.Close();
   }
}

जैसा कि आप देखते हैं, मैंने रचनाएं लिखी हैं using(), जो Dispose()प्रत्येक ऑब्जेक्ट पर स्वचालित रूप से कॉल विधि है। लेकिन मैं Close()तरीकों को भी बुलाता हूं । क्या यह सही है?

कृपया स्ट्रीम ऑब्जेक्ट का उपयोग करते समय मुझे सर्वोत्तम प्रथाओं का सुझाव दें। :-)

MSDN उदाहरण using()निर्माण और कॉल Close()विधि का उपयोग नहीं करता है :

अच्छी है?


यदि आप ReSharper का उपयोग कर रहे हैं, तो आप इसे संरक्षक सूची के भीतर "एंटीपैटर्न" के रूप में परिभाषित कर सकते हैं। ReSharper आपकी परिभाषा के संबंध में प्रत्येक उपयोग को त्रुटि / संकेत / चेतावनी के रूप में चिह्नित करेगा। यह परिभाषित करना भी संभव है कि ReSharper को ऐसी घटना के लिए QuickFix कैसे लागू करना है।
थॉर्स्टन हंस

3
बस एक टिप: आप कई डिस्पोजेबल इटेंस के लिए उस तरह के स्टेटमेंट का उपयोग कर सकते हैं: (स्ट्रीम रिस्पांस स्ट्रीम = रेस्पोंस.गेटस्प्रेसस्ट्रीमस्ट्रीम ()) (स्ट्रीमरएडर रीडर = न्यू स्ट्रीमराइडर (रिस्पॉन्स स्ट्रीक) का उपयोग करके (स्ट्रीमव्यूटर राइटर = नया स्ट्रीमवेटर (फाइलनाम)) का उपयोग कर सकते हैं। {//..सोम कोड}
लैट्रोवा


आपको अपने बयानों का उपयोग करने की आवश्यकता नहीं है, जैसे कि आप उन्हें एक दूसरे के ऊपर रख सकते हैं और कोष्ठक का एक सेट कर सकते हैं। एक अन्य पोस्ट पर, मैंने एक कोड स्निपेट के लिए एक संपादन का सुझाव दिया, जो उस तकनीक के साथ बयानों का उपयोग करना चाहिए था यदि आप अपना "कोड तीर" देखना चाहते हैं और उसे ठीक करना चाहते हैं: stackoverflow.com/questions/5282999/…
टिमोथी गोंजालेज

2
@ Suncat2000 आप कई बयानों का उपयोग कर सकते हैं, लेकिन उन्हें घोंसला नहीं बना सकते हैं और इसके बजाय उन्हें ढेर कर सकते हैं। मेरा मतलब सिंटैक्स इस तरह नहीं है जो टाइप को प्रतिबंधित करता है using (MemoryStream ms1 = new MemoryStream(), ms2 = new MemoryStream()) { }:। मेरा मतलब इस तरह से है जहाँ आप इस प्रकार को फिर से परिभाषित कर सकते हैं:using (MemoryStream ms = new MemoryStream()) using (FileStream fs = File.OpenRead("c:\\file.txt")) { }
टिमोथी गोंजालेज

जवाबों:


101

Reflector.NET में एक त्वरित कूद से पता चलता है कि इस Close()विधि परStreamWriter है:

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

तथा StreamReader है:

public override void Close()
{
    this.Dispose(true);
}

में Dispose(bool disposing)ओवरराइड कियाStreamReader है:

protected override void Dispose(bool disposing)
{
    try
    {
        if ((this.Closable && disposing) && (this.stream != null))
        {
            this.stream.Close();
        }
    }
    finally
    {
        if (this.Closable && (this.stream != null))
        {
            this.stream = null;
            /* deleted for brevity */
            base.Dispose(disposing);
        }
    }
}

StreamWriterविधि समान है।

इसलिए, कोड को पढ़ने से यह स्पष्ट है कि आप कॉल कर सकते हैं Close() और Dispose()धाराओं को जितनी बार चाहें और किसी भी क्रम में। यह किसी भी तरह से व्यवहार को नहीं बदलेगा।

तो यह नीचे आता है कि क्या यह उपयोग करने के लिए अधिक पठनीय है या नहीं Dispose(),Close() और / या using ( ... ) { ... }

मेरी व्यक्तिगत प्राथमिकता वह है using ( ... ) { ... } जब भी संभव हो इसका उपयोग हमेशा किया जाना चाहिए क्योंकि यह आपको "कैंची से नहीं चलाने" में मदद करता है।

लेकिन, जबकि यह शुद्धता में मदद करता है, यह पठनीयता को कम करता है। C # में हमारे पास पहले से ही घुंघराले ब्रेस को बंद करने का बहुतायत है इसलिए हम कैसे जानते हैं कि कौन सा वास्तव में स्ट्रीम पर क्लोज़ करता है?

इसलिए मुझे लगता है कि ऐसा करना सबसे अच्छा है:

using (var stream = ...)
{
    /* code */

    stream.Close();
}

यह कोड के व्यवहार को प्रभावित नहीं करता है, लेकिन यह पठनीयता में सहायता करता है।


20
" सी # में हमारे पास पहले से ही घुंघराले ब्रेसिज़ को बंद करने का बहुतायत है, इसलिए हमें कैसे पता चलेगा कि कौन सा वास्तव में स्ट्रीम पर करीबी प्रदर्शन करता है? " मुझे नहीं लगता कि यह एक बड़ी समस्या है: धारा "सही समय पर बंद" है? यानी, जब चर दायरे से बाहर हो जाता है और इसकी आवश्यकता नहीं होती है।
हेंजजी

110
हम्म, नहीं, यह एक "क्यों बिल्ली है वह इसे दो बार बंद कर रहा है ??" पढ़ते समय तेजी।
हंस पासेंट

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

14
यदि आप + बंद () और चालू / विश्लेषण का उपयोग करते हैं, तो आपको "चेतावनी: CA2202: Microsoft.Usage: ऑब्जेक्ट 'f' को विधि 'फू (स्ट्रिंग)' में एक से अधिक बार निपटाया जा सकता है। सिस्टम उत्पन्न करने से बचने के लिए। ObjectDisposedException आपको किसी कॉल को एक बार में एक से अधिक बार डिस्पोज़ नहीं करना चाहिए। लाइन्स: 41 "इसलिए जब कि क्लोज़ एंड कॉलिंग के साथ करेंट इम्प्लीमेंटेशन ठीक है, डॉक्यूमेंटेशन और / एनालिसिस के अनुसार, यह ठीक नहीं है और भविष्य के संस्करणों में बदल सकता है। शुद्ध।
marc40000

4
अच्छे उत्तर के लिए +1। एक और बात पर विचार करें। क्लोजिंग ब्रेस जैसे // क्लोज या जैसा मैं करता हूं, उसके बाद एक टिप्पणी क्यों नहीं जोड़ रहा हूं, एक नौसिखिया होने के नाते, मैं किसी भी बंद ब्रेस के बाद एक लाइनर जोड़ता हूं जो स्पष्ट नहीं है। उदाहरण के लिए एक लंबी कक्षा में मैं अंतिम समापन ब्रेस के बाद // एंड नेमस्पेस XXX जोड़ूंगा, और दूसरी अंतिम समापन ब्रेस के बाद // एंड क्लास YYY। क्या यह टिप्पणी के लिए नहीं है। बस उत्सुक। :) एक नौसिखिया के रूप में, मैंने ऐसे कोड को देखा, घबराया कि मैं यहां क्यों आया हूं। मैंने प्रश्न पूछा "दूसरे करीबी की आवश्यकता क्यों है"। मुझे लगता है कि कोड की अतिरिक्त लाइनें स्पष्टता में नहीं जुड़ती हैं। माफ़ करना।
फ्रांसिस रोडर्स

51

नहीं, आपको उन तरीकों को मैन्युअल रूप से कॉल नहीं करना चाहिए। usingब्लॉक के अंत में Dispose()विधि को स्वचालित रूप से कहा जाता है जो मुक्त मानव रहित संसाधनों (कम से कम मानक .NET बीसीएल वर्गों जैसे धाराओं, पाठकों / लेखकों, ...) के लिए देखभाल करेगा। तो आप भी अपना कोड इस तरह लिख सकते हैं:

using (Stream responseStream = response.GetResponseStream())
    using (StreamReader reader = new StreamReader(responseStream))
        using (StreamWriter writer = new StreamWriter(filename))
        {
            int chunkSize = 1024;
            while (!reader.EndOfStream)
            {
                 char[] buffer = new char[chunkSize];
                 int count = reader.Read(buffer, 0, chunkSize);
                 if (count != 0)
                 {
                     writer.Write(buffer, 0, count);
                 }
            }
         }

Close()प्रणाली को बुलाती है Dispose()


1
मुझे पूरा यकीन है कि आपको usingसबसे पहले होने की ज़रूरत नहीं है responseStreamक्योंकि यह लिपटा हुआ है readerजिससे यह सुनिश्चित हो जाएगा कि पाठक के निपटारे के बाद यह बंद हो जाएगा। +1 फिर भी
इसाक

यह भ्रमित करने वाला है जब आपने कहा था The Close method calls Dispose... और आपके पोस्ट के बाकी हिस्सों में, आप आसन्न कर रहे हैं कि Dispose()कॉल करेगा Close(), मुझे बाद वाले को मैन्युअल रूप से कॉल नहीं करना चाहिए। क्या आप कह रहे हैं कि वे एक दूसरे को बुलाते हैं?
नवाज

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

3
भयानक जवाब। यह मानता है कि आप एक usingब्लॉक का उपयोग कर सकते हैं । मैं एक वर्ग लागू कर रहा हूं जो समय-समय पर लिखता है और इसलिए नहीं कर सकता।
जेज

5
@Jez आपकी कक्षा को तब आइडीएसोपायरी इंटरफ़ेस लागू करना चाहिए, और संभवतः बंद भी हो सकता है () यदि क्षेत्र में मानक शब्दावली है , ताकि आपकी कक्षा का उपयोग करने वाली कक्षाएं using(या, फिर से, डिस्पोज़ पैटर्न के लिए जाएं) का उपयोग कर सकें ।
डोरस

13

प्रलेखन का कहना है कि ये दो विधियां समतुल्य हैं:

StreamReader.Close : क्लोज़ के इस कार्यान्वयन को सही मान पास करने वाली डिस्पोज़ विधि कहते हैं।

StreamWriter.Close : क्लोज़ के इस कार्यान्वयन से डिस्पोज़ विधि को सही मान से गुजरता है।

Stream.Close : यह विधि सभी संसाधनों को जारी करने के लिए सही निर्दिष्ट करते हुए डिस्पोज़ को कॉल करती है।

तो, ये दोनों समान रूप से मान्य हैं:

/* Option 1, implicitly calling Dispose */
using (StreamWriter writer = new StreamWriter(filename)) { 
   // do something
} 

/* Option 2, explicitly calling Close */
StreamWriter writer = new StreamWriter(filename)
try {
    // do something
}
finally {
    writer.Close();
}

व्यक्तिगत रूप से, मैं पहले विकल्प के साथ रहना चाहता हूं, क्योंकि इसमें कम "शोर" होता है।


5

कई वर्गों पर जो दोनों तरीकों Close()और Dispose()तरीकों का समर्थन करते हैं , दोनों कॉल समतुल्य होंगे। कुछ वर्गों पर, हालांकि, एक वस्तु को फिर से खोलना संभव है जिसे बंद कर दिया गया है। इस तरह की कुछ कक्षाएं फिर से खोलने की अनुमति देने के लिए, क्लोज के बाद कुछ संसाधनों को जीवित रख सकती हैं; अन्य किसी भी संसाधन को जीवित नहीं रख सकते हैं Close(), लेकिन Dispose()फिर से खोलने के लिए स्पष्ट रूप से मना करने पर एक झंडा लगा सकते हैं ।

IDisposable.Disposeस्पष्ट रूप से अनुबंध की आवश्यकता है कि इसे किसी ऐसी वस्तु पर कॉल करना, जिसे फिर कभी उपयोग नहीं किया जाएगा, सबसे खराब हानिरहित होगा, इसलिए मैं IDisposable.Disposeया तो कॉल करने की सलाह दूंगा या Dispose()हर IDisposableवस्तु पर कॉल करने की विधि , चाहे वह कॉल भी हो या नहीं Close()


FYI करें यहाँ MSDN ब्लॉग्स पर एक लेख है जो क्लोज़ और डिस्पोज़ फ़न की व्याख्या करता है। blogs.msdn.com/b/kimhamil/archive/2008/03/15/…
JamieSee

1

यह एक पुराना प्रश्न है, लेकिन अब आप प्रत्येक को अवरुद्ध करने की आवश्यकता के बिना बयानों का उपयोग करके लिख सकते हैं। युक्त ब्लॉक समाप्त होने पर उन्हें रिवर्स ऑर्डर में निपटाया जाएगा।

using var responseStream = response.GetResponseStream();
using var reader = new StreamReader(responseStream);
using var writer = new StreamWriter(filename);

int chunkSize = 1024;
while (!reader.EndOfStream)
{
    char[] buffer = new char[chunkSize];
    int count = reader.Read(buffer, 0, chunkSize);
    if (count != 0)
    {
        writer.Write(buffer, 0, count);
    }
}

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/using

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.