5

我想我知道我的意思,但我不太确定......

框架文档将类型总结如下:

当方法调用对于对象的当前状态无效时引发的异常。

有一些明确的情况,想到的一种情况是操作需要打开的数据库,但尚未使用所需的信息初始化对象以连接到数据库。

(相切:另一方面,ADO.NET 还要求您显式打开连接的行为并不那么明确;DataAdapter 通过简单地打开连接来偏离这一点,当且仅当它关闭时再次关闭它入口, 我发现这很方便, 并为自己制作了一个使用此模式的 ADO.NET 包装器. 当然这意味着我冒着执行 2 ExecuteNonQuery 并不必要地返回到池的连接, 但我仍然可以打开和关闭我想要的连接和这种性能损失与获得异常相比微不足道。)

我想我的问题的答案是,只有在这种明确的情况下,我们才应该抛出异常。但是在以下情况下哪种异常类型最合适:

public class FormatterMapping
{
  Dictionary formattersByName = new ...();

  public IFormatter GetFormatter(string key) 
  {
     IFormatter f;
     if (formattersByName.TryGetValue(key, out f)) 
       return f;
     else
       throw new ??Exception("explanation of why this failed.");
  }
}

我的第一反应是抛出 ArgumentException。然后我开始认为映射缺少键也可能是参数“错误”。基本上“获取格式化程序 X”操作是无效的,因为X 不在映射中,但我真的不知道 X 是否“应该在那里”或者在这里询问 X 是不明智的。

我当然可以通过返回 null 来绕过整个问题,但这会打开一个更大、更深的蠕虫罐。没有办法知道什么时候会使用返回值,所以后来发生 NullReferenceException 的代码可能与出错的地方没有明显的关系。要么是映射设置不当,要么是使用它的代码要求了一些不应该的东西。

躲避这个问题的另一种方法是使用 TryGetFormatter 选项,但我打算使用此选项的方式实际上应该让调用者知道映射中的内容和不存在的内容,因此在用户代码上强制使用此模式不是也不好。

请不要回答我应该抛出ApplicationException!无论您认为代码应该做什么,请提供原因。毕竟,这里真正有问题的是推理。

除非有人说服我,否则我倾向于 ArgumentException。从映射的角度来看,这个论点是错误的,所以至少有一个明确的推理支持这一点。:)

4

2 回答 2

4

两者都不是完美的,两者都很好。或者你可能想要一些真正明确的东西:

KeyNotFoundException

public class FormatterMapping
{
    Dictionary<string, IFormatter> formattersByName = new ...();

    public IFormatter GetFormatter(string key) 
    {
        // validate the argument
        if (!formattersByName.ContainsKey(key))
            throw new KeyNotFoundException("No formatter exists for given key");

        return formattersByName[key];
    }
}

或者,您可以让 Dictionary<> 扔掉它。

我建议你选择一个,记录它并继续前进。通常不值得浪费大量时间来选择要抛出的特定异常。记录行为更为重要。

于 2012-02-27T16:25:53.373 回答
4

我会考虑使用ArgumentException这样的东西:

if (string.IsNullOrEmpty(key))
{
    throw new ArgumentException("Expected a key");
}

对于您的示例,我认为要么InvalidOperationException合适,要么KeyNotFoundException合适,或者如果您认为合适,请自己编写。

纯粹主义者可能不喜欢我的意见,但我的异常会在我工作的系统中自动通过电子邮件发送给我,所以最终在大多数情况下,只要我能获得足够有用的信息,我并不真正关心我捕获的异常类型看看发生了什么。这包括:

  1. 易于理解的错误信息
  2. 堆栈跟踪
  3. 必要时的内部异常
  4. 任何额外的上下文信息都是可选的奖励。我的意思是,如果您可以在抛出异常时捕获任何值,它可以使调试变得更加容易。
于 2012-02-27T16:54:43.187 回答