Delphi中如何简化多层对象Assigned()链式空检查?
简化Delphi深层对象链式空检查的方案
首先得明确:你尝试直接写 if Assigned(ASomeObj.Session.UserInfo.SomeFactsList) then 是不可行的——只要中间任何一个对象(比如ASomeObj、Session、UserInfo)是nil,访问其属性时会直接触发访问违规(AV)导致程序崩溃,所以必须逐层验证每个环节的对象是否存在。
下面给你几个实用的简化方案,兼顾代码简洁性和可读性:
1. 利用Delphi 11+的空安全特性(最推荐)
从Delphi 11 Alexandria开始,官方引入了类似C#的?.空传播操作符,完美解决这个问题!你可以直接写出和C#几乎一样的代码:
if Assigned(ASomeObj?.Session?.UserInfo?.SomeFactsList) then begin // 执行操作 end;
或者更简洁的判断nil:
if ASomeObj?.Session?.UserInfo?.SomeFactsList <> nil then begin // 执行操作 end;
这个语法会自动逐层检查每个对象是否为nil,只要某一层是nil,整个表达式就会返回nil,不会触发AV,代码清晰到一眼就能看懂。
2. 自定义链式检查辅助函数(适配旧版本Delphi)
如果你的项目还在用Delphi 11以下的版本,可以写一个通用的辅助函数来封装逐层检查逻辑:
function ChainAssigned(const AStartObj: TObject; const ASelectors: array of TFunc<TObject, TObject>): Boolean; var CurrentObj: TObject; Selector: TFunc<TObject, TObject>; begin CurrentObj := AStartObj; Result := Assigned(CurrentObj); if not Result then Exit; // 遍历每个选择器,逐层检查对象 for Selector in ASelectors do begin CurrentObj := Selector(CurrentObj); Result := Assigned(CurrentObj); if not Result then Break; end; end;
调用的时候,用匿名方法指定每层的属性访问:
if ChainAssigned(ASomeObj, [function(O: TObject): TObject begin Result := TSomeObj(O).Session; end, function(O: TObject): TObject begin Result := TSession(O).UserInfo; end, function(O: TObject): TObject begin Result := TUserInfo(O).SomeFactsList; end]) then begin // 执行操作 end;
虽然比?.多写一点,但比原来一长串Assigned()嵌套要整洁,而且这个函数可以在整个项目里复用。
3. 封装检查逻辑到类方法(提升可读性)
如果这些对象是你自己定义的业务类,最好的做法是把检查逻辑封装到各自的类中,让代码更符合面向对象的单一职责原则:
// 在TSomeObj类中添加方法 function TSomeObj.HasValidSomeFactsList: Boolean; begin Result := Assigned(Session) and Session.HasValidUserInfo; end; // 在TSession类中添加方法 function TSession.HasValidUserInfo: Boolean; begin Result := Assigned(UserInfo) and UserInfo.HasValidSomeFactsList; end; // 在TUserInfo类中添加方法 function TUserInfo.HasValidSomeFactsList: Boolean; begin Result := Assigned(SomeFactsList); end;
之后主逻辑里的检查就变得异常简洁:
if Assigned(ASomeObj) and ASomeObj.HasValidSomeFactsList then begin // 执行操作 end;
这种方式不仅简化了主代码,还把检查逻辑分散到各个类中,后续维护时如果对象结构变化,只需要修改对应类的检查方法即可。
额外的可读性建议
- 不管用哪种方案,尽量把长检查逻辑从主业务代码中抽离出来,让主逻辑专注于业务操作;
- 如果是临时的简单检查,也可以把逐层检查拆成多行对齐,比如:
换行对齐后,可读性也会比一行到底好很多。// 检查所有中间对象是否存在 if Assigned(ASomeObj) and Assigned(ASomeObj.Session) and Assigned(ASomeObj.Session.UserInfo) and Assigned(ASomeObj.Session.UserInfo.SomeFactsList) then begin // 执行操作 end;
内容的提问来源于stack exchange,提问作者Andrew
相关产品推荐
相关产品推荐

