2017-02-01 3 views
-2

У меня есть модульные тесты, в которых я устанавливаю свойство на ресурсе в деструкторе класса как true, но он бросает нулевую ссылку excpetion. ресурс предоставляется классу и все еще имеет ссылку на вызывающего.Destructor throws System.NullReferenceException

Тесты модуля проходят, но в консоли отображается исключение NullReferenceException, а номер строки указывает на строку в деструкторе.

class Resource 
{ 
    public bool Release { get; set; } = false; 
} 

Класс быть тест

class Foo 
{ 
    public Foo() 
    { 
    } 

    ~Foo() 
    { 
     Resource.Release = true; // error points to this line 
    } 

    public Resource Resource { get; set; } 
} 

Unit Test

[TestMethod] 
    public void TestRelease() 
    { 
     Foo foo = new Foo(); 
     Resource resource = new Resource(); 
     foo.Resource = resource; 

     Assert.IsTrue(resource.Release == false, "Expecting resource.Release == false"); 

     foo = null; 
     System.GC.Collect(); 
     System.GC.WaitForPendingFinalizers(); 

     Assert.IsNotNull(resource, "Expecting resource is not null"); 
     Assert.IsTrue(resource.Release == true, " Expecting resource.Release == true"); 

    } 

Ошибка

System.NullReferenceException: Object reference not set to an instance of an object. 
at Foo.Finalize() 

Редактировать - это исправляет проблему, но не знает почему?

Интересно, что я добавил эту строку, и теперь больше нет ошибок. else никогда не называется вызываемым значением. destructor/finalize вызывается только один раз, но вопрос все еще остается там, почему он в первую очередь ошибся, если?

~Foo() 
    { 
     if(Resource != null) 
     { 
      Console.Write("releasing"); 
      resource.Release = true; 
     } else 
     { 
      Console.Write("this never gets executed"); 
     } 

    } 
+1

Они называются ** Финализаторы **, не деструкторы. Финализаторы должны использоваться с неуправляемыми ресурсами. Ни 'Resource', ни' Foo', кажется, неуправляемы. Финализаторы являются неопределенными и, возможно, являются причиной вашей проблемы. – pinkfloydx33

+0

жаль, что я новичок в этом, какая разница между управляемыми и неуправляемыми ресурсами, а также что я могу сделать, чтобы исправить код? – user2727195

+1

@ pinkfloydx33 - Я тоже предпочитаю термин финализатор, но, к сожалению, [C# Programming Guide] (https://msdn.microsoft.com/en-us/library/66x5fx1b.aspx) не согласен с терминологией. –

ответ

1

Вот ваш Foo класс переписана с IDisposable рисунком:

class Foo : IDisposable 
{ 
    public Foo() 
    { 
    } 

    public void Dispose() 
    { 
     Resource.Release = true; 
    } 

    public Resource Resource { get; set; } 
} 
+0

Должен ли я вызывать 'Dispose()' вручную или он автоматически вызывается? – user2727195

+0

@ user2727195 Вам нужно вызвать его вручную, либо явно, вызвав 'fooObj.Dispose()' или неявно с помощью блока 'using'. (Вы также можете называть его в финализаторе Foo, но, как я уже говорил, это не то, что вы можете надежно зависеть от того, чтобы вызвать вызов в «правильное время».) – Abion47

+1

Вы вызываете его вручную через 'Dispose()' или используя переменную в блоке 'using (...) {...}'. Но обратите внимание, что когда-то из области использования блока вы не имеете доступа к самой переменной или к свойству 'Resource'. Поэтому, если вам нужно получить к нему доступ, вам придется вручную управлять «Dispose». Исходя из этого и использования, которое вы описываете в комментариях, я не уверен, что этот шаблон поможет вам в том, что вы делаете, без кучи дополнительной полезности. – pinkfloydx33