Reputation: 937
Currently I have many records with helper methods like this:
TRec1 = packed record
S1: string;
S2: string;
procedure FromJSON(const AJSON: string);
function ToJSON: string;
end;
procedure TRec1.FromJSON(const AJSON: string);
begin
RecordLoadJSON(Self, StringToUTF8(AJSON), TypeInfo(TRec1));
end;
function TRec1.ToJSON: string;
begin
Result := UTF8ToString(RecordSaveJSON(Self, TypeInfo(TRec1)));
end;
TRec2 = packed record
S1: string;
I1: string;
procedure FromJSON(const AJSON: string);
function ToJSON: string;
end;
procedure TRec2.FromJSON(const AJSON: string);
begin
RecordLoadJSON(Self, StringToUTF8(AJSON), TypeInfo(TRec2));
end;
function TRec2.ToJSON: string;
begin
Result := UTF8ToString(RecordSaveJSON(Self, TypeInfo(TRec2)));
end;
As you can see all records contain the same methods ToJSON and FromJSON. This methods contain completely the same code except TypeInfo()
Any way to use generics for this and do not declate this methods for every records?
Upvotes: 3
Views: 1258
Reputation: 76567
Records or classes?
Even though Delphi has support for methods, records are still 2nd class citizens because there is no support for record inheritance.
Classes have all the features records have plus the full set of OOP features, so they are often a better choice. However you need to deal with the reference semantics that requires managing the creation and destruction of classes manually on non-ARC platforms vs the care-free value semantics of records that have automatic clean-up.
Non generics solution (using a class)
Declare the string containers as a class and use inheritance.
TRec1 = class(TPersistent)
S1: string;
S2: string;
procedure FromJSON(const AJSON: string);
function ToJSON: string;
end;
TRec2 = class(TRec1)
end;
procedure TRec1.FromJSON(const AJSON: string);
begin
RecordLoadJSON(Self, StringToUTF8(AJSON), Self.ClassInfo);
end;
function TRec1.ToJSON: string;
begin
Result := UTF8ToString(RecordSaveJSON(Self, Self.ClassInfo));
end;
All classes inherited from TPersistent
have RTTI and thus the self.classinfo
works.
You may need to revise the RecordSaveJSON
call depending on your needs.
Generics solution
If you have RTTI enabled for your unit {$M+}
and you must use records, simply use methods outside of the record and feed the record type as a generic parameter.
TRec1 = record
data: typex;
....
end;
TDoJSONThings = record //helper record for any and all types with RTTI.
procedure FromJSON<T:record>(var Data: T; const JSON: string); static;
function ToJSON<T:record>(const [ref] Data: T): string; static;
end;
procedure TDOJSONThings.FromJSON<T>(var Data: T; const JSON: string); static;
var
pt: PTypeInfo;
begin
pt:= TypeInfo(T);
RecordLoadJSON(Data, StringToUTF8(AJSON), pt);
end;
function TDOJSONThings.ToJSON<T:record>(const [ref] Data: T): string; static;
var
pt: PTypeInfo;
begin
pt:= TypeInfo(T);
Result:= UTF8ToString(RecordSaveJSON(Data, pt));
end;
Remarks
Using a record helper
(as in THelper = record helper for TRec1
) will not work, because that will only work for TRec1
and records sadly do not support inheritance.
It would have been better to use stand-alone methods, but Delphi does not allow generic methods that are not part of a record or class.
Oh and please drop the 1970's packed record
style construct. It serves no purpose but to make your code slower by misaligning it.
Upvotes: 2