You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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;

关键说明

  1. NameOf函数:Delphi XE8及以上版本支持,是获取参数原始名称的核心;若使用更早版本,可通过自定义编译宏解析符号名称,但复杂度会显著提升。
  2. RTTI解析:通过TRttiContext获取类型信息与属性值,针对不同数据类型做格式化处理(如字符串加引号、枚举值同时显示名称和数值)。
  3. 属性链处理:拆分属性调用的.分隔符,逐层解析对象属性,构建完整的层级描述字符串。

内容的提问来源于stack exchange,提问作者Mahan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 01:33:28