2009-10-21 2 views
4

Вопрос в том, как я могу проверить, что объект распределяет ресурсы при вызове finalize. Код класса:Тестирование финализаторов и IDisposable

public class TestClass : IDisposable { 

    public bool HasBeenDisposed {get; private set; } 

    public void Dispose() { 
     HasBeenDisposed = true; 
    } 

    ~TestClass() { 
     Dispose(); 
    } 
} 

Обратите внимание, что я не забочусь о правильном выполнении Dispose/Finalize только сейчас, когда я хочу, чтобы найти способ, чтобы проверить его первым. На этом этапе достаточно принять значение . HasBeenDisposed будет установлен в true, если вызывается утилита Dispose/Finalize.

Действительное тест, который я написал, как выглядит:
ОБНОВЛЕНО С WeakReference:

[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(); 
} 

Этот тест (ПРОСМОТРЕТЬ ПОСЛЕ ОБНОВЛЕНИЯ), но я наблюдаю за следующим (ОБНОВЛЕНО):

  1. GC.WaitForPendingFinalizers() делает приостановить поток и дорабатывает экземпляр в о, но только если не укоренились. Назначил NULL к нему и использовал WeakReference, чтобы получить его после завершения.
  2. Код завершения (деструктор) выполнен в правильной точке, когда o не имеет экземпляра.

Итак, какой правильный способ проверить это. Что мне не хватает?

Я полагаю, что это переменная о, который предотвращает GC из коллекции его.
ОБНОВЛЕНИЕ: Да, это проблема. Пришлось использовать WeakReference.

+0

Это было именно то, что мне нужно. Ваше использование делегата Action получило мой код в отдельный стек стека и позволило моему модульному тесту правильно собирать объекты. – NathanAW

+0

Спасибо за это решение, он отлично работает. Для меня это работает без делегата, просто устанавливая исходную переменную в значение null. (изменить: извините за некропость, не заметили дату) – amnesia

ответ

3

«Я полагаю, что это переменная o, которая препятствует сборке GC». Верный. Существование ссылки в стеке означает, что объект доступен, и, следовательно, не имеет права на сбор (и финализацию).

Поскольку объект не будет доработан до тех пор, пока не будет ссылок на него, тестирование поведения по окончательной настройке, вероятно, будет сложным. (Для того, чтобы делать утверждения об этом, вам нужна ссылка на объект!) Один из способов - косвенно сделать это: отправить объект на какое-то сообщение во время финализации. Но это искажает код завершения только для тестовых целей. Вы могли бы также иметь слабую ссылку на объект, что сделало бы его пригодным для окончательной доработки, и воскресить его в финализаторе, но опять же вы не хотите, чтобы он воскресал себя в производственном коде.

+0

Я изменил код для использования WeakReference с отслеживанием Target после завершения. Обновлен вопрос с полным кодом. Большое спасибо за то, что дал мне эту идею. Все просто, если вы считаете простым :) –

0

Зачем нужен объект, если есть локальная ссылка на него?

+0

Да. Виноват. Я частично ответил на свой вопрос в последнем предложении. Я должен придумать какую-то другую проверку финилера. –

0

Профилировщик памяти является наиболее подходящим способом проверки на утечки.

Я могу порекомендовать .Net Memory Profiler.

Смежные вопросы