代码之家  ›  专栏  ›  技术社区  ›  James

无例外错误处理

  •  26
  • James  · 技术社区  · 17 年前

    不用于处理常规错误情况。

    记住这个词的用法

    不要对信号抛出异常 常见事件。 当事情真正发生时 普通的事情发生了。

    private void DoSomething(string requiredParameter)
    {
    if (requiredParameter == null) throw new ArgumentExpcetion("requiredParameter cannot be null");
    // Remainder of method body...
    }
    

    // Renames item to a name supplied by the user.  Name must begin with an "F".
    public void RenameItem(string newName)
    {
       // Items must have names that begin with "F"
       if (!newName.StartsWith("F")) throw new RenameException("New name must begin with /"F/"");
       // Remainder of method body...
    }
    

    1. 让方法返回a 重命名结果。成功,

    2. 将重命名状态作为事件参数的一部分?

    3. 并从UI类调用一个方法


    根据对这个问题的回答,我觉得我必须用更具体的术语来陈述这个问题:

    1. 用户在UI中输入值。
    2. BLL发现违规行为。
    3. UI接收BLL的返回并向用户报告错误。

    8 回复  |  直到 15 年前
        1
  •  3
  •   Jason Williams    16 年前

    我假设您正在创建自己的业务规则验证引擎,因为您还没有提到您正在使用的那个。

    1. Message
    2. 事实上,一些业务规则的失败可能是由异常引起的—— FormatException 例如。您可以捕获该异常并将其添加到列表中。

    事实上,本月的MSDN杂志有一篇文章提到了新的 AggregateException


    由于您使用的是Windows窗体,因此应该使用内置的验证机制: Validating ErrorProvider 组件。

        2
  •  4
  •   John Saunders    17 年前

    ValidationResult v = ValidateName(string newName);
    if (v == ValidationResult.NameOk)
        SetName(newName);
    else
        ReportErrorAndAskUserToRetry(...);
    

    public void SetName(string newName)
    {
        if (ValidateName(newName) != ValidationResult.NameOk)
            throw new InvalidOperationException("name has not been correctly validated");
    
        name = newName;
    }
    

    (请注意,这可能不是性能的最佳方法,但在对UI输入应用简单的验证检查的情况下,两次验证不太可能有任何意义。或者,上述检查可以纯粹作为仅调试断言检查来完成,以捕捉程序员在不先验证输入的情况下调用方法的任何尝试。一旦你知道所有调用者都遵守他们的约定,通常根本不需要进行发布运行时检查)

    Either a member fulfills its contract or it throws an exception. Period.

    这遗漏了一件事:合同是什么?在“合约”中声明方法返回状态值是完全合理的。例如文件。Exists()返回一个状态代码,而不是异常,因为这是它的合约。

    设置名称,它试图在一个任务中完成两个任务,这意味着调用者永远不知道它会表现出什么行为,并且必须对这些情况进行特殊处理。但是,如果将SetName拆分为单独的Validate和Store步骤,则StoreName的约定可以是传递有效输入(由ValidateName传递),如果不符合此约定,则会抛出异常。因为每个方法只做一件事,所以契约非常明确,何时应该抛出异常也很明显。

        3
  •  4
  •   dotarj    9 年前

    我认为你对预期的信息有了错误的印象。这是我昨天从《 current edition of Visual Studio magazine

    要么一个成员履行了合同,要么它抛出了一个例外。期间。没有中间地带。没有返回码,有时有效,有时无效。

    应谨慎使用异常,因为创建和抛出异常的成本很高,但它们是唯一的例外。NET框架通知客户端(我指的是任何调用组件)错误的方式。

        4
  •  2
  •   Henk Holterman    17 年前

    我同意Henk的部分建议。

    .Has错误

        5
  •  1
  •   Echiban    16 年前

    我经过深思熟虑,想出了这个解决方案。对域类执行以下操作:

    • 添加属性:ThrowsOnBusinessRule。默认值应为true才能抛出异常。如果你不想扔掉它,请将其设置为false。
    • 添加一个私有Dictionary集合,以存储具有违反业务规则的域属性键的异常。(当然,如果你愿意,你可以公开披露)
    • 添加一个方法:ThrowsBusinessRule(字符串propertyName,异常e)来处理上述逻辑
        6
  •  0
  •   Paul Turner    17 年前

    以更新数据库中的记录为例。这是许多可能单独失败的操作的集合。

    如。

    1. 检查数据库中是否有要更新的记录。--记录可能不存在

    public class Result<T> {
      public T Value { get; }
      public string Error { get; }
    
      public Result(T value) => Value = value;
      public Result(string error) => Error = error;
    
      public bool HasError() => Error != null;
      public bool Ok() => !HasError();
    }
    

    public Result<Record> FindRecord(int id) {
      var record = Records.Find(id);
      if (record == null) return new Result<Record>("Record not found");
      return new Result<Record>(record);
    }
    
    public Results<bool> RecordIsValid(Record record) {
      var validator = new Validator<Record>(record);
      if (validator.IsValid()) return new Result<bool>(true);
      return new Result<bool>(validator.ErrorMessages);
    }
    
    public Result<bool> UpdateRecord(Record record) {
      try {
        Records.Update(record);
        Records.Save();
        return new Result<bool>(true);
      }
      catch (DbUpdateException e) {
        new Result<bool>(e.Message);
      }
    }
    

    现在,我们的聚合方法将所有这些联系在一起:

    public Result<bool> UpdateRecord(int id, Record record) {
      if (id != record.ID) return new Result<bool>("ID of record cannot be modified");
    
      var dbRecordResults = FindRecord(id);
      if (dbRecordResults.HasError())
        return new Result<bool>(dbRecordResults.Error);
    
      var validationResults = RecordIsValid(record);
      if (validationResults.HasError())
        return validationResults;
    
      var updateResult = UpdateRecord(record);
      return updateResult;
    }
    

    哇!真是一团糟!

    Result<T> 以指示特定的错误类型:

    public class ValidationError : Result<bool> {
      public ValidationError(string validationError) : base(validationError) {}
    }
    
    public class RecordNotFound: Result<Record> {
      public RecordNotFound(int id) : base($"Record not found: ID = {id}") {}
    }
    
    public class DbUpdateError : Result<bool> {
      public DbUpdateError(DbUpdateException e) : base(e.Message) {}
    }
    

    var result = UpdateRecord(id, record);
    if (result is RecordNotFound) return NotFound();
    if (result is ValidationError) return UnprocessableEntity(result.Error);
    if (result.HasError()) return UnprocessableEntity(result.Error);
    return Ok(result.Value);
    

    然而在上述示例中, result is RecordNotFound 将始终返回false,因为它是 Result<Record> ,而 UpdateRecord(id, record) 返回 Result<bool>

    一些积极因素: *它避免了例外 *The 结果<T> *子类 结果<T>

    T 可能不同。如。 结果<T> 结果<记录> *聚合方法,例如 UpdateRecord(int, Record) 现在需要让他们自己关注他们调用的方法的结果。

    现在使用例外。..

    public class ValidationException : Exception {
      public ValidationException(string message) : base(message) {}
    }
    
    public class RecordNotFoundException : Exception  {
      public RecordNotFoundException (int id) : base($"Record not found: ID = {id}") {}
    }
    
    public class IdMisMatchException : Exception {
      public IdMisMatchException(string message) : base(message) {}
    }
    
    public Record FindRecord(int id) {
      var record = Records.Find(id);
      if (record == null) throw new RecordNotFoundException("Record not found");
      return record;
    }
    
    public bool RecordIsValid(Record record) {
      var validator = new Validator<Record>(record);
      if (!validator.IsValid()) throw new ValidationException(validator.ErrorMessages)
      return true;
    }
    
    public bool UpdateRecord(Record record) {
      Records.Update(record);
      Records.Save();
      return true;
    }
    
    public bool UpdateRecord(int id, Record record) {
      if (id != record.ID) throw new IdMisMatchException("ID of record cannot be modified");
    
      FindRecord(id);
      RecordIsValid(record);
      UpdateRecord(record);
      return true;
    }
    

    然后在控制器动作中:

    try {
      UpdateRecord(id, record)
      return Ok(record);
    }
    catch (RecordNotFoundException) { return NotFound(); }
    // ...
    

    没有检查方法是否成功或失败。.. 没有带异常结果的类型转换。.. 您可以轻松添加一个应用程序范围的异常处理程序,该程序根据异常类型返回正确的响应和状态代码。.. 对控制流使用异常有很多好处。..

    我不确定负面因素是什么。..人们说他们就像GOTO。我真的不知道为什么这很糟糕。..他们还说表现很差。..但那又怎样?这与正在进行的DB调用相比如何?我不确定这些负面因素是否真的是合理的理由。