使用“ref” 和/或“出”。 对于对象类型
我被困在 .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);
这失败并显示一条消息:“无法从‘out MyComClass’转换为‘out object’”
虽然我可以想到为什么它可能是一个问题(即从对象到MyComClass的反向转换)不是隐式的,并且不能保证该方法会做什么),我想知道是否有解决方法,或者我需要保留数百个空值分配。
注意:我有一堆不同的 COM 对象类型,这就是为什么我需要一个“对象”参数,而不是类型安全的参数。
I'm stuck with .Net 1.1 application (i.e. I can not use the generics goodies from 2.0 for now), and I was trying to optimize some parts of the code. As it deals a lot with runtime callable wrappers, which need to be released, I ended up to create a utility method which loops until all references are released. The signature of the method is:
void ReleaseObject(object comObject)
After releasing all comObjects, I call GC.Collect and GC.WaitForPendingFinalizers (don't ask - anybody dealing with Office interop knows).
And ... as usual, I hit a corner case - if I do not assign the corresponding managed reference to null before the GC.Collect call, it does not cleanup properly.
So, my code looks like:
ReleaseObject(myComObject);
myComObject = null;
GC.Collect()
...
As, there are a bunch of xxx=null, I decided to put this in the util method, but as there is a difference between passing by reference, and passing a reference parameter, obviously I had to change the method to:
void ReleaseObject(out object comObject)
{
//do release
comObject = null;
}
and edit the caller to:
MyComClass myComObject = xxxx;
ReleaseObject(out myComObject);
This fails with a message: "Cannot convert from 'out MyComClass' to 'out object'"
While I can think of why it can be a problem (i.e. the reverse cast from object to MyComClass is not implicit, and there is no guarantee what the method will do), I was wondering if there is a workaround, or I need to stay with my hundreds assignments of nulls.
Note: I have a bunch of different COM objects types, thats why I need a "object" parameter, and not a type safe one.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您应该调用
Marshal.ReleaseComObject
,AFAIK 在 1.1 中可用。您可能的意思是“ref”:
[编辑评论]但是,这仅适用于无类型对象,因此没有泛型就没有多大用处! 哦,对于 C# 2.0...
重新“ref”; 如果变量是真正的变量(即:方法变量),那么它们很快就会超出范围并被收集。 “ref”仅对释放字段有用。 但说实话,将它们设置为 null 会更简单......
典型的 COM 模式是:
You should be calling
Marshal.ReleaseComObject
, which AFAIK was available in 1.1.You probably mean "ref":
[edit re comments] however, this will only work for untyped objects, so not much use without generics! Oh for C# 2.0...
Re the "ref"; if the variables are truly variables (meaning: method variables), then they will go out of scope shortly and get collected. The "ref" would only be useful to release fields. But to be honest, it would be simpler just to set them to null...
A typical COM pattern is:
为什么调用方法比将变量设置为 null 更好? 它们都是单线调用,后者要简单得多。
不过,您首先需要将它们设置为空,这听起来确实很奇怪。 这些是静态变量还是实例变量,其值需要比其包含对象更早释放? 如果变量只是一个局部变量,无论如何都会超出范围,那么将其设置为 null 应该不会有任何区别(在发布中)。
RCW 没有实现 IDisposable 吗? 如果是这样,调用 Dispose (最好通过 using 语句)将是最好的选择。
(在评论中讨论之后。)
这些是局部变量,稍后在方法中不会引用。 这意味着垃圾收集器将意识到它们不需要被视为“根”引用 - 因此将它们设置为 null 应该不会有任何区别。
然而,要直接回答最初的问题:不,除非方法参数具有完全相同的类型,否则您不能通过引用传递变量,所以您在这里运气不佳。 (使用泛型这是可能的,但你说过你仅限于 .NET 1.1。)
Why is it better to call a method than to just set the variable to null? They're both single line calls, and the latter is a lot simpler.
It does sound very odd that you need to set them to null in the first place though. Are these static variables, or instance variables whose values need to be released earlier than their containing object? If the variable is just a local variable which will go out of scope anyway, setting it to null shouldn't make any difference (in release).
Do the RCWs not implement IDisposable? If so, calling Dispose (preferably via a using statement) would be the best bet.
(After discussions in comments.)
These are local variables, which aren't referenced later in the method. That means the garbage collector will realise that they don't need to be treated as "root" references - so setting them to null shouldn't make any difference.
To answer the original question directly, however: no, you can't pass a variable by reference unless the method parameter is of exactly the same type, so you're out of luck here. (With generics it would be possible, but you've said you're limited to .NET 1.1.)
Sunny、ref 和 out 是编译器的编组提示和契约。 Ref 和 out 是 COM 时代的延续——通过线路/在进程之间发送对象时的编组提示。
out
合约x
设置为某个值。x
没有值,如果在设置它之前尝试使用x
,则会出现编译器错误。 (使用未分配的输出参数 x)ref
合约最后两点的现实可能会阻止你做你想做的事情,因为实际上当你了解参考文献的真正含义时,它们就没有意义了。 如果你想知道这一点,请询问乔恩·斯基特(Jon Skeet)(他写了一本书)。
当编组 ref 时,它表示除了返回值之外,还返回 ref 值。
当编组出时,它说在调用方法时不要费心发送出值,但要记住除了返回值之外还要带回出值。
免责声明 免责声明 免责声明
正如其他人指出的那样,有些可疑的事情正在发生。 看来您正在维护的暴力代码存在一些微妙的错误,并且巧合地遭受了编码的困扰。 最好的解决方案可能是添加另一层间接层。 即包装器类的包装器,可确保确定性清理,您可以一次且仅编写一次混乱的代码,而不是将其散布在整个代码库中。
这就是说..
替代方案 1
Ref 不会成功,除非您为调用它的每种类型的 (com) 对象提供重载。
替代方案 2
如果您不想这样做,请从 Remove() 方法返回 null 并强制转换回对象类型。
* 我添加了通用版本进行比较。
上面的代码的效果是,当您查看代码时,如果您看到对 Remove(x) 的调用但没有赋值(使错误的代码看起来是错误的),您就会产生怀疑。 您甚至可以在代码库中使用 Grep 查找未发生赋值的对 Remove 的调用。
免责声明 - 以上所有内容都基于您需要手动将引用设置为 null,这(通常)是没有必要的。
Sunny, ref and out are a marshalling hints + contract to the compiler. Ref and out are a carryover to COM days - the marshalling hints for objects when sent over the wire / between processes.
The
out
contractx
to something before it returns.x
has no value when foo() is entered, and you get a compiler error if you attempt to usex
before setting it. (use of unassigned out parameter x)The
ref
contractThe reality of the last two points are likely to stop you doing what you're trying to do, because in effect they make no sense when you understand what references really are. If you want to know that, ask Jon Skeet (he wrote a book).
When marshalling ref, it says that in addition to the return value, bring back ref values as well.
When marshalling out, it says don't bother sending the out value when the method is called, but remember to bring back the out value in addition to the return value.
DISCLAIMER DISCLAIMER DISCLAIMER
As others point out, something fishy is going on. It appears the brute-force code you are maintaining has some subtle bugs and suffers from coding by coincidence. The best solution is probably to add another layer of indirection. i.e. A wrapper to the wrapper class that ensures deterministic cleanup where you can write the messy code once and only once instead of peppering it throughout your codebase.
That said ..
Alternative 1
Ref won't do the trick unless you provide overloads for every type of (com) object you will call it with.
Alternative 2
If you don't want to do that, return null from the Remove() method and cast back to the type of object.
* I added generic versions for comparison.
The above code has the effect that when looking at code you become suspicious when you see a call to Remove(x) without the assignment (making wrong code look wrong). You could even Grep through the codebase looking for calls to Remove where assignment doesn't take place.
DISCLAIMER - all the above is predicated on your needing to set the reference to null manually, which (normally) isn't necessary.
在我看来,您将无法在另一种方法中将这些对象设置为 null(顺便说一句,您需要使用 ref 参数而不是 out 才能使其正常工作您会遇到同样的问题,并出现“无法转换...”错误。)
我建议创建对象数组,然后迭代该数组,调用 ReleaseObject 方法并将这些对象设置为 null。 就像是:
In my opinion you won't be able to set those objects to null in another method (BTW you would need to use ref parameter instead of out to make it working, anyway you would hit the same problem with "Cannot convert..." error.)
I'd recommend to create and array of objects and than iterate through that array, calling the ReleaseObject method and setting those objects to null. Something like: