DELPHI盒子
!实时搜索: 盒子论坛 | 注册用户 | 修改信息 | 退出
检举帖 | 全文检索 | 关闭广告 | 捐赠
技术论坛
 用户名
 密  码
自动登陆(30天有效)
忘了密码
≡技术区≡
DELPHI技术
移动应用开发
Web应用开发
数据库专区
报表专区
网络通讯
开源项目
论坛精华贴
≡发布区≡
发布代码
发布控件
文档资料
经典工具
≡事务区≡
网站意见
盒子之家
招聘应聘
信息交换
论坛信息
最新加入: p1990526
今日帖子: 15
在线用户: 12
导航: 论坛 -> DELPHI技术 斑竹:liumazi,sephil  
作者:
男 tuesdays (Tuesday) ▲▲▲▲△ -
注册会员
2022/6/10 11:54:49
标题:
公共底层库需要保障不报错误异常吗? 浏览:747
加入我的收藏
楼主: 判断工作是调用层处理, 还是底层处理? 

1: 调用层的人说传错0, 也不是你弄崩程序的借口.
2: 底层的人说瞎jb传参数, 不崩溃才怪. 

这到底是谁的责任? 

比如: 用户传0后果不堪.
function GFile.intdiv(inta,intb:Integer):Integer;
begin
 result :=  (inta div intb); 
end;

比如: 用户传不存在文件后果不堪.
procedure GFile.LoadFromFile(const AFileName: String;
  AEncoding: TTextEncoding);
var
  AStream: TFileStream;
begin
  AStream := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite);
  try
    LoadFromStream(AStream, AEncoding);
  finally
    FreeObject(AStream);
  end;
end;
----------------------------------------------
delphi界写python最强, python界写delphi最强. 写自己的代码, 让别人去运行.
作者:
男 lsuper (lsuper) ★☆☆☆☆ -
盒子活跃会员
2022/6/10 15:51:13
1楼: 个人习惯,底层如果“臣妾做不到”就 raise 足够的 detail 让调用者处理

不过:
1、这只是“个人风格”也不是那么一刀切,要么人家 msdn 上 api 各种 return/remark 提醒呢?
2、譬如,上面 GFile.LoadFromFile 文件不存在究竟应该咋处理?直接忽略 or 抛异常都是存在合理的场景,建议调用者还是根据文档 主动防御 or try catch
----------------------------------------------
-
作者:
男 tuesdays (Tuesday) ▲▲▲▲△ -
注册会员
2022/6/10 16:31:56
2楼: @lsuper
现在是程序在客户那崩了, 公司追责任,  这怎么定责任? 

文档只有说传文件路径进去, 没说文件不存在, 不可读, 或者防火墙会引起崩.
----------------------------------------------
delphi界写python最强, python界写delphi最强. 写自己的代码, 让别人去运行.
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2022/6/10 16:41:17
2楼: 那个文件不存在的情况,底层的处理是 raise exception 了,你不做任何代码,它也会 raise 出来。顶上调用这个的人会遇到异常,自己处理。

假设文件不存在,这里不会自己抛出异常,而是直接崩溃,那就你要判断是否文件存在,不存在,则你给他抛个异常,让顶上的代码去处理这个异常。这样防止程序崩溃。
----------------------------------------------
-
作者:
男 tuesdays (Tuesday) ▲▲▲▲△ -
注册会员
2022/6/10 16:50:19
3楼: @pcplayer 
那就是底层的人没写raise导致的.  崩了.
----------------------------------------------
delphi界写python最强, python界写delphi最强. 写自己的代码, 让别人去运行.
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2022/6/10 16:57:39
4楼: 底层的代码,大概需要这样写:

if not FileExists(YourFileName) then raise Exception.Create('文件不存在');

try
  do....
except
  raise exception.Create('未知原因导致程序出错');
end;

文件不存在算是已知原因,抛出一个异常,就不会去执行底下的 do.. 代码了。顶层的程序员也知道是文件不存在;

其它想不到但可能带来程序崩溃的情况,直接 try...
----------------------------------------------
-
作者:
男 tuesdays (Tuesday) ▲▲▲▲△ -
注册会员
2022/6/10 17:21:49
5楼: @pcplayer

底层到处try, 会不会导致逻辑混乱?  导致调用层需要接收处理?
----------------------------------------------
delphi界写python最强, python界写delphi最强. 写自己的代码, 让别人去运行.
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2022/6/10 21:23:49
6楼: try 怎么会导致逻辑混乱。

不知道是否会出错的地方,就要预设它会出错,就必须要 try ..except  把错误抓住否则就崩溃了。

程序的稳固,健壮,就靠这个。

至于说你要不要 raise 告诉应用层,看你的逻辑。一般来说最好是告诉应用层。应用层也好自己想办法处理。你写底层的,不要预设应用层会如何处理。
----------------------------------------------
-
作者:
男 lsuper (lsuper) ★☆☆☆☆ -
盒子活跃会员
2022/6/10 21:51:14
7楼: @tuesdays (Tuesday): 所以应用层需要在顶层(每个线程,主 ui 一般 vcl 帮你处理了,自己的 thread 自己处理)统一处理异常啊;其他如 @pcplayer (pcplayer)所说,关注就 try except 否则默认应用层统一处理
----------------------------------------------
-
作者:
男 emailx45 (emailx45) ▲▲▲▲△ -
注册会员
2022/6/10 22:10:02
7楼: at general, you should raise a exception if the task should still if it alread started... else, you can do the workaround and continue.

try some like this:
button1 = try open the file  -- using fmOpenShareXXX/ZZZ
button2 = try open the same file  -- using fmOpenShareXXX/ZZZ
----
implementation

{$R *.dfm}

const
  arquivo: string = 'test.dcu';

var
  arq1: TFileStream = nil;
  arq2: TFileStream = nil;

function MyLoadFile(AFileName: string): TFileStream;
begin
  result := nil;
  //
  if FileExists(arquivo) then // FileExists() already try take care about possibles errors ... "GetLastError..."
    try
      result := TFileStream.Create(arquivo, fmOpenRead or fmShareDenyRead { fmShareDenyWrite / fmShareExclusive } ); // simulating a "DenyRead" in all cases
      // if success... return the file opened!
    except
      // here show error just for your test!
      on E: EFOpenError do
        ShowMessage('First: the Exception for this error: ' + E.ClassName);
      on E: Exception do
        ShowMessage('for last: Generic Exception: ' + E.ClassName);
    end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  arq1 := MyLoadFile(arquivo);
  //
  if not(arq1 = nil) then
    ShowMessage('hello world to arq1');
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  arq2 := MyLoadFile(arquivo);
  //
  if not(arq2 = nil) then
    ShowMessage('hello world to arq2');
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  arq1.Free;
  arq1 := nil;
end;

procedure TForm1.Button4Click(Sender: TObject);
begin
  arq2.Free;
  arq2 := nil;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  arq1.Free;
  arq2.Free;
end;

end.

----------

function MyLoadFile(AFileName: string): TFileStream;
begin
  result := nil;
  //
  if FileExists(arquivo) then // FileExists() already try take care about possibles errors ... "GetLastError..."
    try
      result := TFileStream.Create(arquivo, fmOpenRead or fmShareDenyRead { fmShareDenyWrite / fmShareExclusive } ); // simulating a "DenyRead" in all cases
      // if success... return the file opened!
    except
      raise;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  // if you need know the exception on FORM caller!
  try
    arq1 := MyLoadFile(arquivo);
    //
  except
    on E: Exception do
      ShowMessage(E.ClassName + sLineBreak + E.Message);
  end;
  //
  if not(arq1 = nil) then
    ShowMessage('hello world to arq1');
end;
----------------------------------------------
The higher the degree, the greater the respect given to the humblest!
作者:
男 emailx45 (emailx45) ▲▲▲▲△ -
注册会员
2022/6/10 22:50:27
8楼: my flaw: in general, you should create an exception if the task has already started and you shouldn't continue after an error that would compromise the result. If not, you could find another way to complete it.
----------------------------------------------
The higher the degree, the greater the respect given to the humblest!
作者:
男 emailx45 (emailx45) ▲▲▲▲△ -
注册会员
2022/6/10 23:21:19
9楼: function MyLoadFile(AFileName: string; out ALastError: integer): TFileStream;
begin
  result := nil;
  //
  if FileExists(arquivo) then // FileExists() already try take care about possibles errors ... "GetLastError..."
    try
      result := TFileStream.Create(arquivo, fmOpenRead or fmShareDenyRead { fmShareDenyWrite / fmShareExclusive } ); // simulating a "DenyRead" in all cases
      // if success... return the file opened!
    except
      on E: Exception do
      begin
        ALastError := GetLastError;
        //
        raise; // the GetLastError value can changes after this...
      end;
    end;
end;
----------------------------------------------
The higher the degree, the greater the respect given to the humblest!
作者:
男 emailx45 (emailx45) ▲▲▲▲△ -
注册会员
2022/6/10 23:24:32
10楼: No, if FILE-NOT-EXISTS should not raise any exception, at general!
Exceptions, only in extreme cases!

GetLastError = ERROR_FILE_NOT_FOUND {2}

GetLastError = ERROR_SHARING_VIOLATION {32}
----------------------------------------------
The higher the degree, the greater the respect given to the humblest!
作者:
男 tuesdays (Tuesday) ▲▲▲▲△ -
注册会员
2022/6/11 12:39:57
11楼: @lsuper 
有没有一种情况, 为了减轻底层代码人员的压力, 所有代码不带raise 
规定所有异常由调用层负责处理?  这样会不会完美点?
----------------------------------------------
delphi界写python最强, python界写delphi最强. 写自己的代码, 让别人去运行.
作者:
男 lsuper (lsuper) ★☆☆☆☆ -
盒子活跃会员
2022/6/11 19:38:35
12楼: @tuesdays (Tuesday): Windows API 就是这样,这是种代码风格(C),你需要完整的文档 + GetLastError
----------------------------------------------
-
作者:
男 luckyrandom (luckyrandom) ★☆☆☆☆ -
普通会员
2022/6/11 22:33:26
13楼: 没啥绝对标准,技术难免会含有些政治、斗法、扯皮
有些是历史原因,比如项目初期团队伙伴间做了约定
或者之前的伙伴间有这个默契。。至于后来,再说吧
----------------------------------------------
SQL SERVER DBA QQ:315054403 曾经的Delphier  缘在上海
作者:
男 hawke2e (hawke2e) ★☆☆☆☆ -
注册会员
2022/6/12 8:31:59
14楼: 从技术角度看,当然是调用层的错:
1. 程序有bug,传参数出错。
2. 最重要的是没有处理下层崩溃的容错机制:异常必须妥善处理,下层崩了最好能妥善处理。

对于下层,1楼说的是对的,处理不了的异常尽可能的详细往上报,但这是加分行为,能做到当然最好,做不到也算及格。
如果下层把不该它处理的异常给处理了,导致上层不知道发生了错误或者造成了误解,就是不及格行为。
----------------------------------------------
软件是什么,相信很多人都说不清。
作者:
男 tuesdays (Tuesday) ▲▲▲▲△ -
注册会员
2022/6/12 9:40:14
15楼: @hawke2e 
但是你认同: 凡是不经过黑盒测试的代码都是不完整的代码吗? 

现在的情况就是底层的代码明显承受不了黑盒测试. 比如读文件那个, 虽然情况可能有很多, 但调用层可能只是想说, 能读就读, 不能读, 请返回空. 

至少错误, 应用层其实并不想关心, 你上Kfc会关心为什么鸡肉太甜吗?
----------------------------------------------
delphi界写python最强, python界写delphi最强. 写自己的代码, 让别人去运行.
作者:
男 tuesdays (Tuesday) ▲▲▲▲△ -
注册会员
2022/6/12 9:44:06
16楼: Windows API的设计理念其实是正确的, 
不管发什么, 流程完整性不能被破坏, 必须有返回. 并且不崩. 
至于是否关心错误, 那是另外一回事.
----------------------------------------------
delphi界写python最强, python界写delphi最强. 写自己的代码, 让别人去运行.
作者:
男 hawke2e (hawke2e) ★☆☆☆☆ -
注册会员
2022/6/12 10:43:11
17楼: 一码归一码,不能一概而论。
你的例子里,参数传递出错,客户程序崩不崩,由文档说了算,文档说了怎么处理,那就是客户的责任;文档没说,那既然参数传错了,按常理就别想有预期的结果。
客户程序崩了抛异常了,有没有得到妥善处理就是调用层的责任了。


WINDOWS API的设计不是风格,因为排错是一件关键的事情,不是你喜欢怎么设计就怎么设计的。
我没设计过操作系统,不好说。WINAPI的设计起码有以下问题:
1. 系统复杂,其内部异常种类会很多,都归为这么少的错误代码,是否足够支撑排错。
  微软把大部分错误都掩盖了,不是好态度。
2. 任何api调用地方都不抛出异常,那得很全面的捕获内部异常才行,成本成本。
3. 即使WIN内部程序逻辑上都没BUG,照样会因为硬件不稳定而抛异常,此时内核怎么应对?处理不了蓝屏?

总而言之,软件工程的方法论无法支撑人类在广袤的银行里探索。
我们换个思路,程序本无对错,只不过放错了地方,就像生物,结构无所谓缺陷,无非不适应当前环境罢了。
在有问题的硬件上,是否都存在能稳定运行的程序呢?
----------------------------------------------
软件是什么,相信很多人都说不清。
作者:
男 hawke2e (hawke2e) ★☆☆☆☆ -
注册会员
2022/6/12 11:21:02
18楼: “不管发什么, 流程完整性不能被破坏, 必须有返回. 并且不崩. ”这是错误的观点。
即使程序逻辑上无bug,照样会因为硬件问题会崩。
而且要程序逻辑上无bug,可能吗?
----------------------------------------------
软件是什么,相信很多人都说不清。
作者:
男 scarlette (Scarlette) ★☆☆☆☆ -
普通会员
2022/6/12 11:35:31
19楼: 这个事情实际上要分两类的:

1、必须有返回码的API。这类API的设计原则是不抛异常(Access Violation可以是个例外),但是做完所有的检查,因此需要非常详细的返回码,让上层逻辑可以处理。

2、不依赖返回码的API。注意此类API的设计原则是要分Configuration的,通常DEBUG版本会检查所有的参数,一但有不对就抛异常;RELEASE版本为了执行效率,则不做任何执行前检查,执行时导致的异常直接透过。这样设计的目的是逼迫上层调用代码在调用前考虑好所有因素,并在测试中尽早暴露错误。
----------------------------------------------
-
信息
登陆以后才能回复
Copyright © 2CCC.Com 盒子论坛 v2.1 版权所有 页面执行39.0625毫秒 RSS