设计异常,用法和定义

时间:2012-04-19 14:56:22

标签: c# exception design-patterns

我只是想知道您对定义,抛出和捕获异常的位置有什么看法,看看是否有关于考虑的共识。

让我举一个基于的例子。 假设我有解决方案,在该解决方案中,我有2个项目:DomainProjectControllerProject

  1. 在DomainProject中,我有一个存储库,用于在RepositoryClass中进行一些查询,我有方法:

    GetObjectById(int id) { ... }
    

    我在这个项目中有一些异常定义,如ObjectNotFoundException

  2. ControllerProject我想查询我的存储库,所以我做了一些看起来像这样的事情:

    MyObject obj = repo.GetObjectById(11);
    
  3. 现在问题是谁应该注意检查id是否确实存在。如果你选择ControllerProject应该检查是否存在id,你可以得到一些像这样的代码:

    MyObject obj = repo.GetObjectById(11);
    if (obj == null) {throw new ObjectNotFoundException();}
    

    但缺点是它在使用GetObjectById的地方往往是重复的。当然,有些情况下你不会在意你是否得到一个空值,所以它在某种程度上是合法的,不会直接在DomainProject中抛出异常。但我首先不喜欢复制if测试,第二,与我的问题更相关,我不喜欢在当前项目之外使用异常定义。

    我觉得Exceptions只应该放在定义它的项目中,而其他项目应该只捕获它们。

    回到我的例子,我将如何解决这种情况。一个简单的想法是在我的域项目中定义2方法。一个抛出异常,一个抛出异常。我唯一不确定的是我必须使用哪种命名约定:GetObjectByIdThrowsIfNotFound()GetObjectById()。或者我可以添加一个可选参数GetObjectById(int id, bool isExceptionThrow = true)

    您如何看待异常?

    由于

3 个答案:

答案 0 :(得分:2)

我认为你很好地考虑如何让你的设计正确地传达意图。我同意你的疑虑:抛出异常的唯一层应该是定义异常的层。

也就是说,如果null返回值不明确,如果将密钥与null相关联是有效的,则只需要抛出异常的变量。如果不是(这应该在你的XML注释中注明!),那么null返回总是意味着相同的事情(找不到值),你可以在域层中节省代码和处理异常的开销。如果'找不到值'是Controller层中真正异常的事件,请在那里定义并抛出异常。

如果'存储的null' 有效,我会使用IDictionary<T>bool TryGetObjectById(int id, out object value)建立的语义模式,而且我只包含异常抛出GetObjectById(int key)变体,如果找不到密钥是真的例外,我想保存在调用Try...变体时所涉及的击键。

答案 1 :(得分:0)

我经常看到两种模式:

1)存储库本身应该抛出异常,如果你想处理获取项目的失败,你的调用方法应该包装在try / catch块中。我认为这种模式将是您的应用程序的主要候选者,因为调用者负责处理异常。我会假设您的存储库没有捕获SqlException或任何持久层异常被抛出正确吗?如果是这样,那么你应该让存储库抛出异常并让它冒泡堆栈。

如果不是......

2)您公开包装在容器中的对象,告诉您失败或成功:

public class RepositoryItemContainer<DataType>
{
    public DataType Object { get; set; }
    public bool WasFound { get; set; }
}

然后,不是只返回值,而是返回此包装器,然后代码可以决定它想要做什么:

var repoItem = _repo.GetObjectById(11);

if(repoItem.WasFound)
   var item = repoItem.Object;

else
   throw new ApplicationSpecificException("Wasnt found yo!")

答案 2 :(得分:0)

以下是一些注意事项:

  1. Exceptions适用于例外情况。因此,如果您认为DB中缺少对象是异常情况,我会说现在就实现它。

  2. 如果不是例外情况,请以不引发Exception的方式实施方法,但如果我们查询的nullid,则返回ContainsId(long id)自然错过了。对于将使用您实现的repo类的开发人员来说,似乎很自然并保持预期的行为。

  3. 您可以添加id方法,首先检查数据库中是否存在已请求的{{1}}。也可能是一个不错的选择,但我个人更喜欢第二点。

  4. 希望这有帮助。