Bir StreamWriter'ı BaseStream'i kapatmadan kapatmanın bir yolu var mı?


117

Benim kök sorun olduğunda olmasıdır usingçağıran Disposebir üzerinde StreamWriter, aynı zamanda elden BaseStream(aynı problem ile Close).

Bunun için bir çözümüm var, ancak görebileceğiniz gibi, akışı kopyalamayı içeriyor. Akışı kopyalamadan bunu yapmanın bir yolu var mı?

Bunun amacı, bir dizinin içeriğini (orijinal olarak bir veritabanından okunan) bir akışa almaktır, böylece akış üçüncü taraf bir bileşen tarafından okunabilir.
Not : Üçüncü taraf bileşenini değiştiremiyorum.

public System.IO.Stream CreateStream(string value)
{
    var baseStream = new System.IO.MemoryStream();
    var baseCopy = new System.IO.MemoryStream();
    using (var writer = new System.IO.StreamWriter(baseStream, System.Text.Encoding.UTF8))
    {
        writer.Write(value);
        writer.Flush();
        baseStream.WriteTo(baseCopy); 
    }
    baseCopy.Seek(0, System.IO.SeekOrigin.Begin);
    return baseCopy;
}

Olarak kullanıldı

public void Noddy()
{
    System.IO.Stream myStream = CreateStream("The contents of this string are unimportant");
    My3rdPartyComponent.ReadFromStream(myStream);
}

İdeal olarak hayali bir yöntem arıyorum BreakAssociationWithBaseStream, örneğin

public System.IO.Stream CreateStream_Alternate(string value)
{
    var baseStream = new System.IO.MemoryStream();
    using (var writer = new System.IO.StreamWriter(baseStream, System.Text.Encoding.UTF8))
    {
        writer.Write(value);
        writer.Flush();
        writer.BreakAssociationWithBaseStream();
    }
    return baseStream;
}


Bunu bir WebRequest'ten gelen bir akışla yapıyordum, ilginç bir şekilde, kodlama ASCII ise ancak UTF8 değilse kapatabilirsiniz. Tuhaf.
tofutim

tofutim, benimkini ASCII olarak kodlamıştım ve hala temeldeki akışı yok ediyor ..
Gerard ONeill

Yanıtlar:


121

.NET Framework 4.5 veya sonraki bir sürümünü kullanıyorsanız, yazıcı kapatıldığında temel akışın açık kalmasını isteyebileceğiniz bir StreamWriter aşırı yüklemesi vardır .

4.5'ten önceki .NET Framework sürümlerinde , akışın sahibi StreamWriter olduğunu varsayar . Seçenekler:

  • Atmayın StreamWriter; sadece sifonu çek.
  • Çağrıları yok sayan Close/ Disposeancak diğer her şeye vekalet eden bir akış sarmalayıcı oluşturun . Oradan kapmak isterseniz MiscUtil'de bunun bir uygulaması var.

15
Açıkçası 4.5 aşırı yük düşünülmemiş bir tavizdi - aşırı yük, 0 veya boş olamayan tampon boyutunu gerektirir. Dahili olarak 128 karakterin minimum boyut olduğunu biliyorum, bu yüzden sadece 1'e ayarlıyorum. Aksi takdirde bu 'özellik' beni mutlu ediyor.
Gerard ONeill

Bu leaveOpenparametreyi StreamWriteroluşturulduktan sonra ayarlamanın bir yolu var mı ?
c00000fd

@ c00000fd: Farkında olduğumdan değil.
Jon Skeet

1
@Yepeekai: "Bir alt yönteme bir akım iletirsem ve bu alt yöntem StreamWriter'ı oluşturursa, o alt yöntemin yürütülmesinin sonunda atılacaktır" Hayır, bu doğru değil. Sadece bir şey Disposeonu çağırırsa imha edilecektir . Yöntem sonlandırma bunu otomatik olarak yapmaz. Bir sonlandırıcı varsa daha sonra sonuçlandırılabilir, ancak bu aynı şey değil - ve hangi tehlikeyi beklediğiniz hala net değil. StreamWriterGC tarafından otomatik olarak atılabileceği için bir yöntemden bir döndürmenin güvenli olmadığını düşünüyorsanız , bu doğru değil.
Jon Skeet

1
@Yepeekai: Ve IIRC'nin StreamWriterbir sonlandırıcısı yok - tam da bu nedenle olmasını beklemiyorum.
Jon Skeet

44

.NET 4.5 bunun için yeni bir yöntem aldı!

http://msdn.microsoft.com/EN-US/library/gg712853(v=VS.110,d=hv.2).aspx

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

Teşekkürler dostum! Bunu bilmiyordum ve .NET 4.5'i hedeflemeye başlamam için iyi bir neden olacak herhangi bir şey varsa!
Vectovox

22
Utanç, bufferSize'ın ayarlanmasını gerektirmeyen aşırı yük yoktur. Oradaki varsayılan durumdan memnunum. Kendim geçmek zorundayım. Dünyanın sonu değil.
Andy McCluggage

3
Varsayılan bufferSizedeğer 1024. Detaylar burada .
Alex Klaus

35

Basitçe demiyorlar Disposeüzerinde StreamWriter. Bu sınıfın tek kullanımlık olmasının nedeni, yönetilmeyen kaynağı tutması değil, yönetilmeyen kaynakları tutabilecek akışın atılmasına izin vermesidir. Temel akışın yaşamı başka bir yerde ele alınırsa, yazarı elden çıkarmaya gerek yoktur.


2
@Marc, Flushverileri arabelleğe alması durumunda işi yapmak olmaz mı?
Darin Dimitrov

3
Güzel, ama CreateStream'den çıktığımızda, StreamWrtier toplanabilir ve üçüncü bölüm okuyucuyu GC'ye karşı yarışmaya zorluyor, ki bu benim kalmak istediğim bir durum değil. Yoksa bir şeyi mi kaçırıyorum?
Binary Worrier

9
@BinaryWorrier: Hayır, yarış koşulu yok: StreamWriter'ın bir finalizörü yok (ve gerçekten olmamalı).
Jon Skeet

10
@Binary Worrier: Yalnızca kaynağa doğrudan sahipseniz bir sonlandırıcıya sahip olmalısınız . Bu durumda StreamWriter, Akışın gerekirse kendini temizleyeceğini varsaymalıdır.
Jon Skeet

2
Görünüşe göre StreamWriter'ın 'close' yöntemi de akışı kapatır ve ortadan kaldırır. Bu nedenle biri yıkamalı, ancak akış yazarını kapatmamalı veya atmamalı, böylece akışı kapatmaz, bu da akışı elden çıkarmakla eşdeğerdir. API'den çok fazla "yardım" burada.
Gerard Bireill

5

Bellek akışı, akış kapalıyken bile kullanılabilen bir ToArray özelliğine sahiptir. Diziye, Konum özelliğinden bağımsız olarak akış içeriğini bir bayt dizisine yazar. Yazdığınız akışa göre yeni bir akış oluşturabilirsiniz.

public System.IO.Stream CreateStream(string value)
{
    var baseStream = new System.IO.MemoryStream();
    var baseCopy = new System.IO.MemoryStream();
    using (var writer = new System.IO.StreamWriter(baseStream, System.Text.Encoding.UTF8))
    {
        writer.Write(value);
        writer.Flush();
        baseStream.WriteTo(baseCopy); 
    }
    var returnStream = new System.IO.MemoryStream( baseCopy.ToArray());
    return returnStream;
}

Bu, döndürülen diziyi içerik boyutuyla doğru şekilde sınırlıyor mu? Çünkü Stream.Positionedebilirsiniz değil o tanzim oluyor sonra çağrılabilir.
Nyerguds

2

StreamWriter'ın bir neslini yaratmanız ve dispose yöntemini geçersiz kılmanız gerekir, her zaman false değerini atma parametresine iletirseniz, akış yazıcıyı kapatmaya DEĞİL, StreamWriter yalnızca dispose yöntemini kapatmaya zorlar, bu nedenle buna gerek yoktur geçersiz kıl (tabii ki isterseniz tüm kurucuları ekleyebilirsiniz, bende sadece bir tane var):

public class NoCloseStreamWriter : StreamWriter
{
    public NoCloseStreamWriter(Stream stream, Encoding encoding)
        : base(stream, encoding)
    {
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(false);
    }
}

3
Bunun düşündüğün şeyi yapmadığına inanıyorum. disposingBayrak parçasıdır desen . Her zaman geçirmeden için temelde sinyalleri için temel sınıf yöntemiyle o (Aradığınızda kadar değil hangi sonlandırıcıyı gelen çağrıldığı açıkça) ve dolayısıyla herhangi yönetilen nesneler erişmesi gerekir. Bu nedenle temel akışı atmaz. Ancak, bunu başarmanın yolu bir hacklemedir; ilk etapta aramamak çok daha kolay olurdu ! IDisposablefalseDispose(bool)StreamWriterDispose()Dispose
stakx - artık

Bu gerçekten Symantec'in, akışı tamamen sıfırdan yeniden yazmak dışında yapacağınız her şey bir hack olacak. Kesinlikle olsa da, basitçe base.Dispose (false) diyemezdiniz, ancak işlevsel bir fark olmayacak ve örneğimin netliğini seviyorum. Bununla birlikte, bunu aklınızda bulundurun, StreamWriter sınıfının gelecekteki bir sürümü, dağıtıldığında akışı kapatmaktan daha fazlasını yapabilir, bu nedenle dispose (false) çağrısı da bunu kanıtlar. Ama her biri kendi başına.
Aaron Murgatroyd

2
Bunu yapmanın başka bir yolu da, Kapat yönteminin temeldeki akışı kapatmak yerine hiçbir şey yapmadığı başka bir akışı içeren kendi akış sarmalayıcınızı oluşturmaktır, bu bir hack olmaktan çok daha fazla iştir.
Aaron Murgatroyd

Şaşırtıcı zamanlama: Sadece aynı şeyi önermek (muhtemelen adlandırılmış dekoratör sınıfı oldu OwnedStream, o yoksaydıklarınız Dispose(bool)ve Close).
stakx - artık

Evet, yukarıdaki kod, hızlı ve kirli bir yöntem için bunu nasıl yaptığımdır, ancak ticari bir uygulama veya benim için gerçekten önemli olan bir şey yapıyor olsaydım, bunu Stream wrapper sınıfını kullanarak doğru bir şekilde yapardım. Şahsen ben Microsoft'un burada bir hata yaptığını düşünüyorum, yayın yazarı bunun yerine temeldeki akışı kapatmak için bir boole özelliğine sahip olmalıydı, ancak o zaman Microsoft'ta çalışmıyorum, bu yüzden tahmin ettiğim şeyi yapıyorlar: D
Aaron Murgatroyd
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.