12

我们有一个第三方库,它在内部将 SafeHandle 用于非托管资源。在某些错误情况下,有必要处理对象并重新创建它。但是,在 dispose 实现中存在一个错误,该错误会阻止 Handle 在这些情况的子集中关闭。这会阻止新对象在其终结器运行之前成功创建。

已经提出了两种解决方案(都是邪恶的)来解决这个问题,直到我们可以修复第三方代码:

  1. 运行 aGC.Collect让终结器运行并清理对象

  2. 如果 dispose 未能这样做,请使用反射获取 Handle 并关闭它

其中哪一个不那么邪恶,为什么?有没有其他我们没有考虑过的方法比这两种方法更邪恶?

4

5 回答 5

14

我赞成私人反思。这是一个本地化的错误,所以解决方案也应该是本地的。你的代码打算做什么更清楚了。并且您可能可以添加一些测试,以便在修复错误后注意到。因此,一旦不再需要该 hack,就可以轻松删除它。

...
thirdPartyObject.Dispose();
ThirdPartyDisposeBugWorkaround(thirdPartyObject);
...

void ThirdPartyDisposeBugWorkaround(ThirdPartyClass thirdPartyObject)
{
   //Do private reflection here
}

另一方面,强制 GC 具有全局影响。干扰 GC 的原因有很多(其中大部分是不好的)。您的代码的作用不太明显。因此,即使修复了错误,也可能会保留呼叫。

旧新事物:不要使用全局状态来管理本地问题

于 2011-05-21T14:49:23.573 回答
5

我会进行反思,但请确保您对它进行了错误处理,从而明确指出出了什么问题,请记住该错误可能要到几年后才会触发,并且您的开发团队可能已经翻身,没有人记得这个古怪的黑客.

try
{
   .. hacky reflection ..
}
catch(Exception ex)
{
    throw new Exception("Reflection on private field 'Xyz' of 3rd Party Component 'Abc' failed.  Was 'Abc' updated? Reflection is used due to bug in 'Dispose' implementation.", ex);
}
于 2011-05-21T14:50:31.250 回答
3

首先选择一个有效的。如果他们都工作,则选择对系统影响最小的那个。Gc.Collect 在整个应用程序中更像是一把锤子。由于您的反射代码很脆弱,但影响应该很小。

于 2011-05-21T15:03:07.240 回答
2

如果它没有被标记为密封,你可以继承它并实现你自己的处置。至于反射与 GC,我肯定会使用反射。正如其他人所说,GC 可能无法按预期工作。它可以进行集合迭代,但实际上不会释放您的句柄。

我想指出:如果其他东西仍然引用此 SafeHandle,并且您释放它,您可以轻松地将其他错误引入您的系统。

于 2011-05-21T14:54:33.140 回答
0

提出“CodeInChaos”论点,为什么不要求在特定代进行收集。

GC.GetGeneration(Object obj)将返回该对象所在的代 和GC.Collect(Int32 gen)

前任:

Int32 generation = GC.GetGeneration(theObject);
theObject = null;
GC.Collect(generation);
GC.WaitForPendingFinalizers();
GC.Collect(generation); // this is req because first collect puts thisObject on freachable queue not // garbaged yet.
于 2011-05-21T15:07:41.623 回答