2010-12-05 3 views
9

다음과 같은 코드를 생각해 한편으로라이터가 삭제 될 때 스트림이 처리되는 이유는 무엇입니까?

using (var ms = new MemoryStream()) 
{ 
    using(var writer = BinaryWriter(ms)) 
    { 
     writer.Write(/*something*/); 
     writer.Flush(); 
    } 

    Assert.That(ms.Length > 0); // Throws ObjectDisposedException 
} 

를, 그것의 자원을 처분해야 일회용 객체; 나는 그것을 얻는다. 그러나 다른 한편으로, 객체는 생성되지 않았고이 자원을 소유하지 않았다. 그것은 제공되었다 -> 호출 코드는 그것을 책임 져야한다 ... 안돼?

은이 같은 다른 상황을 생각할 수 없다,하지만 자신의 처분에 폐기하는 일회용 객체를 수신하는 모든 클래스에 대한 프레임 워크에서 일관된 패턴이다?

답변

13

당신은 스트림 당 하나의 라이터를해야합니다 암시 적 가정이있다, 그래서 작가는 편의를 위해 스트림의 소유권을 - 당신이 다음 obly 정리 한 가지가 있습니다.

그러나 나는 동의 이것은 항상 사실이 아니며 종종 불편합니다. 일부 구현 (예 : DeflateStream, GZipStream)을 사용하면 선택할 수 있습니다. 그렇지 않으면 유일한 실제 옵션은 라이터와 기본 스트림 사이에 더미 스트림을 삽입하는 것입니다. http://www.yoda.arachsys.com/csharp/miscutil/

사용법 같은 것을 다음과 같습니다 : IIRC이 정확하게 수행 존 소총의 "MiscUtil"라이브러리의 NonClosingStreamWrapper가

using (var ms = new MemoryStream()) 
{ 
    using(var noClose = new NonClosingStreamWrapper(ms)) 
    using(var writer = BinaryWriter(noClose)) 
    { 
        writer.Write(/*something*/); 
        writer.Flush(); 
    } 

    Assert.That(ms.Length > 0); 
} 
4

나는 완전히 당신과 동의합니다. 이것은 일관된 동작이 아니지만 구현 방법입니다. 매우 직관적이지 않은이 동작에 대한 설명서의 끝에 comments이 있습니다. 모든 스트림 작성자는 기본 스트림의 소유권을 가져 와서 처리합니다. 개인적으로 나는 이런 항상 둥지 내 using 문 :

어설에서 당신이 넣어 것과 같은 코드를 작성해서는 안 있도록
using (var ms = new MemoryStream()) 
using(var writer = BinaryWriter(ms)) 
{ 
    writer.Write(/*something*/); 
} 

.

0

에 대한 생성자 매개 변수를 가지고 있었을 것이다 짓을 할 수있는 옳은 일 스트림 작성기는 생성자가있을 때 스트림을 처리해야하는지 여부를 나타냅니다. Microsoft가 그렇게하지 않았다면 스트림을 랩핑하지만 Dispose 호출을 랩핑 된 스트림으로 전달하지 않는 NonDisposingStream (Of T as Stream) 클래스를 정의하는 것이 좋습니다. 하나는 다음 StreamWriter를 생성자에 새 NonDisposingStream을 통과 할 수 있고, 기본이되는 스트림은 처분 (이 스트림 직접 처분, 물론 필요하다) 안전 할 것입니다.

는 전달 된 객체를 처리 할 목적을 갖는 것은 유용하다. 이러한 행동은 객체 작성자가 처리를 처리하는 일반적인 패턴과 일치하지 않지만 객체 작성자가 객체가 실제로 얼마나 오래 필요할지 모를 상황이 종종 있습니다. 예를 들어, 메서드는 새 Stream을 사용하는 새 StreamWriter를 만들 것으로 예상 될 수 있습니다. StreamWriter의 소유자는 처리시기를 알 수 있지만 내부 스트림의 존재를 알지 못할 수 있습니다. 내부 스트림의 작성자는 외부 StreamWriter가 얼마나 오래 사용될 것인지 전혀 모른다. StreamWriter에 "전달 된"스트림의 소유권을 갖게되면 해당 특정 (일반적인) 경우의 처리 문제가 해결됩니다.

0

나는이 래퍼 클래스를 제안한다 :

public class BetterStreamWriter : StreamWriter 
{ 
    private readonly bool _itShouldDisposeStream; 

    public BetterStreamWriter(string filepath) 
     :base(filepath) 
    { 
     _itShouldDisposeStream = true; 
    } 

    public BetterStreamWriter(Stream stream) 
     : base(stream) 
    { 
     _itShouldDisposeStream = false; 
    } 

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

객체들이 인스턴스화하지 않은 물건을 처분하지 않아야합니다. 파일 스트림 작성자 인 경우 처리해야합니다. 외부 스트림 인 경우에는 그렇지 않아야합니다.

첫 번째 위치에서 파일 열기 경로를 구현하지 않아야합니다.이것은 파일의 쓰기와 수명을 관리하는 객체로서 단일 책임 원칙을 위반합니다.

+1

개체는 소유하지 않은 물건을 처리해서는 안됩니다. 객체를 직접 인스턴스화하는 것 이외의 객체 소유권을 획득하는 것이 가능합니다 (가장 일반적으로는 팩토리 메소드 호출). 팩토리 메서드는 일반적으로 단일 개체 만 반환하기 때문에 메서드에서 얻은 모든 리소스는 해당 개체가 소유해야합니다. 'StreamWriter'가 전달 된 스트림의 소유권을 갖게되면 팩토리 메소드가 합법적으로 스트림을 구성하고 그것을 캡슐화하는'StreamWriter'를 리턴 할 수 있습니다. – supercat

+1

@ supercat eh no. 나는 너에게 동의한다. "물건은 물건을 처분해서는 안된다." 그것은 그것을 말하는 올바른 방법입니다. 그러나,이 경우가 아닙니다. StreamWriter 클래스 자체를 설계하면 호출자가 스트림을 어떻게 처리하는지 알 수 없으며 스트림을 처리 할 수도 없습니다. 여러 리더에서 스트림을 사용하는 경우를 고려하십시오. 그런 다음 그것을 처리 할 것인지에 따라 문을 사용하여 조건부로 StreamReader를 래핑해야합니까? IDisposable은 성명서를 사용하여 왜곡되어야합니다. 질문이 없습니다. –

+0

올바른 접근법은 호출자가 소유권이 이전되었는지 (.NET Framework의 이후 버전에서 수행되는지)를 지정하도록 허용하는'StreamReader' /'StreamWriter'의 생성자를위한 것입니다. 그렇지 않으면,'StreamReader' 형태로 가져온 오디오 데이터를 비동기 적으로 재생할 방법을 작성하는 방법을 고려하십시오. 오디오를 재생하는 코드는 기본 스트림에 대해 아무 것도 모를 수 있으며 'StreamReader'를 구성하는 코드는 재생 코드가 끝날 때 전혀 알 수 없습니다. 스트림이 순수하게 오디오 재생 목적으로 열리는 일반적인 경우에는 ... – supercat

관련 문제