.NET Framework中的一个常见模式是TryXXX模式(我不知道这是否是他们真正称之为的模式),其中被调用的方法尝试执行某些操作,True如果成功False则返回,或者操作失败.一个很好的例子是通用Dictionary.TryGetValue方法.
这些方法的文档说它们不会抛出异常:失败将在方法返回值中报告.到目前为止都很好.
我最近遇到了两种不同的情况,其中实现TryXXX模式的.NET Framework方法引发了异常.请参阅System.Random构造函数中的Bug?和Uri.TryCreate抛出UriFormatException?详情.
在这两种情况下,该TryXXX方法都会调用其他方法来引发意外异常,并且这些异常会被转义.我的问题:这是否打破了不抛出异常的隐含合同?
换句话说,如果你正在写作TryFoo,你能保证异常无法逃脱,通过这样写吗?
public bool TryFoo(string param, out FooThing foo)
{
try
{
// do whatever
return true;
}
catch
{
foo = null;
return false;
}
}
Run Code Online (Sandbox Code Playgroud)
这很诱人,因为这可以保证不会逃避任何例外,从而兑现隐含的合同.但这是一个隐藏的bug.
根据我的经验,我的直觉是,这是一个非常糟糕的主意.但是,如果TryFoo让一些异常逃脱,那么它真正说的是"我不会抛出任何我知道如何处理的异常",然后合同的整个想法"我不会抛出异常"被抛出窗口.
那么,你有什么看法?应该TryFoo处理所有异常,还是只处理它预期发生的异常?你的理由是什么?
这取决于.TryParseFoo应该捕获所有解析异常,例如null或格式错误的字符串.但也有不相关的例外(ThreadAbortException比如说)不应该自动安静.
在Python中(跟我一起),你可以通过捕获KeyboardInterrupt异常来解决很多麻烦,这基本上意味着你的程序即使你用Ctrl-C也能继续运行.
我的经验法则是你应该TryFoo在Foo没有清理输入的情况下使用.如果发现TryFoo与Foo您的输入无关的错误,则会过于激进.
这不是不抛出异常的保证.在Dictionary上考虑这个简单的例子......
var dic = new Dictionary<string, string>() { { "Foo", "Bar" } };
string val = String.Empty;
string key = null;
dic.TryGetValue(key, out val); // oops
Run Code Online (Sandbox Code Playgroud)
我发送了一个null键,我得到一个NullArgumentException.反射器因此显示代码......
private int FindEntry(TKey key)
{
if (key == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
}
// more stuff
}
Run Code Online (Sandbox Code Playgroud)
在我看来,合同的意思是"如果你试图做的事情失败了,我就不会抛出异常",但这远非"我不会抛出任何例外".既然你可能没有编写框架,你可能会妥协这样的事情......
catch( Exception ex )
{
Logger.Log( ex );
Debug.Assert( false );
foo = null;
return false;
}
Run Code Online (Sandbox Code Playgroud)
...并且不处理具有该catch块的TryXXX故障情况(因此您没有一堆简单的日志内容).在这种情况下,您至少会揭示错误的性质并在开发期间识别它.时间以及在运行时记下它.
| 归档时间: |
|
| 查看次数: |
689 次 |
| 最近记录: |