BaseStream을 닫지 않고 StreamWriter를 닫는 방법이 있습니까?


117

내 루트 문제 즉 using통화DisposeStreamWriter, 그것은 또한 처분 BaseStream(같은 문제로를 Close).

이에 대한 해결 방법이 있지만 보시다시피 스트림 복사가 포함됩니다. 스트림을 복사하지 않고이 작업을 수행 할 수있는 방법이 있습니까?

이것의 목적은 문자열의 내용 (원래 데이터베이스에서 읽음)을 스트림으로 가져 와서 제 3 자 구성 요소에서 스트림을 읽을 수 있도록하는 것입니다.
NB : 타사 구성 요소를 변경할 수 없습니다.

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;
}

사용

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

이상적으로 불리는 가상의 방법을 찾고 있어요 BreakAssociationWithBaseStream, 예를 들어,

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;
}

이것은 비슷한 질문입니다 : stackoverflow.com/questions/2620851
Jens Granlund

WebRequest의 스트림으로이 작업을 수행했습니다. 흥미롭게도 인코딩이 UTF8이 아닌 ASCII이면 닫을 수 있습니다. 기묘한.
tofutim

tofutim, 나는 나의 ASCII로 인코딩했고, 여전히 .. 기본이되는 스트림을 처분
제라드 오닐을

답변:


121

.NET Framework 4.5 이상을 사용하는 경우 작성기가 닫힐 때 기본 스트림을 열어 두도록 요청할 수 있는 StreamWriter 오버로드가 있습니다 .

4.5 이전 버전의 .NET Framework StreamWriter 에서는 스트림을 소유 한다고 가정 합니다. 옵션 :

  • 처분하지 마십시오 StreamWriter; 그냥 플러시하십시오.
  • Close/에 대한 호출을 무시 Dispose하지만 다른 모든 것을 프록시 하는 스트림 래퍼를 만듭니다 . 거기에서 가져오고 싶다면 MiscUtil 에서 구현 했습니다.

15
분명히 4.5 오버로드는 고려되지 않은 양보였습니다. 오버로드에는 버퍼 크기가 필요하며 0도 null이 될 수 없습니다. 내부적으로는 128자가 최소 크기라는 것을 알고 있으므로 1로 설정했습니다. 그렇지 않으면이 '기능'이 저를 기쁘게합니다.
Gerard ONeill 2014 년

생성 leaveOpen후 해당 매개 변수 를 설정하는 방법이 StreamWriter있습니까?
c00000fd

@ c00000fd : 내가 아는 것은 아닙니다.
Jon Skeet

1
@Yepeekai : "하위 메서드에 스트림을 전달하고 해당 하위 메서드가 StreamWriter를 생성하면 해당 하위 메서드의 실행이 끝날 때 처리됩니다."아니요, 이는 사실이 아닙니다. 뭔가 호출 Dispose하는 경우에만 폐기됩니다 . 메소드 엔딩은 자동으로 수행하지 않습니다. 할 수있다 마무리 가 종료자를을 가지고 있지만 그 같은 일이 아니라면 이상 - 그리고 아직도 당신이 기대하고있는 무슨 위험이 분명하지 않다. StreamWriterGC에 의해 자동으로 처리 될 수 있기 때문에 메서드에서 를 반환하는 것이 안전하지 않다고 생각하는 경우 이는 사실이 아닙니다.
Jon Skeet

1
@Yepeekai : 그리고 IIRC에는 종료 StreamWriter자가 없습니다. 정확히 이런 이유로 기대하지 않습니다.
Jon Skeet

44

.NET 4.5에는 새로운 방법이 있습니다!

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
)

고마워 친구! 이것을 몰랐고, .NET 4.5를 목표로 시작하는 좋은 이유가 있다면!
Vectovox 2014 년

22
부끄러운 점은 bufferSize를 설정할 필요가없는 과부하가 없습니다. 나는 거기 기본값에 만족합니다. 직접 통과해야합니다. 세상의 끝이 아닙니다.
Andy McCluggage 2014

3
기본값 bufferSize1024입니다. 자세한 내용은 여기에 있습니다 .
Alex Klaus

35

간단하게 호출하지 않습니다 DisposeStreamWriter. 이 클래스가 삭제되는 이유는 관리되지 않는 리소스를 보유하기 때문이 아니라 자체적으로 관리되지 않는 리소스를 보유 할 수있는 스트림의 처리를 허용하기 때문입니다. 기본 스트림의 수명이 다른 곳에서 처리되는 경우 작성자를 삭제할 필요가 없습니다.


2
@Marc, Flush데이터를 버퍼링하는 경우 작업을 호출 하지 않습니까?
Darin Dimitrov

3
좋습니다.하지만 일단 CreateStream을 종료하면 StreamWrtier를 수집 할 수 있으므로 제 3 자 독자가 GC와 경쟁하게되는데, 이는 제가 남겨두고 싶은 상황이 아닙니다. 아니면 제가 뭔가를 놓치고 있습니까?
Binary Worrier

9
@BinaryWorrier : 아니요, 경합 조건이 없습니다 : StreamWriter에는 종료자가 없습니다 (실제로는 안 됨).
Jon Skeet

10
@Binary Worrier : 리소스 를 직접 소유 한 경우에만 종료자가 있어야 합니다. 이 경우 StreamWriter는 필요한 경우 Stream이 스스로 정리할 것이라고 가정해야합니다.
Jon Skeet

2
StreamWriter의 'close'메서드도 스트림을 닫고 처리하는 것 같습니다. 따라서 스트림 작성기를 플러시해야하지만 닫거나 처리하지 않아야합니다. 따라서 스트림을 닫지 않습니다. 이는 스트림을 처리하는 것과 동일합니다. 여기 API의 "도움"이 너무 많습니다.
Gerard ONeill 2014 년

5

메모리 스트림에는 스트림이 닫혀 있어도 사용할 수있는 ToArray 속성이 있습니다. To Array는 Position 속성에 관계없이 스트림 내용을 바이트 배열에 씁니다. 작성한 스트림을 기반으로 새 스트림을 만들 수 있습니다.

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;
}

반환 된 배열을 콘텐츠 크기로 올바르게 제한합니까? 때문에 Stream.Position없습니다 가 배치되어있어 다음에 호출 될 수있다.
Nyerguds

2

StreamWriter의 하위 항목을 만들고 dispose 메서드를 재정의해야합니다. 항상 disposing 매개 변수에 false를 전달하면 스트림 작성기가 닫히지 않습니다. StreamWriter는 close 메서드에서 dispose를 호출하기 만하면됩니다. 재정의하십시오 (물론 원하는 경우 모든 생성자를 추가 할 수 있습니다.

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

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

3
나는 이것이 당신이 생각하는대로하지 않는다고 믿습니다. disposing플래그의 일부 패턴 . 항상 기본 클래스 의 메서드에 전달 하는 것은 기본적으로 종료 자 ( 명시 적으로 호출 할 때는 그렇지 않음)에서 호출되고 있다는 신호 이므로 관리되는 개체에 액세스해서는 안됩니다. 이것이 기본 스트림을 처리하지 않는 이유입니다. 그러나 이것을 달성 한 방법은 해킹입니다. 애초에 전화하지 않는 것이 훨씬 더 간단 할 것입니다 ! IDisposablefalseDispose(bool)StreamWriterDispose()Dispose
stakx-더 이상 기여하지 않음 2012-08-11

시만텍은 스트리밍을 완전히 처음부터 다시 작성하는 것 외에는 무엇이든 해킹이 될 것입니다. 확실히, 당신은 단순히 base.Dispose (false)를 전혀 호출 할 수는 없지만 기능적 차이는 없을 것이며, 저는 제 예제의 명확성을 좋아합니다. 그러나이를 염두에 두십시오. StreamWriter 클래스의 향후 버전은 처리 할 때 스트림을 닫는 것 이상을 수행 할 수 있으므로 dispose (false)를 호출하면 향후에도이를 증명할 수 있습니다. 그러나 각자 자신에게.
Aaron Murgatroyd 2012-08-12

2
이를 수행하는 또 다른 방법은 Close 메서드가 기본 스트림을 닫는 대신 아무 작업도 수행하지 않는 다른 스트림을 포함하는 자체 스트림 래퍼를 만드는 것입니다. 이것은 해킹이 덜하지만 더 많은 작업입니다.
Aaron Murgatroyd 2012-08-12

놀라운 타이밍 : 나는 똑같은 것을 제안하려고했다 ( 그리고 OwnedStream를 무시하는 데코레이터 클래스, 아마도 명명 될 수 있음 ). Dispose(bool)Close
stakx-더 이상 기여하지 않음 2012-08-12

예, 위의 코드는 빠르고 더러운 방법으로 수행하는 방법이지만 상용 응용 프로그램이나 실제로 중요한 것을 만드는 경우 Stream 래퍼 클래스를 사용하여 올바르게 수행합니다. 개인적으로 나는 마이크로 소프트가 여기에 실수를 생각의 StreamWriter는 대신 기본 스트림을 닫습니다 부울 속성을 가지고 있어야하지만, 그들이 무엇을 할 수 있도록 내가 생각처럼 나는 마이크로 소프트에서 일을 해달라고 : D
아론 Murgatroyd
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.