我在MSDN中挖掘并发现this article有一些有用的建议:没有公共成员可以抛出或不抛出基于某些选项的异常。 < / p>
例如:
Uri ParseUri(string uriValue, bool throwOnError)
现在我当然可以看到,在99%的情况下,这将是可怕的,但它偶尔使用是否合理?
我看到它使用的一种情况是在访问数据库或配置文件中的数据时使用“AllowEmpty”参数。例如:
object LoadConfigSetting(string key, bool allowEmpty);
在这种情况下,替代方法是返回null。但是调用代码会被空引用检查。 (如果您愿意,该方法还会排除实际允许null作为特定可配置值的能力。)
你有什么想法?为什么这会是一个大问题?
答案 0 :(得分:9)
我认为投掷/不投掷决定基于布尔值肯定是一个坏主意。也就是因为它要求开发人员查看一段代码以获得API的功能知识,以确定布尔的含义。这本身就很糟糕,但当它改变底层错误处理时,它可以使开发人员在阅读代码时很容易出错。
在这种情况下,拥有2个API会更好,更具可读性。
Uri ParseUriOrThrow(string value);
bool TryParseUri(string value, out Uri uri);
在这种情况下,它100%清楚这些API的作用。
关于为什么布尔值不好作为参数的文章:http://blogs.msdn.com/jaredpar/archive/2007/01/23/boolean-parameters.aspx
答案 1 :(得分:3)
通常最好选择一种错误处理机制并始终坚持下去。允许这种触发器代码无法真正改善开发人员的生活。
在上面的例子中,如果解析失败并且throwOnError为false会发生什么?现在用户必须猜测如果要返回NULL,或者上帝知道......
确实,异常和返回值之间存在持续的争论,因为它是更好的错误处理方法,但我非常肯定对于保持一致并坚持你做出的任何选择都存在共识。 API不会让用户感到惊讶,错误处理应该是界面的一部分,并且与界面一样明确。
答案 2 :(得分:1)
但是,我认为MSDN文章严格指的是'throwOnError'标志。在这些情况下,要么在方法本身内部忽略错误(错误,因为它是隐藏的),或者返回某种类型的null /错误对象(错误,因为您没有使用异常来处理错误,这是不一致的并且本身就是错误易发)。
虽然你的例子对我来说似乎很好。异常表示该方法无法执行其职责 - 没有返回值。但是'allowEmpty'标志会改变方法的语义 - 所以现在预期和合法的是异常('空值')。另外,如果您有抛出异常,您将无法轻松返回配置数据。所以在这种情况下似乎没问题。
答案 3 :(得分:1)
在任何公共API中,有两种方法来检查错误情况是一个坏主意,因为如果发生错误将会发生什么变得不明显。只是通过查看代码将无济于事。你必须理解flag参数的语义(没有什么能阻止它成为表达式)。
如果检查null不是一个选项,并且如果我需要从这个特定的失败中恢复,我更喜欢创建一个特定的异常,以便我以后可以捕获它并适当地处理它。在任何其他情况下,我抛出一般情况。
答案 4 :(得分:0)
与此相符的另一个例子可能是某些值类型
上的TryParse方法集
bool DateTime.TryParse(string text, out DateTime)
答案 5 :(得分:0)
使用donTThrowException参数会破坏整个异常点(使用任何语言)。如果调用代码想要:
public static void Main()
{
FileStream myFile = File.Open("NonExistent.txt", FileMode.Open, FileAccess.Read);
}
欢迎他们(C#甚至没有检查过例外)。在Java中,同样的事情将通过以下方式完成:
public static void main(String[] args) throws FileNotFoundException
{
FileInputStream fs = new FileInputStream("NonExistent.txt");
}
无论哪种方式,调用者的工作是决定如何处理(或不处理)异常,而不是被调用者。
答案 6 :(得分:0)
在链接到文章中有一个注释,不应将异常用于控制流 - 这似乎在示例任务中隐含。例外应反映方法级别失败。要有一个签名,可以抛出一个错误似乎没有考虑到设计。
Jeffrey Richters通过C#预订CLR指出 - “当方法无法完成其名称所指示的任务时,您应该抛出异常”。
他的书也指出了一个非常常见的错误。人们倾向于编写代码来捕获所有内容(他的话“在没有经过适当使用异常训练的开发人员的普遍存在的错误往往过于频繁和不正确地使用catch块。当你发现异常时,你会说明你期望这个例外,你明白它为什么会发生,你知道如何处理它。“)
这让我尝试编写我可以预期的异常代码,并且可以在我的逻辑中处理,否则它应该是一个错误。
验证您的参数并防止异常,并且只捕获您可以处理的内容。
答案 7 :(得分:0)
我希望有一个参数可以指示失败是应该导致异常还是简单地返回错误指示,因为这样的参数可以很容易地从外部例程传递到内部例程。考虑类似的事情:
Byte [] ReadPacket(bool DontThrowIfNone)//记录为如果没有则返回null { int len = ReadByte(DontThrowIfNone); //如果没有,则记录为返回-1 如果(len如果在读取数据时出现类似TimeoutException的行为应该导致异常,则应在ReadByte()或ReadMultiBytesbytes()中抛出此类异常。但是,如果这种缺乏数据应该被认为是正常的,那么ReadByte()或ReadMultiBytesbytes()例程不应该抛出异常。如果只使用do / try模式,则ReadPacket和TryReadPacket例程需要具有几乎相同的代码,但其中一个使用Read *方法,另一个使用TryRead *方法。恶心。
使用枚举而不是布尔值可能更好。