这可能是一个非常简单的问题,但是 try-catch block 上的返回类型的最佳实践是什么?目前,我正在做这样的事情:
public List<SomeAttribute> FindAttributes(int id)
{
try
{
//Some query and some logic
}
catch (Exception ex)
{
Logger.Error(ex);
return new List<SomeAttribute>();
}
}
这有什么特别可怕的吗?异常将被记录下来,并且该方法将返回一个空列表——调用函数可以处理该列表。返回null更好吗?如果是,为什么?
一般来说,人们考虑三种策略来处理部分函数(只有输入域的一个子集有效的函数):Total、Defensive,和标称。
总计
这意味着您总是返回一个答案。在这种情况下,您可以考虑返回一个 null
-pointer,在 String.IndexOf
的情况下,例如返回 -1
.
优点:
调用者有时更容易不去想潜在的错误。有时返回值很有用。例如,如果你想切断字符串的第一部分(包括第一个逗号),你可以将其编码为:
string foo = "Foo,Bar"
string foocut = foo.SubString(foo.IndexOf(',')+1); //Bar, in case no comma, it returns the entire string
从而产生更紧凑的代码。但另一方面,有时很难确定最佳返回值是多少。
缺点:
- 它需要工程来确定“最佳”返回值。有很多选择,每个选择只会对一部分来电者有利。
- 有时无法区分没有出错的有效输出和出错时的(默认)输出。
防守
此处您抛出异常(或不捕获异常)。由(特定于域的)调用者来确定抛出异常的原因并进行准确处理。 Util 方法通常对系统了解不多,因此不知道异常发生的原因。
优点:
- 异常可以由拥有最佳知识的调用者处理(因此某种“责任链”)。这可以导致更好的错误处理(向用户提供有用的消息)。不是“SQL 查询中发生错误...”,而是“用户名已存在。”
缺点:
- 错误处理有时很困难。特别是在 C# 中,不需要注释方法可以抛出哪个异常。涵盖所有类型的异常并不容易。未捕获的异常可能会返回到顶部
Main
调用,从而导致应用程序崩溃。对于网络服务器等某些应用程序,这并不总是一种选择。
标称
在这里您记录您的方法并提供预条件:在文档中您指定使用该方法的正确方法是什么。这并不总是可能的,因为有时方法是否成功取决于外部因素(服务器的可用性、操作系统的类型……)。程序员不一定能控制的因素。
优点:
- 产生记录良好(有时严格)定义的方法。
- 实现方法(被调用者)更容易,因为它可以假设一切正常。
缺点:
- 并非总是可能(有时结果确实取决于无法控制的因素)。
- 编写调用程序比较困难,因为它有一个约定,它只会调用带有正确参数的方法。
- 记录所有条件以及验证每个调用是否满足这些条件也很困难。有时,代码契约用于(部分)自动处理验证。
大多数程序不会坚持使用一种策略,而是将它们混合使用:例如,一些异常是完全处理的,另一些是名义上的,而另一些是防御性的。或者一些模块遵循防御策略,而另一个模块使用名义编程。