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:
我坚持使用.Net 1.1应用程序(即我现在不能使用2.0中的泛型好东西),我试图优化代码的某些部分。因为它需要释放运行时可调用包装器,所以我最终创建了一个实用程序方法,它循环直到所有引用都被释放。该方法的签名是:
void ReleaseObject(object comObject)
After releasing all comObjects, I call GC.Collect and GC.WaitForPendingFinalizers (don't ask - anybody dealing with Office interop knows).
释放所有comObject后,我调用GC.Collect和GC.WaitForPendingFinalizers(不要问 - 任何处理Office互操作的人都知道)。
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.
并且...像往常一样,我遇到了一个极端情况 - 如果我在GC.Collect调用之前没有将相应的托管引用分配给null,则它不能正确清理。
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:
因为,有一堆xxx = null,我决定将它放在util方法中,但由于传递引用和传递引用参数之间存在差异,显然我不得不将方法更改为:
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'"
这失败并显示一条消息:“无法从'输出MyComClass'转换为'输出对象'”
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.
虽然我可以想到为什么它可能是一个问题(即从对象到MyComClass的反向转换不是隐含的,并且不能保证该方法将做什么),我想知道是否有解决方法,或者我需要留下我的数百个空值分配。
Note: I have a bunch of different COM objects types, thats why I need a "object" parameter, and not a type safe one.
注意:我有一堆不同的COM对象类型,这就是为什么我需要一个“对象”参数,而不是一个类型安全的参数。
2
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.
为什么调用方法比仅将变量设置为null更好?它们都是单线呼叫,后者更简单。
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).
虽然你需要将它们设置为null,但听起来很奇怪。这些静态变量或实例变量的值是否需要比其包含对象更早发布?如果变量只是一个局部变量,无论如何都会超出范围,将其设置为null不应该有任何区别(在发布中)。
Do the RCWs not implement IDisposable? If so, calling Dispose (preferably via a using statement) would be the best bet.
RCW不实现IDisposable吗?如果是这样,调用Dispose(最好通过using语句)将是最好的选择。
(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.
这些是局部变量,稍后在方法中不会引用。这意味着垃圾收集器将意识到它们不需要被视为“根”引用 - 因此将它们设置为null应该没有任何区别。
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.)
但是,要直接回答原始问题:不,你不能通过引用传递变量,除非方法参数的类型完全相同,所以你在这里运气不好。 (使用泛型它是可能的,但你已经说过你只限于.NET 1.1。)
3
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.
Sunny,ref和out是编译器的编组提示+合同。 Ref和out是COM日的遗留物 - 通过线路/进程之间发送对象的编组提示。
The out
contract
出合同
void foo( out MyClass x)
x
to something before it returns.foo()会在返回之前将x设置为某个东西。
x
has no value when foo() is entered, and you get a compiler error if you attempt to use x
before setting it. (use of unassigned out parameter x)输入foo()时x没有值,如果在设置之前尝试使用x,则会出现编译器错误。 (使用未分配的参数x)
The ref
contract
裁决合同
void foo( ref MyClass x)
ref允许更改调用者引用。
你不能把东西投射到中间变量foo(ref(object)something)
x不能是属性
x必须是可赋值的你不能把东西转换成中间变量foo(ref(object)something)x不能是属性
The 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).
最后两点的现实可能会阻止你做你想做的事情,因为实际上当你明白什么是真正的引用时它们没有任何意义。如果你想知道,请问Jon Skeet(他写了一本书)。
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.
编组ref时,它表示除了返回值之外,还要返回ref值。当编组时,它表示在调用方法时不打扰发送out值,但是记住除了返回值之外还要返回out值。
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.
除非您为每个类型的(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(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.
Alternative 2
If you don't want to do that, return null from the Remove() method and cast back to the type of object.
如果您不想这样做,请从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 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
* 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.
上面的代码会产生这样的效果:在查看代码时,如果看到调用Remove(x)而没有赋值(使错误的代码看起来错误),则会变得可疑。您甚至可以通过代码库Grep查找在不进行分配的情况下调用Remove。
DISCLAIMER - all the above is predicated on your needing to set the reference to null manually, which (normally) isn't necessary.
免责声明 - 以上所有内容都取决于您是否需要手动将引用设置为null,这通常是不必要的。
0
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:
在我看来,你将无法在另一种方法中将这些对象设置为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();
...
0
You should be calling Marshal.ReleaseComObject
, which AFAIK was available in 1.1.
您应该调用Marshal.ReleaseComObject,其中AFAIK在1.1中可用。
You probably mean "ref":
你可能的意思是“参考”:
static void ReleaseObject(ref object comObject)
{
if(comObject != null)
{
//do release
comObject = null;
}
}
[edit re comments] however, this will only work for untyped objects, so not much use without generics! Oh for C# 2.0...
[编辑注释]但是,这只适用于无类型的对象,所以没有泛型就没用多少!哦,对于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...
重新“参考”;如果变量是真正的变量(意思是:方法变量),那么它们很快就会超出范围并被收集。 “ref”仅对释放字段有用。但说实话,将它们设置为null会更简单...
A typical COM pattern is:
典型的COM模式是:
SomeType obj = new SomeType();
try {
obj.SomeMethod(); // etc
} finally {
Marshal.ReleaseComObject(obj);
}