2015-11-16 5 views
2

Основанный на this post, я создал мост между компонентами для форм Windows и IDisposable объектов. Это в значительной степени выглядит следующим образом:Почему Dispose вызывается, если мой объект никогда не был создан?

namespace MyApp 
{ 
    public class Disposer: Component 
    { 
     private readonly Action<bool> _dispose; 

     public Disposer(Action<bool> disposeCallback) 
     { 
      if (disposeCallback == null) 
       throw new ArgumentNullException(nameof(disposeCallback)); 

      this._dispose = disposeCallback; 
     } 

     protected override void Dispose(bool disposing) 
     { 
      this._dispose(disposing); 
      base.Dispose(disposing); 
     } 
    } 
} 

Пока все хорошо. Затем я создал модульные тесты, в том числе один для конкретной проверки в аргументе конструктора.

[TestMethod] 
[ExpectedException(typeof(ArgumentNullException))] 
public void Disposer_ShouldNotAllowNullActions() 
{ 
    new Disposer(null); 
} 

Вот улов: не только мой тест терпит неудачу, но он фактически прерван. Сбой самой тестовой платформы (тестовый бегун ReSharper). Покопавшись в моем средстве просмотра событий Windows, я мог видеть, что метод Dispose() вызывается, и поскольку this._dispose по существу является нулевым на данный момент, он терпит неудачу с .

Я установил это с предоставлением пустой лямбды в качестве значения по умолчанию.

Но если конструктор выбрасывает исключение (что я его подтвердил), почему вообще вызван метод Dispose?

+0

Что делать, если конструктор уже приобрел некоторые доступные ресурсы до того, как будет выброшено исключение? Эти ресурсы по-прежнему необходимо освободить. –

+0

В чем заключается цель получения делегата, вызываемого при удалении объекта? Какой прецедент? –

+0

@DStanley IDisposable может быть создан в форме, но вы захотите избавиться от них, когда форма фактически удалена (не закрыта). Dispose уже переопределяется в сгенерированном виде кодом, поэтому способ подключиться к нему - это зарегистрировать компонент. (Мне не понравилось перемещение сгенерированного кода.) – Alpha

ответ

1

Почему метод Dispose называется?

Финализатор для класса вызывается, даже если конструктор генерирует исключение. В finalizer for Component звонки Dispose():

~Component() { 
     Dispose(false); 
    } 

Поскольку вы переопределить Dispose(bool), переопределения вызывается, если конструктор выбрасывает исключение. Поскольку это реальная возможность в вашем коде, я бы посоветовал убедиться, что оба и this._dispose не являются нулевыми.

6

Класс Component должен иметь финализатор, который вызывает this.Dispose(), что вызывает отмену переопределения.

Финализаторы запускаются, даже если конструктор не завершил - это позволяет выделить все ресурсы, которые были выделены до того, как конструктор не был очищен.

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