如何在Delphi中获取传入参数的详细信息?
Delphi实现可识别参数详情的GetInfo函数
需求说明
需要实现一个通用的GetInfo函数,能够获取传入参数的名称、类型、值,对于对象属性还需完整解析属性链的层级信息,示例调用及预期输出如下:
var s: string; begin s := 'Hi'; ShowMessage(GetInfo(s)); // Expected output: s: string = 'Hi' ShowMessage(GetInfo(Label1.Caption)); // Expected output: (Label1: TLabel).Caption: string = 'First name:' ShowMessage(GetInfo(pb1.Position)); // Expected output: (pb1: TProgressBar).Position: Integer = 70 ShowMessage(GetInfo(Edit1.Font.Color)); // Expected output: ((Edit1: TEdit).Font: TFont).Color: TColor = 16729702 end;
实现思路
普通RTTI无法直接获取参数的原始名称和属性调用链——因为Delphi编译时会将变量/属性调用转换为内存地址,丢失符号信息。要实现需求,需结合两种技术:
- 编译时内置函数:利用Delphi XE8+支持的
NameOf函数,在编译阶段获取变量/属性的字符串名称; - RTTI运行时解析:获取变量/属性的类型信息、值,以及对象属性的层级类型。
完整示例代码
核心实现单元
unit InfoUtils; interface uses System.SysUtils, System.Rtti, System.TypInfo, System.Classes; function GetInfo(const AExpr: string; const AValue: TValue): string; overload; function GetInfo<T>(const AExpr: string; const AValue: T): string; overload; // 简化调用的宏(Delphi XE8+) {$DEFINE GetInfoExpr(X) := GetInfo(NameOf(X), X)} implementation function GetValueStr(const AValue: TValue): string; var LType: TRttiType; LIntf: IInterface; begin if AValue.IsEmpty then Exit('(empty)'); case AValue.Kind of tkUnknown: Result := 'unknown'; tkInteger: Result := IntToStr(AValue.AsInteger); tkChar: Result := QuotedStr(AValue.AsChar); tkEnumeration: begin LType := TRttiContext.Create.GetType(AValue.TypeInfo); Result := LType.GetName(AValue.AsOrdinal); // 枚举值同时输出对应数值 if LType is TRttiEnumerationType then Result := Format('%s (%d)', [Result, AValue.AsOrdinal]); end; tkFloat: Result := FloatToStr(AValue.AsExtended); tkString, tkUString, tkLString, tkWString: Result := QuotedStr(AValue.AsString); tkSet: Result := SetToString(AValue.TypeInfo, AValue.AsOrdinal); tkClass: if AValue.AsObject = nil then Result := 'nil' else Result := Format('(%s: %s)', [AValue.AsObject.Name, AValue.AsObject.ClassName]); tkClassRef: Result := AValue.AsClass.ClassName; tkInterface: begin LIntf := AValue.AsInterface; if LIntf = nil then Result := 'nil' else Result := LIntf.ToString; end; tkDynamicArray: Result := 'dynamic array'; tkArray: Result := 'array'; tkRecord: Result := 'record'; tkMethod: Result := 'method'; tkPointer: Result := Format('0x%p', [AValue.AsPointer]); tkProcedure: Result := 'procedure'; end; end; function ParseExprAndGetValue(const AExpr: string; const AValue: TValue): string; var LContext: TRttiContext; LType: TRttiType; LObj: TObject; LProp: TRttiProperty; LProps: TArray<string>; I: Integer; LCurrentValue: TValue; LCurrentExpr: string; begin LProps := AExpr.Split(['.']); LCurrentValue := AValue; LCurrentExpr := ''; if Length(LProps) = 1 then begin // 处理普通变量 LType := LContext.GetType(LCurrentValue.TypeInfo); Result := Format('%s: %s = %s', [LProps[0], LType.Name, GetValueStr(LCurrentValue)]); Exit; end; // 解析多层属性链 LObj := LCurrentValue.AsObject; LCurrentExpr := Format('(%s: %s)', [LProps[0], LObj.ClassName]); for I := 1 to High(LProps) do begin LType := LContext.GetType(LObj.ClassType); LProp := LType.GetProperty(LProps[I]); if not Assigned(LProp) then begin Result := Format('%s.%s: (unknown property)', [LCurrentExpr, LProps[I]]); Exit; end; LCurrentValue := LProp.GetValue(LObj); if I = High(LProps) then begin // 处理最终属性 Result := Format('%s.%s: %s = %s', [LCurrentExpr, LProps[I], LProp.PropertyType.Name, GetValueStr(LCurrentValue)]); end else begin // 处理中间对象属性 if LCurrentValue.IsObject then begin LObj := LCurrentValue.AsObject; LCurrentExpr := Format('%s.%s: %s', [LCurrentExpr, LProps[I], LObj.ClassName]); end else begin Result := Format('%s.%s: %s = %s', [LCurrentExpr, LProps[I], LProp.PropertyType.Name, GetValueStr(LCurrentValue)]); Exit; end; end; end; end; function GetInfo(const AExpr: string; const AValue: TValue): string; begin Result := ParseExprAndGetValue(AExpr, AValue); end; function GetInfo<T>(const AExpr: string; const AValue: T): string; begin Result := ParseExprAndGetValue(AExpr, TValue.From<T>(AValue)); end; end.
调用示例
uses InfoUtils; var s: string; begin s := 'Hi'; ShowMessage(GetInfoExpr(s)); // 输出: s: string = 'Hi' ShowMessage(GetInfoExpr(Label1.Caption)); // 输出: (Label1: TLabel).Caption: string = 'First name:' ShowMessage(GetInfoExpr(pb1.Position)); // 输出: (pb1: TProgressBar).Position: Integer = 70 ShowMessage(GetInfoExpr(Edit1.Font.Color)); // 输出: ((Edit1: TEdit).Font: TFont).Color: TColor = clBlue (或对应数值) end;
关键说明
NameOf函数:Delphi XE8及以上版本支持,是获取参数原始名称的核心;若使用更早版本,可通过自定义编译宏解析符号名称,但复杂度会显著提升。- RTTI解析:通过
TRttiContext获取类型信息与属性值,针对不同数据类型做格式化处理(如字符串加引号、枚举值同时显示名称和数值)。 - 属性链处理:拆分属性调用的
.分隔符,逐层解析对象属性,构建完整的层级描述字符串。
内容的提问来源于stack exchange,提问作者Mahan
相关产品推荐
相关产品推荐

