在运行时获取delphi记录中字段的偏移量

发布于 2024-09-08 03:08:57 字数 639 浏览 8 评论 0原文

给定记录类型:

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。

Given a record type:

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

And the name of a field as a string, how can I get the offset of that field within the record? I need to do this at runtime - the name of the field to access is decided at runtime.

Example:

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)^;
   ..

I'm using Delphi 7.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

花开柳相依 2024-09-15 03:08:57

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

You can't. Delphi 7 does not emit RTTI for records. There are other options (as seen the previous answers) but those require manual mapping of "Field Name" -> "Offset".

一影成城 2024-09-15 03:08:57

正如 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);

As alex said, Delphi 7 doesn't emit RTTI for records, so you can't retrieve the required info at runtime. However, in later versions (Delphi 2010+) it does, and the following code:

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 + '}';

will produce 'TItem {{name=UPC,offset=0}{name=Price,offset=24}{name=Cost,offset=32}}'

You can also set the field value in a particular instance (although you should really also verify the type) using:

if fields[i].Name = 'Price' then
  fields[i].SetValue(@item, 10);
泪之魂 2024-09-15 03:08:57

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

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

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;

Following would work for your simplified scenario but I doubt it will be possible to make a generic function for this kind of thing.

The best I can think if is to add some kind of registration object but it still would require you to register all the records you need an offset from.

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;
扮仙女 2024-09-15 03:08:57

这是您要找的吗

 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;

Is this what you are looking for

 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;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文