我的C#程序使用具有大量各种接口和子对象的COM组件。问题是每次我检索一些COM接口时都会创建RCW并且RCW存在未知时间(直到GC收集)。每个RCW都在COM服务器中保存一些对象。
COM组件是一个out-proc服务器,因此它的对象驻留在一个单独的非常重的进程中,该进程在释放驻留在其中的所有对象之前不会终止。我希望所有对象尽快发布,以便我确切地知道,一旦我发布了最后一个对象,out-proc服务器进程就会终止并且不再消耗系统资源。这不是我的偏执 - 有几个重量级进程消耗系统资源,特别是内存,这对性能来说真的很糟糕。
到目前为止,我制作了一个实现IDisposable
的泛型类,接受对象的引用(事实上是RCW)并在Dispose
实现中调用Marshal.FinalReleaseComObject
。
为了这个问题,让我们暂时忽略因使用FinalReleaseComObject()
而引起的问题 - 我知道它们并且可以容忍它们。
真正的问题是我被迫对所有对象使用using
或写finally
个子句并在那里调用Dispose()
。它有效,但是代码变得混乱,带来了许多额外的布线,这让我很烦恼。
例如,我需要为someObject.Params.ColorParams.Hint
属性分配一个字符串 - 我不能只写
someObject.Params.ColorParams.Hint = whatever;
因为每个访问者都会有一个我需要释放的COM对象,所以我改为:
using( MyWrapper<IParams> params = new MyWrapper<IParams>( someObject.Params ) ) {
using( MyWrapper<IColorParams> colorParams =
new MyWrapper<IColorParams>( params.Controlled ) )
{
colorParams.Controlled.Hint = whatever;
}
}
这是最简单的例子 - 有时我需要访问五个级别的内容,然后我写一组五级深度的using
语句。
这个问题有更优雅的解决方案吗?
答案 0 :(得分:1)
请参阅Hans Passant的this answer to Clean up Excel Interop Objects with IDisposable。
总之,您根本不需要IDisposable
。只是在所有COM引用超出范围之后显式调用垃圾收集 - 即不是来自使用它们的函数,而是来自该函数的调用者。
答案 1 :(得分:0)
我不确定我是否完全理解这个问题,但如果我这样做(代码混乱),有两种可能的解决方案:
包装器方法 - 您可以定义一个简单的方法:
TResult Exec<TResult>(Func<TResult> func, params MarshalByRefObject comObjects)
{
TResult res = default(TResult);
try
{
res = func.Invoke();
}
catch (Exception e) { /* Log? */ }
finally
{
foreach (MarshalByRefObject combObj in comObjects)
{ /* release resources using common interface or reflection */ }
}
}
非常类似于之前的方法,只使用众多AOP框架中的一个(例如PostSharp)
答案 2 :(得分:0)
c#和资源的确定性释放通常是最麻烦的 释放进程外COM对象就是问题的一个例子。
如果您乐意编写一些C ++ / cli代码,它至少可以选择托管堆对象的堆栈语义。当对象超出范围时,这就避免了编译器调用dispose时使用或最终包装的需要。
当然它确实为您带来了比c#,头文件更嘈杂的语言。, - &gt;,:: etc。
作为一项规则,如果您的代码能够在使用块内的本地范围内创建和释放COM对象,我将留在C#中并忍受使用麻烦。
如果您的代码需要将COM对象保留为成员变量,我会将这些类移动到c ++ / cli,并利用成员变量的堆栈语义来自动为您调度处理调用。