如何替换空除块?

时间:2017-01-13 15:35:28

标签: delphi exception exception-handling try-except

浏览一些遗留代码,我遇到了一些"空"除了块。它们都是出于类似的原因实现的,即处理从TEdit中的文本到数值的转换。由于TEdit可能为空,因此在这种情况下应该没有错误消息:

procedure TmyForm.EditExit(Sender: TObject);
begin
  ...  
  try
    _value := StrToFloat(Edit.Text);
  except
  end; 
  ...
end;

这很有效,但我认为这不是一个好的做法。有没有更好的方法来获得相同的行为?

2 个答案:

答案 0 :(得分:4)

您应该使用TryStrToFloat

function reinitSwiper(swiper) {
  setTimeout(function () {
   swiper.reInit();
  }, 500);
}

这是一个返回指示转换成功的布尔值的函数。成功时,转换后的值将以out参数返回。

答案 1 :(得分:3)

TLDR 请勿寻求“适合所有人”的“一刀切”。例外吞咽者的毯子更换选项。而是确定您在每种情况下尝试解决的具体问题,并确保您的代码明确传达其意图。

您可能已经知道这一点,但我想强调所提供代码中的一些问题。理解这些问题会在可能出错的地方产生明显的可能性。在每种情况下考虑您想要的行为是有帮助的,您应该确保您的代码明确反映您的决策,使其更易于阅读和维护。

begin
  ...  
  try
    _value := StrToFloat(Edit.Text);
  except
  end; 
  ...
end;

吞咽异常的原因解释如下:

  

由于TEdit可能为空,因此在这种情况下不应出现错误消息。

  1. 参考引用的评论:异常吞噬者隐藏的内容比指明的意图更多。无效字符或“不适合”的值\ n'并且无法存储,也会引发即时吞下的异常。这可能是可以接受的,但我的猜测是这些可能性更容易被忽视。尽管可能性很小,但即使是与转换本身完全无关的异常也会被吞噬:这样的“内存不足”和“内存不足”。或者'堆栈溢出'。
  2. 如果在StrToFloat中引发了异常,则会跳过对_value的分配。所以它保留了之前的价值。
    • 这不一定是 ,如果 ,其先前的值可预测 为了目的,它以前的值可接受
    • 但价值可能无法预测。特别是如果它是一个未初始化的局部变量:它将具有在调用时恰好在堆栈上发生的任何事情。但是你也可能会发现,如果它是一个对象的成员,它具有可预测但很难辨别以前的值 - 使代码难以阅读。
    • 即使看起来可以预测,这个价值可能也不符合目的"。考虑在按钮单击时将编辑控件值读入属性的表单。首次单击时,该值对于属性集有效。但是在第二次点击时,价值已经改变,财产没有更新 - 但它在第一次点击时显然不再有效。
  3. 最后,最重要的问题是,如果出现问题,吞下异常 的方法无法 正确执行其任务。调用该方法的其他代码(但可能依赖于其正确的行为)幸福地没有意识到这个问题。通常,这只会导致延迟错误(更难修复)。根本问题是隐藏的,所以以后会出现问题。例如。
    • 另一种称为上述期望_value的方法将正确分配给某些计算。
    • 隐藏了根错误,因此_value不正确。
    • 以后的计算可能会产生EDivByZero或只是不正确的结果。
    • 如果持续存在错误结果,问题可能会隐藏多年。
  4. 如前所述,你如何修复异常吞咽者取决于你在每种情况下的意图(是的,这样做的工作更多,但是如果你正在修复它有助于解决它的问题"正确"。)

    选项1

    你真的需要隐瞒"用户犯了错误"

    假设代码中没有其他错误处理错误:

    • 如果用户输入"您好"将引发异常,中止当前操作。用户将看到一条错误消息,指出'Hello' is not a valid floating point value.
    • 同样,不输入任何值将导致:'' is not a valid floating point value.

    因此,值得认真考虑的一个选择是:只需删除吞咽者。

    begin
      ...  
      _value := StrToFloat(Edit.Text);
      ...
    end;
    

    选项2

    由于您在TEdit 为空时特别关注引发错误,因此您可能会将此视为特殊情况。

    • 假设当用户没有提供值时,0是合适的默认值可能是有意义的。 NB!只有在确实是适当的默认值时才这样做。 (见下一点)
    • 该值可能是可选的。在这种情况下,如果未提供可选内容,则您不想报告错误。而是设置一个与该值相关联的标志,表示未提供该标志。 (注意:不要超载 有效 值以表示未提供的值'否则您将无法区分两个。)

    在任何一种情况下,都明确地处理特殊情况并允许默认错误报告以其他方式启动。

    begin
      ...  
      if (Trim(Edit.Text) = '') then
        _value := 0
        //or _valueIsNull := True;
      else
        _value := StrToFloat(Edit.Text);
      ...
    end;
    

    注意:您当然也可以在用户更新控件之前在控件值中设置默认值0。这使得默认设置为用户。如果用户选择删除 默认值,则还原为选项1会通知用户不允许这样做。

    选项3

    可以使默认错误处理更加用户友好。例如。您可能需要更详细的信息,确保将焦点设置为“错误控制”,或设置提示消息。

    在这种情况下,您希望根据David的回答使用TryStrToFloat()据我所知,这是在Delphi 7中引入的。(在旧版本中,您可以使用TextToFloat()。例如,检查StrToFloat()的实现。)

    以下是一个可能的示例,用于演示如何编写一些简单的实用程序函数来封装您的特定需求,并使您对某些案例的特殊处理是明确的。

    type
      TDefaultMode = (dmNone, dmDefaultEmptyValue, dmDefaultInvalidValue);
    
    function ReadFloatFromEditControl(AEdit: TCustomEdit; ADefaultMode: TDefaultMode = dmNone; ADefaultValue: Double = 0.0): Double;
    begin
      if not TryStrToFloat(AEdit.Text, Result) then
      begin
        if (ADefaultMode = dmDefaultEmptyValue) and (Trim(AEdit.Text) = '') then
          Result := ADefaultValue
        else if (ADefaultMode = dmDefaultInvalidValue) then
          Result := ADefaultValue
        else
        begin
          AEdit.SetFocus;
          raise EConvertError.Create('['+AEdit.Text+'] is an invalid floating point value.');
        end;
        {If default is applied, replace value in edit control}
        AEdit.Text := FloatToStr(Result);
      end;
    end;
    
    {Use as follows:}
    v1 := ReadFloatFromEditControl(Edit1);
    v2 := ReadFloatFromEditControl(Edit2, dmDefaultInvalidValue);
    v3 := ReadFloatFromEditControl(Edit3, dmDefaultEmptyValue, 1.0);