Akış okuyucuyu atmak akışı kapatıyor mu?


167

Yazmak için yöntemlere bir akış gönderiyorum ve bu yöntemlerde ikili bir okuyucu / yazıcı kullanıyorum. Okuyucu / yazar usingatıfta bulunulduğunda ya da atıfta bulunulmadığında bertaraf edildiğinde, akış da kapatılır mı?

Ben bir BinaryReader / Writer gönderirim, ama ben de bir StreamReader kullanıyorum (belki de bunun etrafında gitmek gerekir. Ben sadece GetLine ve ReadLine için kullanıyorum). Bir yazar / okuyucu her kapatıldığında akışı kapatırsa bu oldukça zordur.

Yanıtlar:


204

Evet, StreamReader, StreamWriter, BinaryReaderve BinaryWriterAradığınızda tüm yakın / altta yatan akışları imha Disposeüzerlerinde. Onlar yok okuyucu / yazıcı sadece çöp olsa toplanır kullanıcı akışta bertaraf - yapmanız gerekir her zaman atmayın okuyucu / yazar, preferrably bir ile usingaçıklamaya. (Aslında, bu sınıfların hiçbirinde finalizör yoktur, ne de olması gerekir.)

Şahsen ben de akış için bir kullanım ifadesi tercih ederim. Parantez usingolmadan ifadeleri oldukça düzgün bir şekilde iç içe yerleştirebilirsiniz :

using (Stream stream = ...)
using (StreamReader reader = new StreamReader(stream, Encoding.Whatever))
{
}

usingAkışın ifadesi biraz fazla olsa da ( StreamReaderyapıcı bir istisna atmadığı sürece ), en iyi uygulamayı, o zaman kurtulmak StreamReaderve sadece daha sonraki bir tarihte doğrudan akışı kullanmak, semantik.


2
oh iyi, sözde sonlandırıldığında değil, sadece Dispose çağırırken olur.
Nefzen

1
@Nefzen: Bunun nedeni, nesnelerinizin hangi sırayla sonlandırılacağının garantisi olmamasıdır. Hem StreamReader hem de temeldeki Akış sonlandırma için uygunsa, GC ilk önce akışı sonlandırabilir - ardından yayın okuyucunun akış için bir referansı olmaz. Bu nedenle, yönetilmeyen kaynakları yalnızca bir sonlandırma içinde bırakabilirsiniz (örneğin, bir FileStream, sonlandırmasında windows dosya tanıtıcısını kapatır). Oh, ve tabii ki, asla atmazsanız, akış yine de sonunda toplanır (ve dosya kapatılır). Bir akışı atmamak çok kötü bir uygulamadır.
JMarsch

13
Bu yuvalama VS kodu analizörünün şikayet etmesine neden olur: CA2202 : Microsoft.Usage : Object 'stream' can be disposed more than once in method '...'. To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object.Bu göz ardı edilmeli midir? Şimdiye kadar herhangi bir istisna alamadım ...
HB

15
@HB: Bu durumda görmezden gelmek güvenlidir. Ya da sadece yapıcı çağrısında akışı oluşturabilirsiniz StreamReader. Belgelerin IDisposable.Disposeaçıkça belirtilmesi nedeniyle uyarı bana sahte geliyor : "Bir nesnenin Dispose yöntemi bir kereden fazla çağrılırsa, nesne birinciden sonra tüm çağrıları yok saymalıdır. Dispose yöntemi ise nesne bir istisna atmamalıdır birden çok kez çağırdı. "
Jon Skeet

5
@JonSkeet: Aslında bunun için bir sayfa var, haklıydın, bu sahte :)
HB

46

Bu eski bir şey, ama bugün benzer bir şey yapmak istedim ve işlerin değiştiğini buldum. .Net 4.5'ten beri bir leaveOpenargüman var:

public StreamReader( Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, bool leaveOpen )

Tek sorun, diğer parametreler için ne ayarlanmasının tamamen açık olmamasıdır. İşte bazı yardım:

Gönderen msdn sayfa StreamReader Oluşturucu (Akış) için:

Bu kurucu UTF8Encoding kodlamasını, stream parametresini kullanarak BaseStream özelliğini ve dahili tampon boyutunu 1024 bayta başlatır.

Bu sadece kaynak kodu ile detectEncodingFromByteOrderMarksyargılanantrue

public StreamReader(Stream stream)
        : this(stream, true) {
}

public StreamReader(Stream stream, bool detectEncodingFromByteOrderMarks)
        : this(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultBufferSize) {
}

Bu varsayılanlardan bazılarının açıkta kalması veya bağımsız değişkenlerin isteğe bağlı olması güzel olurdu, böylece sadece istediklerimizi belirleyebiliriz.


1
Çok güzel bilgi! Bu yeni parametreyi hiç duymadım ve aslında çok mantıklı.
julealgon

4
Benim gibi tembel insanlar için, akışı açık bırakmak için kısa cevap şöyle olurdu:using (var streamReader = new StreamReader(myStream, Encoding.UTF8, true, 1024, true))
beawolf

29

Evet öyle. Reflector ile uygulamaya bakarak bunu doğrulayabilirsiniz.

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;    
            this.encoding = null;
            this.decoder = null;
            this.byteBuffer = null;
            this.charBuffer = null;
            this.charPos = 0;
            this.charLen = 0;
            base.Dispose(disposing);
        }
    }
}

13

Altı yıl geç ama belki bu birisine yardım edebilir.

StreamReader atıldığında bağlantıyı kapatır. Ancak, StreamReader / StreamWriter ile "(Stream stream = ...) {...}" kullanılması Stream'in iki kez atılmasına neden olabilir: (1) StreamReader nesnesi atandığında (2) ve Stream blok kullanıldığında kapanır. Bu, VS'nin kod analizini çalıştırırken CA2202 uyarısı verir.

Doğrudan CA2202 sayfasından alınan başka bir çözüm, bir dene / sonla bloğunu kullanmaktır. Doğru şekilde kurun, bu bağlantıyı yalnızca bir kez kapatır.

CA2202'nin altına yakın bir yerde Microsoft aşağıdakileri kullanmanızı önerir:

Stream stream = null;
try
{
    stream = new FileStream("file.txt", FileMode.OpenOrCreate);
    using (StreamWriter writer = new StreamWriter(stream))
    {
        stream = null;
        // Use the writer object...
    }
}
finally
{
    if(stream != null)
        stream.Dispose();
}

onun yerine...

// Generates a CA2202 warning
using (Stream stream = new FileStream("file.txt", FileMode.Open))
using (XmlReader reader = new XmlReader (stream))
{
    // Use the reader object...
}

2
Uyarı, kabul edilen cevabın yorumlarında da tartışılmaktadır. Jon Skeet orada bazı tavsiyeler sunuyor.
Marcin

Ayrıca, using ifadesinin derleyici tarafından bir try-finally bloğuna dönüştürüldüğünü unutmayın.
Jason Kelley

2

Evet. Dispose () on ve IDisposable (hangi "using" yapar) çağrıldığında, bir nesnenin tüm kaynaklarını temizlemesi gerekir. Bu, dosya tanımlayıcılarını temizleme ve kapatma akışlarını içerir.

Sizin durumunuzda, bunu diğer yöntemlere aktarmak istiyorsanız, o zaman bu yöntemlerin okuma / yazma işlemlerini bir kullanma bloğunda yapmadığından emin olmanız gerekir.



-2

"anahtar kelime" kullanarak veya açıkça imha çağrılarak atılan akış

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.