2

我坚持使用 .Net 1.1 应用程序(即我现在不能使用 2.0 中的泛型好东西),并且我试图优化代码的某些部分。由于它处理了很多需要释放的运行时可调用包装器,因此我最终创建了一个实用方法,该方法循环直到所有引用都被释放。该方法的签名是:

void ReleaseObject(object comObject)

释放所有 comObjects 后,我调用 GC.Collect 和 GC.WaitForPendingFinalizers(不要问 - 任何处理 Office 互操作的人都知道)。

而且......像往常一样,我遇到了一个极端情况 - 如果我没有在 GC.Collect 调用之前将相应的托管引用分配给 null ,它就不会正确清理。

所以,我的代码看起来像:

ReleaseObject(myComObject);
myComObject = null;
GC.Collect()
...

因为有一堆xxx=null,所以我决定把它放在util方法中,但是由于引用传递和引用参数传递之间存在差异,显然我不得不将方法更改为:

void ReleaseObject(out object comObject)
{
   //do release
   comObject = null;
}

并将调用者编辑为:

MyComClass myComObject = xxxx;
ReleaseObject(out myComObject);

这失败并显示一条消息:“无法从 'out MyComClass' 转换为 'out object'”

虽然我能想到为什么它可能是一个问题(即从 object 到 MyComClass 的反向转换不是隐式的,并且不能保证该方法会做什么),但我想知道是否有解决方法,或者我需要留下我的数百个空值分配。

注意:我有一堆不同的 COM 对象类型,这就是为什么我需要一个“对象”参数,而不是一个类型安全的参数。

4

4 回答 4

3

为什么调用方法比将变量设置为 null 更好?它们都是单线调用,后者要简单得多。

不过,您首先需要将它们设置为 null 听起来确实很奇怪。这些静态变量,还是实例变量,其值需要在其包含对象之前释放?如果变量只是一个无论如何都会超出范围的局部变量,那么将其设置为 null 应该没有任何区别(在发布中)。

RCW 不实施 IDisposable 吗?如果是这样,调用 Dispose(最好通过 using 语句)将是最好的选择。

(在评论中讨论之后。)

这些是局部变量,稍后在方法中不会引用它们。这意味着垃圾收集器将意识到它们不需要被视为“根”引用 - 因此将它们设置为 null 应该没有任何区别。

但是,要直接回答原始问题:不,除非方法参数的类型完全相同,否则您不能通过引用传递变量,因此您在这里不走运。(使用泛型是可能的,但您说过您仅限于 .NET 1.1。)

于 2008-10-28T20:54:29.840 回答
3

Sunny、ref 和 out 是编译器的编组提示 + 合同。Ref 和 out 是 COM 日的遗留物——当通过线路/进程之间发送时对象的编组提示。

out合同_

void foo( out MyClass x)
  1. foo() 将x在返回之前设置为某个值。
  2. x输入 foo() 时没有值,如果x在设置之前尝试使用,则会出现编译器错误。(使用未分配的输出参数 x)

ref合同_

void foo( ref MyClass x)
  1. ref 允许更改调用者引用。
  2. x 必须是可分配的
    • 您不能将某些东西转换为中间变量 foo( ref (object) something)
    • x 不能是属性

最后两点的现实可能会阻止您做您想做的事情,因为实际上当您了解参考的真正含义时,它们毫无意义。如果您想知道这一点,请询问 Jon Skeet(他写了一本书)。

在编组 ref 时,它表示除了返回值之外,还要带回 ref 值。编组时,它说在调用方法时不要费心发送输出值,但请记住除了返回值之外还要带回输出值。


免责声明 免责声明

正如其他人指出的那样,正在发生一些可疑的事情。您维护的蛮力代码似乎有一些微妙的错误,并且由于巧合而受到编码的影响。最好的解决方案可能是添加另一层间接。即包装器类的包装器,可确保确定性清理,您可以在其中编写一次且仅一次的凌乱代码,而不是将其散布在整个代码库中。


那说..

备选方案 1

除非您为将调用它的每种类型的 (com) 对象提供重载,否则 Ref 不会起作用。

// need a remove method for each type. 
void Remove( ref Com1 x ) { ...; x = null; }
void Remove( ref Con2 x ) { ...; x = null; }
void Remove( ref Com3 x ) { ...; x = null; }

// a generics version using ref.
void RemoveComRef<ComT>(ref ComT t) where ComT : class
{
    System.Runtime.InteropServices.Marshal.ReleaseComObject(t);
    t = null; 
}

Com1 c1 = new Com1();
Com2 c2 = new Com2();
Remove( ref c1 );
RemoveComRef(ref c2); // the generics version again.

备选方案 2

如果您不想这样做,请从 Remove() 方法返回 null 并转换回对象的类型。

class Remover
{
    // .net 1.1 must cast if assigning
    public static object Remove(object x)
    {
        System.Runtime.InteropServices.Marshal.ReleaseComObject(x);
        return null;
    }

    // uses generics.
    public static ComT RemoveCom<ComT>(ComT t) where ComT : class
    {
        System.Runtime.InteropServices.Marshal.ReleaseComObject(t);
        return null;
    }   
}

Com1 c1 = new Com1();
Com2 c2 = new Com2();
c1 = (Com1)Remover.Remove(c1); // no reliance on generics
c2 = Remover.RemoveCom(c2); // relies on generics

* 我添加了通用版本进行比较。

上面的代码的效果是,在查看代码时,当您看到对 Remove(x) 的调用而没有赋值时会变得可疑(使错误的代码看起来是错误的)。您甚至可以通过代码库 Grep 查找未进行分配的 Remove 调用。


免责声明-以上所有内容都基于您需要手动将引用设置为 null ,这(通常)不是必需的。

于 2008-10-29T00:18:52.640 回答
0

你应该打电话Marshal.ReleaseComObject,AFAIK 在 1.1 中可用。

您可能的意思是“参考”:

static void ReleaseObject(ref object comObject)
{
   if(comObject != null)
   {
     //do release
     comObject = null;
   }
}

[编辑重新评论] 但是,这仅适用于无类型对象,因此没有泛型就没有多大用处!哦,对于 C# 2.0...

重新“参考”;如果变量是真正的变量(意思是:方法变量),那么它们很快就会超出范围并被收集。“ref”仅对释放字段有用。但老实说,将它们设置为 null 会更简单......

典型的 COM 模式是:

SomeType obj = new SomeType();
try {
  obj.SomeMethod(); // etc
} finally {
  Marshal.ReleaseComObject(obj);
}
于 2008-10-28T21:08:12.277 回答
0

在我看来,您将无法在另一种方法中将这些对象设置为 null (顺便说一句,您需要使用ref参数而不是out来使其工作,无论如何您会遇到“无法转换...”的相同问题错误。)我建议创建对象数组,然后遍历该数组,调用 ReleaseObject 方法并将这些对象设置为空。就像是:

List garbagedObjects = new List();
garbagedObjects.Add(myComObject1);
garbagedObjects.Add(myComObject2);
...
foreach(object garbagedObject in garbagedObjects)
{
  ReleaseObject(garbagedObject);
  garbagedObject = null;
}
garbagedObjects = null;
GC.Collect();
...
于 2008-10-28T21:12:26.730 回答