谁负责错误检查和处理?

时间:2014-07-17 14:37:48

标签: delphi

谁负责错误检查和处理?

我没有任何昂贵的组件库,例如DevExpress或TMS Components等,所以我无法查看源代码以了解大多数组件如何管理错误处理。

具体而言,我想知道的是组件开发人员应该尝试捕获多少错误和警告?在进行有意义的错误检查和让开发人员使用您的组件变得太容易之间是否存在平衡?


以下是使用几种方案的示例:

请注意,这些内容直接来自组件来源(仅供参考)

procedure TMyComponent.AddFromFile(FileName: string);
begin
  FBitmap.LoadFromFile(FileName);
end;

procedure TMyComponent.AddFromFile(FileName: string);
begin
  if FileExists(FileName) then
  begin
    FBitmap.LoadFromFile(FileName);
  end
  else
    raise Exception.Create(FileName + ' does not exist.');
end;

最后两个是在运行时使用组件的实例:

procedure TForm1.FormCreate(Sender: TObject);
begin
  MyComponent1.AddFromFile('D:\Test.bmp');
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  if FileExists('D:\Test.bmp') then
  begin
    MyComponent1.AddFromFile('D:\Test.bmp');
  end
  else
    raise Exception.Create('D:\Test.bmp does not exist.');
end;

我想这归结为谁应该错误检查并处理什么?组件开发人员是负责处理这些类型的检查还是组件的用户?

在我写这篇文章时,我相信组件开发人员和用户都应该处理这样的检查,但我不确定,所以我正在寻找开发人员普遍的共识。

感谢。

3 个答案:

答案 0 :(得分:2)

要回答您的具体问题:

  

具体而言,我想知道的是组件开发人员应该尝试捕获多少错误和警告?在进行有意义的错误检查和让开发人员使用您的组件变得太容易之间是否存在平衡?

关于异常处理的一般规则是,您应该只捕获您知道如何处理的异常,并让其他人传播到可能知道如何处理它的更高代码。如果组件内部引发异常,则组件需要决定是否:

  1. 在内部处理该特定异常并优雅地转移到其他内容而不会通知调用者。

  2. 重新抛出异常(可能会对其进行调整),或者重新抛出一个全新的异常,以便调用者能够识别并处理该特定故障(如果需要)。

  3. 忽略异常(根本不抓住它),让它按原样传播。

  4. 如果组件使用的API返回错误代码而不是引发异常,则组件也需要决定如何处理它。是否忽略错误并继续前进,或者引发异常以使其更明显。

    在您的特定示例中,我更喜欢以下方法:

    type
      EMyComponentAddError = class(Exception)
      private
        FFileName: String;
      begin
        constructor CreateWithFileName(const AFileName: string);
        property FileName: string read FFileName;
      end;
    
    constructor EMyComponentAddError.CreateWithFileName(const AFileName: string);
    begin
      inherited CreateFmt('Unable to add file: %s', [AFileName]);
      FFileName := AFileName;
    end;
    
    procedure TMyComponent.AddFromFile(FileName: string);
    begin
      try
        FBitmap.LoadFromFile(FileName);
      except
        Exception.RaiseOuterException(EMyComponentAddError.CreateWithFileName(FileName));
      end;
    end;
    

    这允许您的组件识别出错误,根据需要对其进行操作,并仍然向调用者报告特定于组件的信息,而不会丢失导致实际失败的原始错误。如果调用者对细节感兴趣,它可以捕获异常,查看其InnerException属性,访问自定义属性(如果存在)等。

    例如:

    procedure TForm1.FormCreate(Sender: TObject);
    begin
      MyComponent1.AddFromFile('D:\Test.bmp');
    end;
    

    让我们假设MyComponent1.AddFromFile('D:\Test.bmp');失败。默认的异常处理程序将捕获它并显示一条弹出消息:

    Unable to add file: D:\Test.bmp
    

    有用但很少有细节,因为它可能因各种原因而失败。也许文件无法打开,但为什么?不存在与未获得许可?也许文件被打开但已损坏?也许内存无法分配?等等。

    如果需要,调用者可以捕获它并显示更多有用信息(不需要 - 组件提供信息,调用者决定是否使用它):

    procedure TForm1.FormCreate(Sender: TObject);
    begin
      try
        MyComponent1.AddFromFile('D:\Test.bmp');
      except
        on E: EMyComponentAddError do
        begin
         ShowMessage('There was a problem adding a file:'+sLineBreak+E.FileName+sLineBreak+sLineBreak+E.InnerException.Message);
         Sysutils.Abort;
        end;
      end;
    end;
    

    或者:

    procedure TForm1.FormCreate(Sender: TObject);
    begin
      try
        MyComponent1.AddFromFile('D:\Test.bmp');
      except
        on E: EMyComponentAddError do
        begin
          raise Exception.CreateFmt('There was a problem adding a file:'#10'%s'#10#10'%s', [E.FileName, E.InnerException.Message]);
        end;
      end;
    end;
    

    其中任何一个都会显示:

    There was a problem adding a file:
    D:\Test.bmp
    The file was not found
    

答案 1 :(得分:1)

<强>组件

procedure TMyComponent.AddFromFile(FileName: string);
begin
  FBitmap.LoadFromFile(FileName);
end;

这就是你所需要的。如果位图对象无法加载文件,无论出于何种原因,它都会引发异常。让该异常传播给代码的使用者。

尝试测试文件是否存在真的没有意义。如果文件存在且它不是位图文件怎么办?如果文件存在,该位图文件是什么,但磁盘有一个duff扇区并且文件读取失败?如果您尝试检查所有错误情况,则只需重复LoadFromFile方法已经执行的检查。

无法从外部检查某些错误情况。只能通过读取文件在某种程度上变得明显的错误无法从外部进行合理的检查。

过度热心,重复错误检查的一个非常常见的后果是,您最终会遇到在应该没有错误的情况下产生错误的代码。如果您的错误检查错误,您最终可能会报告在您让底层代码运行时不会发生的错误。

<强>消费

procedure TForm1.FormCreate(Sender: TObject);
begin
  MyComponent1.AddFromFile('D:\Test.bmp');
end;

此时决定更加困难。我通常希望以下问题成为决定的驱动因素:

  

文件不存在是一个预期的,合理的事件吗?

如果该问题的答案是肯定的,那么您应该考虑在FormCreate方法中处理异常。同样,测试FileExists()只捕获一种失败模式,尽管是常见的失败模式。也许您应该使用try/except块来捕获错误。

如果问题的答案为否,请让错误传播。

也就是说,您还应该考虑是否要从表单的OnCreate事件处理程序中抛出异常。这可能是完全合理的,但你肯定不希望这样做。

答案 2 :(得分:1)

正如大卫所说,我们只需要这个

procedure TMyComponent.AddFromFile(FileName: string);
begin
  FBitmap.LoadFromFile(FileName);
end;

这将检查

  • 有一个现有文件
  • 此文件中的
  • 是有效的位图

现在它取决于应用程序,这对应用程序有多重要。如果此TForm1Application.MainForm,则您在创建过程中未捕获的每个异常都将终止该应用程序。这有时是一种有效的行为。

  1. 非常重要的是,如果没有

    ,应用程序就无法运行
    procedure TForm1.Form1Create(Sender:TObject);
    begin
      MyComponent.AddFromFile( 'D:\Test.bmp' );
    end;
    

    或包装用户友好消息的异常

    procedure TForm1.Form1Create(Sender:TObject);
    begin
      try
        MyComponent.AddFromFile( 'D:\Test.bmp' );
      except
        on E: Exception do
          raise Exception.Create( 'Sorry, I cannot run, because of: ' + E.Message );
      end;
    end;
    
  2. 非常重要,但我们有一个后退来处理这个问题,也许

    procedure TForm1.Form1Create(Sender:TObject);
    var
      LBitmapFiles : TStringList;
      LBitmapIdx : Integer;
      LBitmapLoaded : Boolean;
      LErrorStore : TStringList;
    begin
      LBitmapFiles := nil;
      LErrorStore := nil;
      try
        LBitmapFiles := TStringList.Create;
        LErrorStore := TStringList.Create;
    
        LBitmapFiles.Add( 'D:\Test.bmp' );
        LBitmapFiles.Add( 'D:\Fallback.bmp' );
    
        LBitmapLoaded := False;
        while not LBitmapLoaded and ( LBitmapIdx < LBitmapFiles.Count ) do
          try
            MyComponent.AddFromFile( LBitmapFiles[LBitmapIdx] );
            LBitmapLoaded := True;
          except
            on E: Exception do
            begin
              LErrorStore.Add( LBitmapFiles[LBitmapIdx] + ': ' + E.Message );
              Inc( LBitmapIdx );
            end;
          end;
    
        if not LBitmapLoaded then
          raise Exception.Create( 'Sorry, I cannot run, because of: ' + LErrorStore.Text );
      finally
        LErrorStore.Free;
        LBitmapFiles.Free;
      end;
    end;
    

    还有其他可能的回退,这也取决于应用程序(f.i.将虚拟位图设置到组件)以使应用程序正常工作。

  3. 不重要,如果我们没有图像......我们没有图像,谁在乎

    procedure TForm1.Form1Create(Sender:TObject);
    const
      CBitmapFile = 'D:\Test.bmp';
    begin
      // check, if there is a file
      if FileExists( CBitmapFile ) then
        try
          MyComponent.AddFromFile( CBitmapFile );
        except
          on E: Exception do
          begin
            // Maybe log the exception
            SomeLogger.Log( E );
            // Maybe set some extra parameters for the application to know, this has failed
            RunningWithoutBitmap();
          end;
        end
      else
        // Maybe set some extra parameters for the application to know, this has failed
        RunningWithoutBitmap();
    end;