Безопасное освобождение ссылки на COM-объект из .NET - PullRequest
6 голосов
/ 02 декабря 2009

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

object target = null;
try {
    // Instantiate and use the target object.
    // Assume we know what we are doing: the contents of this try block
    // do in fact represent the entire desired lifetime of the COM object,
    // and we are releasing all RCWs in reverse order of acquisition.
} finally {
    if(target != null) {
        Marshal.FinalReleaseComObject(target);
        target = null;
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }
}

Однако некоторые люди выступают за сборку мусора до Marshal.FinalReleaseComObject, некоторые после, а некоторые - совсем нет. Действительно ли необходимо вручную собирать каждый RCW, особенно после того, как он уже отсоединен от своего COM-объекта?

На мой взгляд, было бы проще и проще просто отсоединить RCW от COM-объекта и оставить RCW для естественного истечения срока действия:

object target = null;
try {
    // Same content as above.
} finally {
    if(target != null) {
        Marshal.FinalReleaseComObject(target);
    }
}

Достаточно ли этого?

1 Ответ

12 голосов
/ 13 декабря 2009

Чтобы освободить вашу ссылку на целевой COM-объект, достаточно и предпочтительнее , чтобы просто вызвать Marshal.FinalReleaseComObject, а , а не , чтобы вызвать сбор. Другими словами, вы выполнили свою обязанность выпустить свою ссылку, как только закончили с ней. Я не буду касаться вопроса FinalReleaseComObject против ReleaseComObject.

Это оставляет большой вопрос о том, почему люди выступают за звонки GC.Collect() и WaitForPendingFinalizers()?

Поскольку для некоторых конструкций сложно узнать, когда больше нет управляемых ссылок, поэтому вы не можете безопасно вызывать ReleaseComObject. У вас есть два варианта: позвольте памяти нарастить и надеяться, что произойдет сбор или форсировать сбор. [см. примечание Стивена Янсена для голосования в комментариях]

Дополнительным примечанием является то, что установка target в null обычно не требуется и, в частности, не требуется в вашем примере кода. Установка нулевого значения для объектов является обычной практикой для VB6, поскольку он использует сборщик мусора на основе счетчика ссылок. Компилятор для C # достаточно умен (при сборке для выпуска), чтобы знать, что target недоступен после его последнего использования и может быть GC'd, даже до выхода из области видимости. Под последним использованием я имею в виду последнее возможное использование, поэтому в некоторых случаях вы можете установить его на null. Вы можете убедиться в этом сами с помощью кода ниже:

   using System;
   class GCTest
   {
       ~GCTest() { Console.WriteLine("Finalized"); } 
       static void Main()
       {
           Console.WriteLine("hello");
           GCTest x = new GCTest();
           GC.Collect();
           GC.WaitForPendingFinalizers();
           Console.WriteLine("bye");
       }
   }

Если вы создаете релиз (например, CSC GCTest.cs), «Finalized» будет распечатываться между «hello» и «bye». Если вы создаете отладку (например, CSC / debug GCTest.cs), «Finalized» будет печататься после «пока», тогда как установка x в ноль до Collect() «исправит» это.

...