Programing

Dispose가 'using'블록에 대해 호출되지 않는 상황이 있습니까?

lottogame 2020. 11. 13. 07:42
반응형

Dispose가 'using'블록에 대해 호출되지 않는 상황이 있습니까?


이것은 전화 인터뷰 질문이었습니다. 사용 블록에 의해 범위가 선언 된 개체에 대해 Dispose가 호출되지 않는 시간이 있습니까?

내 대답은 '아니오'였습니다. 사용 블록 중에 예외가 발생하더라도 Dispose는 계속 호출됩니다.

면접관은 동의하지 않고 - 블록에 using싸여 있으면 catch 블록에 들어갈 때 Dispose가 호출되지 않는다고 말했습니다 .trycatch

이것은 구조에 대한 나의 이해와 상반되며, 나는 면접관의 관점을 뒷받침하는 어떤 것도 찾을 수 없었습니다. 그가 맞습니까, 아니면 제가 그 질문을 오해했을까요?


void Main()
{
    try
    {
        using(var d = new MyDisposable())
        {
            throw new Exception("Hello");
        }
    }
    catch
    {
        "Exception caught.".Dump();
    }

}

class MyDisposable : IDisposable
{
    public void Dispose()
    {
        "Disposed".Dump();
    }
}

이것은 다음을 생성했습니다.

Disposed
Exception caught

그래서 나는 똑똑한 면접관이 아니라 당신에게 동의합니다 ...


using 블록에서 Dispose가 호출되지 않도록하는 네 가지 사항 :

  1. 사용 블록 내부에서 컴퓨터의 정전.
  2. 사용 블록 내부에서 기계가 원자 폭탄에 녹아 있습니다.
  3. StackOverflowException, AccessViolationException그리고 아마도 다른 것과 같은 잡을 수없는 예외 .
  4. Environment.FailFast

이상하게도 오늘 아침에 사용 블록에서 Dispose가 호출되지 않는 상황에 대해 읽었습니다. MSDN 에서이 블로그 를 확인하십시오. 전체 컬렉션을 반복하지 않을 때 IEnumerable 및 yield 키워드와 함께 Dispose를 사용하는 것입니다.

불행히도 이것은 예외 사례를 다루지 않습니다. 솔직히 나는 그것에 대해 잘 모르겠습니다. 나는 그것이 끝날 것이라고 예상했지만 아마도 간단한 코드로 확인할 가치가 있습니까?


정전, Environment.FailFast()반복자 또는 모든 흥미로운 using것에 의한 부정 행위에 대한 다른 답변 null입니다. 그러나 나는 그것이 궁금 아무도 내가 생각하는 가장 일반적인 상황 언급되지 찾을 Dispose()도의 존재에 호출되지 않습니다 using식 내부가 때 using예외가 발생합니다.

물론 이것은 논리적입니다.의 표현에 using예외가 발생했기 때문에 할당이 이루어지지 않았고 우리가 호출 할 수있는 것이 없습니다 Dispose(). 그러나 삭제 가능한 개체는 이미 절반으로 초기화 된 상태 일 수 있지만 존재할 수 있습니다. 이 상태에서도 이미 관리되지 않는 리소스를 보유 할 수 있습니다. 이것이 일회용 패턴을 올바르게 구현하는 것이 중요한 또 다른 이유입니다.

문제가있는 코드의 예 :

using (var f = new Foo())
{
    // something
}


class Foo : IDisposable
{
    UnmanagedResource m_resource;

    public Foo()
    {
        // obtain m_resource

        throw new Exception();
    }

    public void Dispose()
    {
        // release m_resource
    }
}

Here, it looks like Foo releases m_resource correctly and we are using using correctly too. But the Dispose() on Foo is never called, because of the exception. The fix in this case is to use finalizer and release the resource there too.


The using block gets turned by the compiler into a try/finally block of its own, within the existing try block.

For example:

try 
{
    using (MemoryStream ms = new MemoryStream())
        throw new Exception();
}
catch (Exception)
{
    throw;
}

becomes

.try
{
  IL_0000:  newobj     instance void [mscorlib]System.IO.MemoryStream::.ctor()
  IL_0005:  stloc.0
  .try
  {
    IL_0006:  newobj     instance void [mscorlib]System.Exception::.ctor()
    IL_000b:  throw
  }  // end .try
  finally
  {
    IL_000c:  ldloc.0
    IL_000d:  brfalse.s  IL_0015
    IL_000f:  ldloc.0
    IL_0010:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
    IL_0015:  endfinally
  }  // end handler
}  // end .try
catch [mscorlib]System.Exception 
{
  IL_0016:  pop
  IL_0017:  rethrow
}  // end handler

The compiler won't rearrange things. So it happens like this:

  1. Exception is thrown in, or propagates to, the using block's try part
  2. Control leaves the using block's try part, and enters its finally part
  3. Object is disposed by the code in the finally block
  4. Control leaves the finally block, and the exception propagates out to the outer try
  5. Control leaves the outer try and goes into the exception handler

Point being, the inner finally block always runs before the outer catch, because the exception doesn't propagate til the finally block finishes.

The only normal case where this won't happen, is in a generator (excuse me, "iterator"). An iterator gets turned into a semi-complicated state machine, and finally blocks are not guaranteed to run if it becomes unreachable after a yield return (but before it has been disposed).


using (var d = new SomeDisposable()) {
    Environment.FailFast("no dispose");
}

Yes there is a case when dispose won't be called... you are over thinking it. The case is when the variable in the using block is null

class foo
{
    public static IDisposable factory()
    {
        return null;
    }
}

using (var disp = foo.factory())
{
    //do some stuff
}

will not throw an exception but would if dispose was called in every case. The specific case that your interviewer mentioned is wrong though.


The interviewer is partially right. Dispose may not correctly clean up the underlying object on a case-by-case basis.

WCF for example has a few known issues if an exception is thrown while in a using block. Your interviewer was probably thinking of this.

Here is an article from MSDN on how to avoid issues with the using block with WCF. Here is Microsoft's official workaround, although I now think that a combination of that answer and this one is the most elegant approach.

참고URL : https://stackoverflow.com/questions/7595372/is-there-a-situation-in-which-dispose-wont-be-called-for-a-using-block

반응형