【问题标题】:Get the offset of a field in a delphi record at runtime在运行时获取delphi记录中字段的偏移量
【发布时间】:2010-07-02 08:44:40
【问题描述】:

给定一个记录类型:

TItem = record
   UPC : string[20];
   Price : Currency;
   Cost : Currency;
   ...
end; 

字段的名称作为字符串,我怎样才能得到该字段在记录中的偏移量?我需要在运行时执行此操作 - 要访问的字段的名称在运行时决定。

例子:

var
   pc : Integer;
   fieldName : string;
   value : Currency;
begin
   pc := Integer(@item);                    // item is defined and filled elsewhere
   fieldName := Text1.Text;                 // user might type 'Cost' or 'Price' etc
   Inc(pc, GetItemFieldOffset(fieldName));  // how do I implement GetItemFieldOffset?
   value := PCurrency(pc)^;
   ..

我正在使用 Delphi 7。

【问题讨论】:

  • @joe 是的,我知道它的味道。我实际上并没有将文本框绑定到此;这只是我能想到的最简单的例子来说明这个问题。它实际上是一种规则引擎的一部分,它执行可以通过名称引用数据库字段的命令。问题是在执行此代码时,相关字段已经加载到记录中。
  • 最后我使用了手动维护的 if-else,完全删除了指针,只是将 name : string 映射到 value : currency

标签: delphi pointers struct delphi-7 rtti


【解决方案1】:

你不能。 Delphi 7 不会为记录发出 RTTI。还有其他选项(如前面的答案所示),但需要手动映射“字段名称”->“偏移量”。

【讨论】:

    【解决方案2】:

    以下内容适用于您的简化场景,但我怀疑是否有可能为这种事情创建一个通用函数。

    我能想到的最好的方法是添加某种注册对象,但它仍然需要您注册所有需要偏移的记录。

    function GetItemFieldOffset(const Value: string): Integer;
    var
      item: TItem;
    begin
      if Value = 'UPC' then Result := 0
      else if Value = 'Price' then Result := Integer(@item.Price) - Integer(@item)
      else if Value = 'Cost' then Result :=  Integer(@item.Cost) - Integer(@item)
      else raise Exception.CreateFmt('Unhandled condition (%0:s)', [Value]);
    end;
    

    【讨论】:

      【解决方案3】:

      正如 alex 所说,Delphi 7 不会为记录发出 RTTI,因此您无法在运行时检索所需的信息。但是,在更高版本(Delphi 2010+)中它确实如此,并且以下代码:

      TItem = record
         UPC : string[20];
         Price : Currency;
         Cost : Currency;
      //...
      end;
          var
             rttiContext: TRttiContext;
             rttiType: TRttiType;
             fields: TArray<TRttiField>;
             item: TItem;
          begin
              rttiType := rttiContext.GetType(TypeInfo(TItem));
              caption := rttiType.Name + ' {';
              fields := rttiType.GetFields;
              for i := low(fields) to high(fields) do
              begin
                caption := caption +'{name='+fields[i].Name+',';
                caption := caption +'offset='+IntToStr(fields[i].Offset)+'}';
              end;
              caption := caption + '}';
      

      将产生 'TItem {{name=UPC,offset=0}{name=Price,offset=24}{name=Cost,offset=32}}'

      您还可以使用以下方法在特定实例中设置字段值(尽管您实际上也应该验证类型):

      if fields[i].Name = 'Price' then
        fields[i].SetValue(@item, 10);
      

      【讨论】:

      • 对。换句话说,RTTI 会自动且通用地为所有类型执行您可以为自己专门且最有效地仅针对需要它的类型执行的操作。
      【解决方案4】:

      这就是你要找的东西

       type
         TItem = record
           UPC : string[20];
           Price : Currency;
           Cost : Currency;
           ...
         end; 
      
       var
         myRecord    : TItem ;
         myRecordPtr : ^TItem ;
      
       begin
         myRecord.price:= 100;
         myRecord.UPC := '111';
         myRecordPtr := @myRecord;
         if edit1.text = 'UPC' then   
           ShowMessage(myRecordptr.UPC);  // Displays '111'
         else if edit1.text = 'price' then   
           ShowMessage(myRecordptr.Price);  // Displays '100'
       end;
      

      【讨论】:

      • 不,我有一个像“UPC”这样的字符串。我想使用反射或 delphi 等效项来获取字符串名称的字段的值。
      • 字符串可以是“UPC”,也可以是“Price”或其他任何东西——直到运行时我才知道。
      • 我希望我理解错了。但是 ShowMessage(myRecordptr.Price) 会显示 100。所以您只需将 UPC 替换为价格即可。
      • 我不能在 runtime 这样做。我有一种情况,用户将在文本框中输入“价格”或“UPC”或其他任何内容,然后我必须显示该字段的值(简化场景)。
      • 但在这一行 Inc(pc, GetItemFieldOffset('Cost'));您正在指定成本。您的意思是直到运行时您才知道是否在此处指定成本或价格
      猜你喜欢
      • 2013-01-05
      • 1970-01-01
      • 1970-01-01
      • 2022-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-19
      • 1970-01-01
      相关资源
      最近更新 更多