2009-10-21 6 views
4

질문은 finalize가 호출 될 때 객체가 리소스를 처리한다는 사실을 테스트하는 방법입니다. 클래스에 대한 코드 :테스트 최종 자 및 IDisposable

public class TestClass : IDisposable { 

    public bool HasBeenDisposed {get; private set; } 

    public void Dispose() { 
     HasBeenDisposed = true; 
    } 

    ~TestClass() { 
     Dispose(); 
    } 
} 

을 유의하시기 바랍니다 난 그냥 지금은 먼저 테스트 할 수있는 방법을 찾아 원하는대로 폐기 /의 Finalize의 올바른 구현에 대해 걱정하지 않는다. 이 단계에서 가정하면 충분합니다. HasBeenDisposed은 Dispose/Finalize가 호출되면 true로 설정됩니다.

[Test] 
public void IsCleanedUpOnGarbadgeCollection() { 
    var o = new TestClass(); 
    o.HasBeenDisposed.Should().Be.False(); 

    **var weak = new WeakReference(o, true); // true =Track after finalisation 
    o = null; // Make eligible for GC** 

    GC.Collect(0, GCCollectionMode.Forced); 
    GC.WaitForPendingFinalizers(); 


    **((TestClass)weak.Target)**.HasBeenDisposed.Should().Be.True(); 
} 

또는 더 나은 (가 UPDATE 이후에 추가) 좋아하는 코드 :

[Test] 
public void IsCleanedUpOnGarbadgeCollection() { 
    WeakReference weak = null; 

    // Use action to isolate instance and make them eligible for GC 
    // Use WeakReference to track the object after finalisaiton 
    Action act =() = { 
     var o = new TestClass(); 
     o.HasBeenDisposed.Should().Be.False(); 
     weak = new WeakReference(o, true); // True=Track reference AFTER Finalize 
    }; 

    act(); 

    GC.Collect(0, GCCollectionMode.Forced); 
    GC.WaitForPendingFinalizers(); 

    // No access to o variable here which forces us to use WeakReference only to avoid error 
    ((TestClass)weak.Target).HasBeenDisposed.Should().Be.True(); 
} 

이 테스트 WeakReference를으로 업데이트
:

실제 시험 내가 좋아하는 외모를 썼다 실패 (시동 후 업데이트)하지만 다음을 준수합니다 (업데이트]) :

  1. GC.WaitForPendingFinalizers()는 스레드를 중단 수행하고 O를 의 인스턴스를 확정,하지만 경우가 뿌리되지 않습니다. 그것에 NULL을 할당하고 WeakReference를 사용하여 종료 후 가져옵니다.
  2. 이 인스턴스를 보유하지 않을 때 올바른 위치에서 Finilize (소멸자) 코드가 실행됩니다.

그래서 이것을 테스트하는 올바른 방법은 무엇입니까? 나는 무엇을 놓치나요?

나는 변수가 인데, GC가 그것을 수집하지 못하게한다고 가정합니다.
업데이트 : 예, 문제입니다. WeakReference를 대신 사용해야했습니다.

+0

이것은 정확히 내가 필요한 것입니다. 액션 델리게이트를 사용하면 코드가 별도의 스택 프레임에 저장되어 유닛 테스트에서 객체를 올바르게 수집 할 수 있습니다. – NathanAW

+0

이 솔루션을 이용해 주셔서 감사합니다. 나에게 그것은 단순히 원래 변수를 null로 설정하여 대리자없이 작동한다. (편집 : necropost 죄송합니다, 날짜를 알리지 않았다) – amnesia

답변

3

"GC가 콜렉션을하지 못하게하는 변수가 있다고 생각합니다." 옳은. 스택에 참조가 존재한다는 것은 개체가 도달 할 수 있음을 의미하므로 컬렉션 (및 완료)에 적합하지 않습니다.

개체가 참조 될 때까지 개체가 마무리되지 않으므로 최종 동작을 테스트하는 것이 까다로울 수 있습니다. (그것에 대한 주장을하기 위해 객체에 대한 참조가 필요합니다!) 한 가지 방법은 그것을 간접적으로 수행하는 것입니다 : 객체가 finalization 동안 어떤 종류의 메시지를 보내도록합니다. 그러나 이것은 테스트 목적으로 파이널 라이 제이션 코드를 왜곡합니다. 객체에 대한 약한 참조를 보유 할 수도 있습니다.이 참조는 finalization에 적합하고 finalizer에서 자체를 부활 시키지만 다시는 프로덕션 코드에서 자체를 부활 시키길 원하지 않습니다.

+0

마무리 후 목표 추적과 함께 WeakReference를 사용하도록 코드를 변경했습니다. 질문을 전체 코드로 업데이트했습니다. 저에게 아이디어를 주셔서 감사합니다. 당신이 간단한 생각이라면 모든 것이 간단합니다 :) –

0

개체에 대한 로컬 참조가있는 경우 개체가 수집되는 이유는 무엇입니까?

+0

예. 내 잘못이야. 나는 마지막 문장에서 내 질문에 부분적으로 대답했다. 나는 finiliser를 시험하는 다른 것을 발명해야한다. –

0

메모리 프로파일 러는 누출을 테스트하는 가장 적절한 방법입니다.

.Net 메모리 프로파일 러를 권장 할 수 있습니다.