c#:如何处理来自第 3 方库的终结器异常?
终结器总是由.net框架调用,因此顺序可能会失控;即使构造函数失败,析构函数仍然可以被触发。
当此类终结器异常来自第三方库时,这可能会带来麻烦:我找不到处理它们的方法!
例如,在下面的代码中,虽然类 A 的构造函数总是抛出异常并失败,但 A 的终结器将由 .net 框架触发,并且由于 A 具有 B 类型的属性而调用 ~B() 。
class Program // my code
{
static void Main(string[] args)
{
A objA;
try
{
objA = new A();
}
catch (Exception)
{
}
; // when A() throws an exception, objA is null
GC.Collect(); // however, this can force ~A() and ~B() to be called.
Console.ReadLine();
}
}
public class A // 3rd-party code
{
public B objB;
public A()
{
objB = new B(); // this will lead ~B() to be called.
throw new Exception("Exception in A()");
}
~A() // called by .net framework
{
throw new Exception("Exception in ~A()"); // bad coding but I can't modify
}
}
public class B // 3rd-party code
{
public B() { }
~B() // called by .net framework
{
throw new Exception("Exception in ~B()"); // bad coding but I can't modify
}
}
如果这些是我的代码,那就更容易了 - 我可以在终结器中使用 try-catch,至少我可以做一些日志记录 - 我可以允许异常使程序崩溃,以便尽快发现错误 - 或者如果我想“容忍”异常,我可以使用 try-catch 来抑制异常,并优雅退出。
但如果 A 和 B 是来自第三方库的类,我就无能为力了! 我无法控制异常的发生,我无法捕获它们,所以我无法记录它或抑制它!
我能做些什么?
Finalizers are always called by .net framework, so the sequence could be out of control; and even if the constructor failed, the destructor still can be triggered.
This could bring troubles, when such finalizer exceptions come from a 3rd-party library: I can't find a way to handle them!
For example, in the code below, although class A's constructor always throw an exception and fail, finalizer of A will be triggered by the .net framework, also ~B() is called as A has a property of B type.
class Program // my code
{
static void Main(string[] args)
{
A objA;
try
{
objA = new A();
}
catch (Exception)
{
}
; // when A() throws an exception, objA is null
GC.Collect(); // however, this can force ~A() and ~B() to be called.
Console.ReadLine();
}
}
public class A // 3rd-party code
{
public B objB;
public A()
{
objB = new B(); // this will lead ~B() to be called.
throw new Exception("Exception in A()");
}
~A() // called by .net framework
{
throw new Exception("Exception in ~A()"); // bad coding but I can't modify
}
}
public class B // 3rd-party code
{
public B() { }
~B() // called by .net framework
{
throw new Exception("Exception in ~B()"); // bad coding but I can't modify
}
}
If these are my code, it is a bit easier
- I can use try-catch in finalizers, at least I can do some logging
- I can allow the exception to crash the program, to discover the error asap
- or if I want to "tolerate" the exception, I can have a try-catch to suppress the exception, and have a graceful exit.
But if A and B are classes from a 3rd-party library, I can do nothing!
I can't control the exception to happen, I can't catch them, so I can't log it or suppress it!
What can I do?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
听起来第 3 方实用程序写得不好。 :)
您是否尝试过使用 AppDomain.UnhandledException 捕获它?
Sounds like the 3rd party utility is poorly written. :)
Have you tried catching it using AppDomain.UnhandledException?
您可能需要考虑为您的应用程序使用全局异常处理程序。您没有指出您是否使用 ASP.NET、WinForm、MVC 等,但这是一个控制台应用程序:
.NET 控制台应用程序中的全局异常处理程序
在 ASP.NET 中,您可以使用 Global.asax 文件来捕获未处理的异常。
如果您总是在应用程序中调用 GC.Collect(),您也可以尝试将其包装在 try-catch 块中。
只是一些需要考虑的想法。
You might want to consider a global exception handler for your application. You didn't indicate if your doing ASP.NET, WinForm, MVC, etc, but here's one for a console application:
.NET Global exception handler in console application
In ASP.NET, you can use the Global.asax file to catch unhandled exceptions.
If you are always calling GC.Collect() in your application, you might try wrapping that in a try-catch block as well.
Just some ideas to consider.
您可以使用 GC.SuppressFinalizer(objA) 和 < a href="http://msdn.microsoft.com/en-us/library/system.gc.keepalive%28v=VS.100%29.aspx" rel="nofollow">GC.KeepAlive(objA) 这将阻止垃圾收集器调用该对象的 Finalize,因此当使用
KeepAlive
时,objB
不会最终确定,因为objA
“它还活着”仍然有它的引用。但是,如果您忘记以正确的方式完成或处置 objA,则应该注意内存泄漏。但是假设 objA 在某个方法中的某个时刻初始化了另一个 objectB 并且没有正确处理它,那么不幸的是我不认为你可以做任何事情关于它。
您可以尝试的另一件事是检查当您处于发布模式而不是调试模式时该库的行为是否有所不同;例如,如果在调试模式下调用终结器,它们可能只会在终结器处抛出异常“这对开发人员来说是一种帮助,因此如果他们没有以正确的方式调用处置或终结对象,则在调试时将抛出异常”:
如果这不是如果是这种情况,那么我想你在处理那个图书馆的时候会过得很糟糕。
You can use GC.SuppressFinalizer(objA) and GC.KeepAlive(objA) which will prevent the garbage collector from calling the finalize on that object,and so when using
KeepAlive
theobjB
will not finalize becauseobjA
"which is alive" is still has reference of it. however you should then be aware from memory leaks if you forget to finalize or disposeobjA
in a proper way.But suppose that the
objA
at some point in a method for instance has initialize anotherobjectB
and it does not dispose it properly, Then unfortunately I don't thing you can do anything about it.Another thing you can try is to check if that library behaves differently when you are in
Release
mode rather than theDebug
mode; for example they maybe only throw exception at the finalizer if it get called in debug mode "it kind of helper to developers so if they not call dispose or finalize the object in right way an exception will throw while debugging":If that is not the case, then I think you will have a bad days dealing with that library.