Is it possible to have a get and set value for TMyRecord when you have the name of the record member? something similar to RTTI
.
I cannot use an array as the members may have different data types.
type
TMyRecord = record
X: Integer;
Y: Integer;
Z: DateTime;
end;
var MyRecord: TMyRecord;
procedure UpdateValue(aRecordMemberName: string; AValue: Integer);
begin
MyRecord[aRecordmemberName] := AValue;
end;
function GetValue(aRecordMemberName: string): Integer;
begin
Result := MyRecord[aRecordmemberName];
end;
procedure Main();
begin
SetValue('X', 5);
showmessage( GetValue('Y').ToString );
end;
On an additional note, is it possible to iterate through all members of a Record
, similar to iterating through TFields
or TFieldDefs
?
thanks.
- Using Delphi 11 in Firemonkey
CodePudding user response:
If you have a fixed number of fields of different types, it is somewhat strange that you need to access these by string names. Still, let's assume this is the right thing to do.
RTTI is a bit complicated (meaning that you need to write "many" lines of code) and rather slow. Sure, it will probably be fast enough in your case, so it will probably be good enough. But it isn't ideal.
In my experience, people are often too eager to resort to RTTI. In most cases, there are better solutions.
One non-RTTI solution would be to use a TDictionary<string, Variant>
.
Another would be like this:
type
EFrogException = class(Exception);
TFrogProperty = (fpName, fpBirthDate, fpWeight);
TFrogPropertyHelper = record helper for TFrogProperty
strict private
const PropNames: array[TFrogProperty] of string = ('Name', 'Birth date', 'Weight');
public
function ToString: string;
class function FromString(const APropName: string): TFrogProperty; static;
end;
TFrog = record
strict private
FProperties: array[TFrogProperty] of Variant;
private
function GetProp(Prop: TFrogProperty): Variant;
procedure SetProp(Prop: TFrogProperty; const Value: Variant);
function GetPropByName(APropName: string): Variant;
procedure SetPropByName(APropName: string; const Value: Variant);
public
property Prop[Prop: TFrogProperty]: Variant read GetProp write SetProp;
property PropByName[Prop: string]: Variant read GetPropByName write SetPropByName; default;
end;
where
{ TFrogPropertyHelper }
class function TFrogPropertyHelper.FromString(
const APropName: string): TFrogProperty;
begin
for var Prop := Low(TFrogProperty) to High(TFrogProperty) do
if SameText(Prop.ToString, APropName) then
Exit(Prop);
raise EFrogException.CreateFmt('Invalid frog property: "%s".', [APropName]);
end;
function TFrogPropertyHelper.ToString: string;
begin
if InRange(Ord(Self), Ord(Low(TFrogProperty)), Ord(High(TFrogProperty))) then
Result := PropNames[Self]
else
Result := '';
end;
{ TFrog }
function TFrog.GetProp(Prop: TFrogProperty): Variant;
begin
Result := FProperties[Prop];
end;
function TFrog.GetPropByName(APropName: string): Variant;
begin
Result := Prop[TFrogProperty.FromString(APropName)];
end;
procedure TFrog.SetProp(Prop: TFrogProperty; const Value: Variant);
begin
FProperties[Prop] := Value;
end;
procedure TFrog.SetPropByName(APropName: string; const Value: Variant);
begin
Prop[TFrogProperty.FromString(APropName)] := Value;
end;
Then you can do things like this:
procedure TForm1.FormCreate(Sender: TObject);
begin
var James: TFrog;
James['Name'] := 'James';
James['Birth date'] := EncodeDate(2016, 05, 10);
James['Weight'] := 2.4;
ShowMessage(James['Name']);
James['Name'] := 'Sir James';
ShowMessage(James['Name']);
// And you can still be type safe if you want to:
James.Prop[fpName] := 'Sir James Doe';
ShowMessage(James.Prop[fpName]);
end;