Given a Record:
MyRecord = record
Company: string;
Address: string;
NumberOfEmplyees: integer;
can you write a function call like
function UpdateField(var FieldName: string; FieldValue: variant): bool;
so that:
UpdateField('Company', 'ABC Co');
would update MyRecord.Company to 'ABC Co'?
I looked for an example but everything I found is for a database. Any help pointing me in the right direction is appreciated.
Thanks, Charles
From Setup, enter Field Updates in the Update box, and select Field Updates. Then use these settings to configure your field update. Before you begin, check the type of the field you want to update. Read-only fields like formula or auto-number fields are not available for field updates.
Identify Salesforce records to update, and set the values to change in those records. To do so, use the IDs and field values stored in a record variable or record collection variable, or use specify conditions to identify the records and set the field values individually.
To update a single field, select it (or click anywhere in it) and press F9, or right-click the field and choose Update Field. If you have toggled on the display of the field code string, updating the field toggles it back to displaying the results.
What Delphi 7 RTTI knows, and can be retrieved from TypeInfo(aRecordType)
, is:
The latest information is necessary to free the memory used by each reference-counted variables inside the record, or copy the record content, at run-time. The initialization of the record is also performed either in compiler-generated code (if the record is created on the stack), either via _InitializeRecord()
method, either with a global fill to 0 when a class or a dynamic array is instanciated.
It's the same for both record
and object
types, in all version of Delphi.
You can note that there is a bug in modern version of Delphi (including Delphi 2009 and 2010 at least), which sometimes don't create the code for initializing objects on stack. You'll have to use record instead, but it will break compatibility with previous version of Delphi. :(
Here are the structure used for storing this RTTI data:
type
TFieldInfo = packed record
TypeInfo: ^PDynArrayTypeInfo; // information of the reference-counted type
Offset: Cardinal; // offset of the reference-counted type in the record
end;
TFieldTable = packed record
Kind: byte;
Name: string[0]; // you should use Name[0] to retrieve offset of Size field
Size: cardinal; // global size of the record = sizeof(aRecord)
Count: integer; // number of reference-counted field info
Fields: array[0..0] of TFieldInfo; // array of reference-counted field info
end;
PFieldTable = ^TFieldTable;
Using this data, here is for instance what you can do:
For instance, here is how two records of the same type can be compared, using this RTTI:
/// check equality of two records by content
// - will handle packed records, with binaries (byte, word, integer...) and
// string types properties
// - will use binary-level comparison: it could fail to match two floating-point
// values because of rounding issues (Currency won't have this problem)
function RecordEquals(const RecA, RecB; TypeInfo: pointer): boolean;
var FieldTable: PFieldTable absolute TypeInfo;
F: integer;
Field: ^TFieldInfo;
Diff: cardinal;
A, B: PAnsiChar;
begin
A := @RecA;
B := @RecB;
if A=B then begin // both nil or same pointer
result := true;
exit;
end;
result := false;
if FieldTable^.Kind<>tkRecord then
exit; // raise Exception.CreateFmt('%s is not a record',[Typ^.Name]);
inc(PtrUInt(FieldTable),ord(FieldTable^.Name[0]));
Field := @FieldTable^.Fields[0];
Diff := 0;
for F := 1 to FieldTable^.Count do begin
Diff := Field^.Offset-Diff;
if Diff<>0 then begin
if not CompareMem(A,B,Diff) then
exit; // binary block not equal
inc(A,Diff);
inc(B,Diff);
end;
case Field^.TypeInfo^^.Kind of
tkLString:
if PAnsiString(A)^<>PAnsiString(B)^ then
exit;
tkWString:
if PWideString(A)^<>PWideString(B)^ then
exit;
{$ifdef UNICODE}
tkUString:
if PUnicodeString(A)^<>PUnicodeString(B)^ then
exit;
{$endif}
else exit; // kind of field not handled
end;
Diff := sizeof(PtrUInt); // size of tkLString+tkWString+tkUString in record
inc(A,Diff);
inc(B,Diff);
inc(Diff,Field^.Offset);
inc(Field);
end;
if CompareMem(A,B,FieldTable.Size-Diff) then
result := true;
end;
So for your purpose, what Delphi 7 RTTI could let you know at runtime, is the position of every string within a record. Using the code above, you could easily create a function using the field index:
procedure UpdateStringField(StringFieldIndex: integer; const FieldValue: string);
But you simply don't have the needed information to implement your request:
If you really need this feature, the only solution under Delphi 7 is to use not records, but classes.
On Delphi 7, if you create a class with published fields, you'll have all needed information for all published fields. Then you can update such published field content. This is what the VCL runtime does when unserializing the .dfm content into class instances, or with an ORM approach.
You need modern versions of Delphi to do what you ask for without resorting to manually coding the lookups, e.g. via a table.
The updated RTTI introduced in Delphi 2010 can support what you are looking for, but there's nothing in Delphi 7 that will do this for records.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With