2016-06-17 5 views
0

Чтобы упростить задачу, предположим, что у меня есть простое определение класса внутри деструктора внутриСтранное поведение деструктор вызывается дважды, но пропустить выполнение кода в первый раз

public class MyDisposeFinalize 
{ 
    ~MyDisposeFinalize() 
    { 
     var breakPoint = string.Empty; 
    } 
} 

В методе Main я сначала создать экземпляр MyDisposeFinalize класса, затем присвойте null моему экземпляру и вызовите сборку мусора.

static void Main(string[] args) 
    { 
     var myDispose = new MyDisposeFinalize(); 
     myDispose = null; 
     GC.Collect(); 

     System.Console.ReadKey(); 
    } 

Я ожидаю, что мой деструктор будет называться один раз, когда очередь финализации завершается.

В самом деле, когда я ставлю точку останова внутри моего метода деструктора на входе, внутри и выхода у меня есть очень странное поведение:

  1. Точка входа перерыва удар
  2. Затем снова точка входа перерыв хит
  3. Внутри останов ударил
  4. Выход точка останова хит
  5. Затем снова точка входа перерыв ударил

Первая идея заключалась в том, что для выполнения деструкторного метода приходят два разных потока, но тогда почему один из них пропускает код метода и выполняет его только на второй итерации? И вообще, почему дескриптор вызывается дважды?

+0

Что вы подразумеваете под «точкой разрыва внутри моего деструктора при входе»? Я никогда не ставил точку останова перед первым утверждением метода. Помещение точки останова на прототипе метода бессмысленно и зависит от компилятора. – AhmadWabbi

+0

Я думаю, что получаю это сейчас - OP ставит точку останова на фигурные скобки до и после строки кода. – Baldrick

+0

@AhmadWabbi Да, я поставил точку останова на фигурные фигурные скобки, внутри метод и на выходе фигурные скобки. –

ответ

2

Если открыть свой код в ILSpy, вы увидите это:

protected override void Finalize() 
{ 
    try 
    { 
     string breakPoint = string.Empty; 
    } 
    finally 
    { 
     base.Finalize(); 
    } 
} 

Я ожидаю, что разбив на открытых и закрытых скобок, вы видите несколько «строк кода» выполнение, что связаны с линией кода, связанной с открытой и закрывающей фигурными скобками. то есть try и вызов base.Finalize().

В сыром IL, концевая часть кода выглядит следующим образом:

finally 
{ 
    IL_000a: ldarg.0 
    IL_000b: call instance void [mscorlib]System.Object::Finalize() 
    IL_0010: nop 
    IL_0011: endfinally 
} // 

Это делает вызов Object :: финализац(), который может быть там, где ваша точка останова останавливает второй раз.

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

+0

Я не думаю, что это правильная интерпретация - ILSpy регенерирует C# из IL - генерируемый C# функционально эквивалентен, но в остальном не связан с C#, который сгенерировал IL. Кроме того, класс не имеет базы для вызова Finalize, поэтому ILSpy, похоже, неправильно проинформировал код и сгенерировал некомпилирующий C#. – PhillipH

+0

@PhillipH: Возможно, вы правы - я буду копаться и обновлять свой ответ. – Baldrick

+0

@PhillipH: Это действительно в raw IL, вызов Object.Finalize(). – Baldrick