我坚持使用.Net 1.1应用程序(即我现在不能使用2.0中的泛型好东西),我试图优化代码的某些部分。由于它需要释放运行时可调用包装器,因此我最终创建了一个实用程序方法,该方法循环直到所有引用都被释放。该方法的签名是:
void ReleaseObject(object comObject)
释放所有comObject后,我调用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);
此操作失败并显示一条消息:“无法从'输出MyComClass'转换为'输出对象'”
虽然我可以想到为什么它可能是一个问题(即从对象到MyComClass的反向转换不是隐含的,并且无法保证该方法将做什么),我想知道是否有解决方法,或者我需要留下我的数百个空值分配。
注意:我有一堆不同的COM对象类型,这就是为什么我需要一个“对象”参数,而不是一个类型安全的参数。
答案 0 :(得分:3)
为什么调用方法比仅将变量设置为null更好?它们都是单线呼叫,而后者则更简单。
虽然你需要将它们设置为null,但听起来很奇怪。这些静态变量或实例变量的值是否需要在其包含对象之前发布?如果变量只是一个局部变量,无论如何都会超出范围,将其设置为null应该没有任何区别(在发布中)。
RCW没有实现IDisposable吗?如果是这样,调用Dispose(最好通过using语句)将是最好的选择。
(在评论中讨论后。)
这些是局部变量,稍后在方法中不会引用。这意味着垃圾收集器将意识到它们不需要被视为“根”引用 - 因此将它们设置为null应该没有任何区别。
然而,直接回答原始问题:不,你不能通过引用传递变量,除非方法参数的类型完全相同,所以你在这里运气不好。 (使用泛型它是可能的,但你已经说过你只限于.NET 1.1。)
答案 1 :(得分:3)
Sunny,ref和out是编译器的编组提示+合同。 Ref和out是COM日的遗留物 - 通过线路/进程之间发送对象的编组提示。
out
合约
void foo( out MyClass x)
x
设置为某个内容。x
没有值,如果在设置之前尝试使用x
,则会出现编译错误。 (使用未分配的参数x) ref
合约
void foo( ref MyClass x)
最后两点的现实可能会阻止你做你想要做的事情,因为实际上当你明白什么是真正的参考时,它们就毫无意义。如果你想知道这一点,请问Jon Skeet(他写了一本书)。
当编组ref时,它表示除了返回值之外,还要返回ref值。 当编组时,它表示在调用方法时不打扰发送out值,但是记住除了返回值之外还要返回out值。
免责声明免责声明
正如其他人所指出的,正在发生一些可疑的事情。你维护的蛮力代码似乎有一些微妙的错误,并且巧合编码。最好的解决方案可能是添加另一层间接。即包装器类的包装器,它确保确定性清理,您可以只编写一次杂乱的代码,而不是在整个代码库中编写它。
那说..
备选方案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)的调用(使错误代码看起来错误),则会变得可疑。你甚至可以通过代码库来寻找调用Remove的调用。
免责声明 - 上述所有内容都取决于您是否需要手动将引用设置为null,这通常是不必要的。
答案 2 :(得分:0)
您应该致电Marshal.ReleaseComObject
,其中AFAIK在1.1中可用。
你可能的意思是“参考”:
static void ReleaseObject(ref object comObject)
{
if(comObject != null)
{
//do release
comObject = null;
}
}
[编辑注释]但是,这只适用于无类型的对象,所以没有泛型就没用多少!哦,对于C#2.0 ......
重新“参考”;如果变量是真正的变量(意思是:方法变量),那么它们很快就会超出范围并被收集。 “ref”仅对释放字段有用。但说实话,将它们设置为空会更简单......
典型的COM模式是:
SomeType obj = new SomeType();
try {
obj.SomeMethod(); // etc
} finally {
Marshal.ReleaseComObject(obj);
}
答案 3 :(得分:0)
在我看来,你将无法在另一种方法中将这些对象设置为null(顺便说一下,你需要使用 ref 参数而不是 out 来制作它工作,无论如何你会遇到“无法转换...”错误同样的问题。) 我建议创建对象数组,然后迭代遍历该数组,调用ReleaseObject方法并将这些对象设置为null。类似的东西:
List garbagedObjects = new List();
garbagedObjects.Add(myComObject1);
garbagedObjects.Add(myComObject2);
...
foreach(object garbagedObject in garbagedObjects)
{
ReleaseObject(garbagedObject);
garbagedObject = null;
}
garbagedObjects = null;
GC.Collect();
...