2009-12-02 2 views
6

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

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, некоторые после, и некоторые из них не совсем. Действительно ли необходимо GC каждый RCW вручную, особенно после того, как он уже отсоединен от своего объекта COM?

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

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

Достаточно ли сделать это?

ответ

12

Чтобы иметь вашу ссылку на целевой объект СОМ выпущен, достаточно и предпочел просто позвонить 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), «Завершена» напечатает между «привет» и «до свидания». Если вы создадите отладку (например, CSC/debug GCTest.cs), «Finalized» будет распечатываться после «bye», тогда как установка x на null до Collect() будет «исправлена».

+0

Отличный и подробный анализ, спасибо Тони! –

+0

Downvote: эти люди все рекомендуют обратное в пользу 'GC.Collect': [Microsoft Distinguished Engineer] (http://blogs.msdn.com/b/cbrumme/archive/2003/04/16/51355. aspx), [команда Visual Studio] (http://blogs.msdn.com/b/visualstudio/archive/2010/03/01/marshal-releasecomobject-considered-dangerous.aspx) и [высокопоставленный член SO ] (http://stackoverflow.com/a/17131389/1995977) –

+0

@Steve: Спасибо за ссылки. Я обновил ответ, чтобы включить ссылку на ваши оговорки. –