2010-11-19 2 views
6

나는이 질문을 제기 할 때 토론을 혼란스럽게 할 것이라고 생각했습니다. returning a Disposable (IDisposable) object from a function에 관한 질문을했습니다. 그럼 내가 호출하기 때문에,나는/일회용품을 사용하는 작업을 이해하지 못합니다.

class UsingTest 
{ 
    public class Disposable : IDisposable 
    { 
     public void Dispose() 
     { 
      var i = 0; 
      i++; 
     } 
    } 
    public static Disposable GetDisposable(bool error) 
    { 
     var obj = new Disposable(); 
     if (error) 
      throw new Exception("Error!"); 
     return obj; 
    } 
} 

내가 이런 식으로 일부러 코드 :

내가 몇 가지 예제 코드를 생성

using (var tmp = UsingTest.GetDisposable(true)) { } 

디버거를 사용하여, 심지어는 Dispose 방법은 실행 결코 알 이미 Disposable 개체를 인스턴스화했지만 Dispose의 목적을 정확히 이해하면이 클래스가 실제로 핸들을 열었 으면 비슷한 작업을 마친 즉시 닫지 않을 것입니다.

이 문제는 내가 예상했던 것과 일치하기 때문에 묻습니다. 그러나 관련된 질문에 대한 답변에서 사람들은 using이 모든 것을 처리 할 것임을 나타 냈습니다.

여전히가이 모든 것을 처리하는 경우 누군가 내가 설명하지 못한 것을 설명 할 수 있습니까? 그러나이 코드가 실제로 리소스 누수를 일으킬 수 있다면 GetDisposable (IDisposable 개체를 인스턴스화하고 return 문 이전에 예외를 throw 할 수있는 코드를 실행해야한다는 조건으로) 코드를 제안하는 방법은 무엇입니까?

답변

4

당신이 GetDisposable을 위해 원하는 것을 의미에 따라, 이것은 내가 그것을 구현하는 것이 방법 아마도 :

public static Disposable GetDisposable(bool error) 
{ 
    var obj = new Disposable(); 

    try 
    { 
     if (error) 
      throw new Exception("Error!"); 

     return obj; 
    } 
    catch (Exception) 
    { 
     obj.Dispose(); 
     throw; 
    } 
} 
11

호출하지 않는 이유는 할당하는 방식 때문입니다. GetDisposable(bool) 함수가 예외를 던 졌기 때문에 절대 반환하지 않기 때문에 "tmp"변수는 전혀 할당되지 않습니다.

당신이

using (var tmp = new Disposable()) 
{ 
    throw new ArgumentException("Blah"); 
} 

은 다음 IDisposable::Dispose() 실제로 전화를받을 않는 것을 볼 것이다, 대신 다음과 같은 말을한다면.

이해해야 할 기본적 사항은 using 블록이 IDisposable 개체에 대한 유효한 참조를 가져야한다는 것입니다. using 블록에 선언 된 변수가 할당되지 않도록 예외가 발생하면 using 블록에 IDisposable 개체에 대한 정보가 없으므로 불운을 빕니다.

함수에서 IDisposable 객체를 반환을 위해, 당신은 실패의 경우에 Dispose()를 호출하는 함수의 내부 표준 catch 블록을 사용해야하지만,이 개체를 처리하기 때문에 분명히 당신은 using 블록을 사용하지 말아야으로 너 자신을 그렇게 할 준비가되기 전에.

+0

이 맞습니다. IDisposable을 만드는 메서드가 참조를 반환하기 전에 실패 할 수 있으면 dispose가 호출되도록해야합니다. – ScottS

1

IDisposable 인터페이스는 단순히 구현 클래스에 Dispose 메서드가 있음을 보장합니다. 이 메서드를 호출하는 것과 관련하여 아무 것도 수행하지 않습니다. using 블록은 블록이 종료 될 때 객체에서 Dispose를 호출합니다.

+0

'IDisposable'은 선택적이지만 구현되면 자동으로'Dispose'가 호출됩니다 : C#'foreach' 또는 vb.net'For Each'가 호출하는 GetEnumerator 함수의 반환 유형 문이'IDisposable'을 구현하거나 반환 유형이'IEnumerator'이고 그것이 반환하는 인스턴스가'IDisposable'을 구현하면 컴파일러는 열거가 완료된 후에'IDisposable.Dispose'를 호출합니다. – supercat

+0

@supercat : 알고있는 것이 좋습니다. 나는 이것이 사실이라는 것을 깨닫지 못했다. –

+0

조금 까다 롭습니다. 반환 유형이'IDisposable'을 구현하지 않는 * 클래스 *이지만'GetEnumerator'가'IDisposable'을 구현하는 파생 클래스를 반환하면'Dispose' 메소드는 호출되지 않지만 비 - 일반 'IEnumerator', 컴파일러는 반환 된 객체가'IDisposable'을 구현하고 런타임에 그것을 검사 할 수 있다는 것을 알 수 있습니다. 못생긴 디자인이지만'Dispose'가'IEnumerator'에서 생략되었으므로 실제로 대안이 없습니다. – supercat

1

GetDisposableIDisposable을 만들지 만 예외를 던져서이 함수를 종료하기 때문에 결코 반환되지 않으므로 tmp은 할당되지 않습니다. 사용하는 문장은

var tmp = UsingTest.GetDisposable(true); 
try { } 
finally 
{ 
    if(tmp != null) tmp.Dispose(); 
} 

에 대한 속기이며 try 블록에 도달하지 않습니다.귀하의 예제에서이 솔루션은 일회용 OBJ 작성하기 전에 error 플래그를 확인하는 것입니다 :

public static Disposable GetDisposable(bool error) 
{ 
    if (error) 
     throw new Exception("Error!"); 
    return new Disposable(); 
} 
+0

아니오; 코드에 놓인 조건에서 실패합니다. – palswim

3

이 tmp 변수가 할당되지 않았기 때문입니다. 일회용 물체에주의해야합니다. GewtDisposable에 대한 더 나은 정의는 다음과 같습니다.

public static Disposable GetDisposable(bool error) 
{ 
    var obj = new Disposable(); 

    try 
    { 
     if (error) 
      throw new Exception("Error!"); 
     return obj; 
    } 
    catch 
    { 
     obj.Dispose(); 
     throw; 
    } 
} 

obj가 처리되도록합니다.

+0

약간의 개선은 주요 객체 변수를 두 번째 변수에 복사하고 두 번째 변수를 반환하기 전에 첫 번째 변수를 null로 만든 다음 처분을 처리하기 위해 "catch"대신 "finally"블록을 사용하는 것입니다 (변수가 null, 처리하지 말아주세요). 이렇게하면 예외가 다시 나타나지 않고 적절한 지점에서 발생했음을 알 수 있습니다. – supercat

0

관련된 질문은 Handling iDisposable in failed initializer or constructor이고 나는 대답을 생각합니다. 당신이 실패한 생성자에서 일회용 객체를 누출시키지 않으려면 생성자에서 객체의 복사본을 밀반입해야합니다 (예 : 건네받은 컨테이너에 건네지거나, 참조에 의해 건네받은 변수에 할당 할 수있다), 생성자의 호출을 catch 블록에 랩한다. Icky, 그러나 나는 그것을 더 잘하는 방법을 모른다. VB.net은 실제로 이니셜 라이저의 작동 방식 때문에 C#보다 조금 더 잘 관리 할 수 ​​있습니다.

관련 문제