2010-10-10 21 views
9

मुझे शुरुआत से अंत तक दो बार एक स्ट्रीम पढ़ने की जरूरत है।StreamReader का निपटान क्यों एक स्ट्रीम अपठनीय बनाता है?

लेकिन निम्न कोड ObjectDisposedException: Cannot access a closed file अपवाद फेंकता है।

string fileToReadPath = @"<path here>"; 
using (FileStream fs = new FileStream(fileToReadPath, FileMode.Open)) 
{ 
    using (StreamReader reader = new StreamReader(fs)) 
    { 
     string text = reader.ReadToEnd(); 
     Console.WriteLine(text); 
    } 

    fs.Seek(0, SeekOrigin.Begin); // ObjectDisposedException thrown. 

    using (StreamReader reader = new StreamReader(fs)) 
    { 
     string text = reader.ReadToEnd(); 
     Console.WriteLine(text); 
    } 
} 

ऐसा क्यों हो रहा है? वास्तव में क्या निपटाया जाता है? और StreamReader में हेरफेर करने से इस तरह से संबंधित धारा प्रभावित होती है? क्या यह उम्मीद करने के लिए तर्कसंगत नहीं है कि एक खोज योग्य धारा कई बार पढ़ी जा सकती है, जिसमें कई StreamReader एस शामिल हैं?

+1

इन तरह की स्थितियों में System.IO.File.ReadAllText() का उपयोग करने पर भी विचार करें। यह आसान है। –

+0

@ डेव मार्कले: आप सही हैं। मैंने इसे एक संक्षिप्त उदाहरण के रूप में रखा है। दरअसल, असली कोड में, जिन धाराओं से मैं निपटता हूं, वे बहुत बड़े हो सकते हैं, इसलिए पहला पाठक उन्हें प्रति पंक्ति रेखा पढ़ता है, फिर स्ट्रीम को प्रति-बाइट प्रति दूसरी स्ट्रीम में कॉपी किया जाता है। –

उत्तर

12

ऐसा इसलिए होता है क्योंकि StreamReader स्ट्रीम का 'स्वामित्व' लेता है। दूसरे शब्दों में, यह स्रोत स्ट्रीम को बंद करने के लिए खुद को जिम्मेदार बनाता है। जैसे ही आपका प्रोग्राम Dispose या Close (आपके मामले में using स्टेटमेंट स्कोप छोड़कर) को कॉल करता है, तो यह स्रोत स्ट्रीम को भी निपटान करेगा। आपके मामले में fs.Dispose() पर कॉल करना। तो पहले using ब्लॉक को छोड़ने के बाद फ़ाइल स्ट्रीम मर गई है। यह लगातार व्यवहार है, .NET में सभी धारा वर्ग जो एक और धारा को लपेटते हैं इस तरह से व्यवहार करते हैं।

StreamReader के लिए एक कन्स्ट्रक्टर है जो यह कहने की अनुमति देता है कि स्रोत स्रोत का मालिक नहीं है। हालांकि यह एक .NET कार्यक्रम से सुलभ नहीं है, कन्स्ट्रक्टर आंतरिक है।

इस विशेष मामले में, आप using-StreamReader के लिए भुगतान का उपयोग न करके समस्या का समाधान करेंगे। हालांकि यह एक काफी बालों के कार्यान्वयन विस्तार है। निश्चित रूप से आपके लिए एक बेहतर समाधान उपलब्ध है लेकिन कोड वास्तविक प्रस्ताव देने के लिए बहुत सिंथेटिक है।

7

Dispose() का उद्देश्य संसाधन के साथ समाप्त होने पर संसाधनों को साफ करना है। पाठकस्ट्रीम पर प्रभाव डालता है क्योंकि पाठक केवल स्ट्रीम को फ़िल्टर कर रहा है, और इसलिए पाठक को निपटाते हुए इसका कोई अर्थ नहीं है, इसके संदर्भ में स्रोत स्ट्रीम को कॉल करने के संदर्भ में इसे छोड़कर कोई अर्थ नहीं है।

अपने कोड को ठीक करने के लिए, सिर्फ एक पाठक पूरे समय का उपयोग करें:

using (FileStream fs = new FileStream(fileToReadPath, FileMode.Open)) 
using (StreamReader reader = new StreamReader(fs)) 
{ 
    string text = reader.ReadToEnd(); 
    Console.WriteLine(text); 

    fs.Seek(0, SeekOrigin.Begin); // ObjectDisposedException not thrown now 

    text = reader.ReadToEnd(); 
    Console.WriteLine(text); 
} 

संपादित नीचे टिप्पणी को संबोधित करने के:

अधिकांश स्थितियों में, आप के रूप में अंतर्निहित धारा का उपयोग करने की जरूरत नहीं है आप अपने कोड में करते हैं (fs.Seek)। इन मामलों में, तथ्य यह है कि StreamReader श्रृंखलाओं को अंतर्निहित धारा में कॉल करने के लिए आपको धारा के लिए usings कथन का उपयोग न करके कोड पर अर्थशास्त्री करने की अनुमति मिलती है। उदाहरण के लिए, कोड देखने की तरह होगा:

using (StreamReader reader = new StreamReader(new FileStream(fileToReadPath, FileMode.Open))) 
{ 
    ... 
} 
+0

असल में, मैं पूछ रहा हूं कि * पाठक पर निपटान() * * स्ट्रीम * को प्रभावित करता है। मुझे आपका जवाब समझ में नहीं आया। क्या इसका मतलब यह है कि पाठकों का निपटान केवल स्ट्रीम डेटा को साफ करने का एक उद्देश्य है? तो क्यों 'StreamReader'' IDISposable 'लागू करता है, अगर यह कुछ भी नहीं करता * उपयोगी * (क्योंकि धारा का निपटारा सभी मामलों में सभी काम करेगा)? –

+1

@ मेनमा, निपटान का उद्देश्य() हमेशा * यह सुनिश्चित करने के लिए है कि दिए गए 'आईडीस्पोजेबल' से जुड़े सभी संसाधन साफ़ किए गए हैं। चूंकि पाठक और धारा एक ही इकाई का निपटान करने वाली इकाई का प्रतिनिधित्व करती है, वही प्रभाव दूसरे के निपटारे के समान होता है। –

2

Using एक गुंजाइश को परिभाषित करता है, जिनमें से बाहर एक वस्तु निपटारा किया जाएगा, इस प्रकार ObjectDisposedException। आप इस ब्लॉक के बाहर StreamReader की सामग्री तक नहीं पहुंच सकते हैं।

0

Dispose() अभिभावक पर Dispose() सभी स्वामित्व वाली धाराएं। दुर्भाग्य से, धाराओं में Detach() विधि नहीं है, इसलिए आपको यहां कुछ कामकाज बनाना होगा।

0

मुझे नहीं पता क्यों, लेकिन आप अपने StreamReader को अनदेखा छोड़ सकते हैं। स्ट्रीमरिएडर एकत्र होने पर भी, आपकी अंतर्निहित धारा का निपटारा नहीं किया जाएगा।

+0

बेशक। लेकिन मेरे लिए 'StreamReader'' का उपयोग न करने के लिए यह बहुत सहज नहीं है, और यह शायद FxCop नियमों का उल्लंघन करेगा। वैसे, इस प्रश्न की उत्पत्ति पर मुद्दा तब सामने आया जब मैंने दोबारा प्रतिक्रिया दी और पुराना कोड जहां उपयोग पूरी तरह गायब थे। –

1

मैं आपके प्रश्न से सहमत हूं। इस जानबूझकर साइड इफेक्ट के साथ सबसे बड़ा मुद्दा तब होता है जब डेवलपर्स इसके बारे में नहीं जानते हैं और using के साथ StreamReader के आस-पास के "सर्वोत्तम अभ्यास" का अंधाधुंध अनुसरण कर रहे हैं। लेकिन यह कीड़े नीचे ट्रैक करने के लिए जब यह एक लंबे समय तक रहा वस्तु की संपत्ति पर है कुछ वास्तव में कड़ी मेहनत कर सकते हैं, सबसे अच्छा (सबसे खराब?) उदाहरण मैंने देखा है

using (var sr = new StreamReader(HttpContext.Current.Request.InputStream)) 
{ 
    body = sr.ReadToEnd(); 
} 

डेवलपर पता नहीं था InputStream अब है किसी भी भविष्य की जगह के लिए तैयार है जो वहां होने की अपेक्षा करता है।

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

मैं करता हूँ और स्ट्रीम उदाहरणों पढ़ने कोड स्पष्ट रूप से पैदा नहीं करता है कि पर करने के लिए हमारे डेवलपर्स बता क्या ...

// save position before reading 
long position = theStream.Position; 
theStream.Seek(0, SeekOrigin.Begin); 
// DO NOT put this StreamReader in a using, StreamReader.Dispose() clears the stream 
StreamReader sr = new StreamReader(theStream); 
string content = sr.ReadToEnd(); 
theStream.Seek(position, SeekOrigin.Begin); 

(खेद मैं एक जवाब के रूप में इस जोड़ा है, में फिट नहीं होगा एक टिप्पणी, मुझे ढांचे के इस डिजाइन निर्णय के बारे में अधिक चर्चा पसंद आएगी)

+2

StreamReader के लिए एक बेहतर डिज़ाइन एक कन्स्ट्रक्टर तर्क निर्दिष्ट करेगा कि उसे स्ट्रीम का स्वामित्व लेना चाहिए या नहीं। ऐसी कई स्थितियां हैं जहां कोड स्ट्रीम खोल सकता है, इसके लिए एक पाठक बना सकता है, पाठक को भविष्य में समय पर डेटा पढ़ेगा, और उसके बाद स्ट्रीम के लिए और उपयोग नहीं किया जाएगा। उस स्थिति में, पाठक को तब निपटान किया जाना चाहिए जब पाठक इसके साथ किया जाता है, लेकिन स्ट्रीम के निर्माता को यह पता नहीं हो सकता कि यह कब होगा। – supercat

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