how to read/write set enumeration from text file with delphi - delphi

I want to use SQL like condition in Delphi as
if VarI in SOMESET then
...
[SOMESET] to read from any text file.
Store few numbers in any text file like ini/txt and read it from file and replace it with set so that we can use in operator with if condition.

If I understand what you are asking correctly, the simple answer is yes. Your terminology may be a bit off - set means something quite specific in Delphi, but I don't think that is what you mean. Also, I don't think you are asking about the specifics of loading and saving so I have not included that. Instead I think you are asking how to allow 'in' to be defined in this specific case, and here is how
unit Unit1;
interface
uses
System.SysUtils;
type
TMySet = record
private
function GetEntry(const i: integer): integer;
public
Entries : array of integer;
// procedure LoadFromFile( const pFromFile : TFileName );
class operator in ( const pTest : integer; pMyset : TMyset ) : boolean;
property Entry[ const i : integer ] : integer
read GetEntry; default;
end;
implementation
{ TMySet }
function TMySet.GetEntry(const i: integer): integer;
begin
Result := Entries[ i ]; // allow default exceptions to occur
end;
class operator TMySet.in(const pTest: integer; pMyset: TMyset): boolean;
var
i: Integer;
begin
for i := Low(pMyset.Entries) to High(pMyset.Entries) do
begin
if i = pMyset[ i ] then
begin
Result := TRUE;
exit;
end;
end;
// else
Result := FALSE;
end;
end.
I hope I have understood your question, and that this helps.

Related

TClientDataSet Custom compare field function

I am using an in-memory TClientDataSet with a TStringField column which contains folders path (Delphi 7).
When I create an index on this column the order is not what I am looking for.
As an example I get :
c:\foo
c:\fôo\a
c:\foo\b
when I would like this order :
c:\foo
c:\foo\b
c:\fôo\a
So I searched a way to use my own compare field function.
Based on this RRUZ answer How to change the implementation (detour) of an externally declared function I tried the following :
type
TClientDataSetHelper = class(DBClient.TClientDataSet);
...
MyCDS : TClientDataSet;
...
// My custom compare field function
function FldCmpHack
(
iFldType : LongWord;
pFld1 : Pointer;
pFld2 : Pointer;
iUnits1 : LongWord;
iUnits2 : LongWord
): Integer; stdcall;
begin
// Just to test
Result := -1;
end;
...
---RRUZ code here---
...
procedure HookDataCompare;
begin
HookProc
(
(MyCDs as TClientDataSetHelper).DSBase.FldCmp, <== do not compile !!!
#FldCmpHack,
FldCmpBackup
);
end;
When I try to compile I get an error (MyCDs as TClientDataSetHelper).DSBase.FldCmp : not enough actual parameters
I do not understand why this does not compile. Could you please help me ?
Is it even possible to "detour" IDSBase.FldCmp in DSIntf.pas ? Am i totally wrong ?
Thank you
EDIT
Finally, thanks to Dsm answer, I transformed the TStringFieldcolumn into a TVarBytesField in order to avoid doubling the buffer. Plus, when a TVarBytesField is indexed the order is based on the bytes value so I get the order I want. For having all child folders after a parent folder and before the next parent folder (c:\foo.new after c:\foo\b), I patched TVarBytesFieldlike this :
TVarBytesField = class(DB.TVarBytesField)
protected
function GetAsString: string; override;
procedure GetText(var Text: string; DisplayText: Boolean); override;
procedure SetAsString(const Value: string); override;
end;
function TVarBytesField.GetAsString: string;
var
vBuffer : PAnsiChar;
vTaille : WORD;
vTexte : PAnsiChar;
vI : WORD;
begin
Result := '';
GetMem(vBuffer, DataSize);
try
if GetData(vBuffer) then
begin
vTaille := PWORD(vBuffer)^;
vTexte := vBuffer + 2;
SetLength(Result, vTaille);
for vI := 1 to vTaille do
begin
if vTexte^ = #2 then
begin
Result[vI] := '\';
end
else
begin
Result[vI] := vTexte^;
end;
Inc(vTexte);
end;
end;
finally
FreeMem(vBuffer);
end;
end;
procedure TVarBytesField.GetText(var Text: string; DisplayText: Boolean);
begin
Text := GetAsString;
end;
procedure TVarBytesField.SetAsString(const Value: string);
var
vBuffer : PAnsiChar;
vTaille : WORD;
vTexte : PAnsiChar;
vI : WORD;
begin
vBuffer := AllocMem(DataSize);
try
vTaille := WORD(Length(Value));
PWORD(vBuffer)^ := vTaille;
vTexte := vBuffer + 2;
for vI := 1 to vTaille do
begin
if Value[vI] = '\' then
begin
vTexte^ := #2
end
else
begin
vTexte^ := Value[vI];
end;
Inc(vTexte);
end;
SetData(vBuffer);
finally
FreeMem(vBuffer);
end;
end;
The message is telling you that FldCmp is a function, and it is expecting you to execute it, but it has not got enough parameters. I am sure that you already realised that and probably already tried to get the address of the function with the # (like you do for FldCmpHack) and found that that does not work.
The reason for that is, I am afraid, that FldCmp is not a normal function. DSBase is actually an interface, which will have been assigned (looking at the source code) by a class factory. What you actually need is the real function itself and for that you need the real object that the class factory creates. And I am sorry, but I can't see any realistic way of doing that.
However, the DSBase field is only created if it has not been assigned, so you could, in theory, create your own IDSBase interface object, which is the way this type of problem is meant to be handled. That is a lot of work, though, unless you know class that the class factory produces and can descend from that.
A sneakier alternative is to override the Translate property and create some sort of hash (perhaps by translating the ASCII codes to their HEX values) so that the database keeps them in the right order
TClientDataSetHelper = class(TClientDataSet)
public
function Translate(Src, Dest: PAnsiChar; ToOem: Boolean): Integer; override;
end;

Reading/writing dynamic arrays of objects to a file - Delphi

Im trying to write some code which will read/write a dynamic array of objects to a file. The objects represent the structure of the java source code. I need to be able to scan the whole source code and gather information on Fields, Methods and Classes. I have an algorithm which does this and the result is kept in a structure of TFieldStruc, TMethodStruc and TClassStruc, all descendants of the TCoreStruc (a descendant of TObject). The java source code takes a couple of minutes to be scanned and have the virtual structure generated. Because of this my application scans all the source code once and saves it into a much more accessible format which is loaded when ever the IDE launches.
Is there a way (other than exporting the objects 'to string' and then re-creating them again when they are loaded) to stream the entire three arrays of TFieldStruc, TMethodStruc and TClassStruc, to a file so they can be read later?
I have tried reading and writing to a 'File of TFieldStruc..' and the TFileStream to save the objects to a file and read them back, but in both cases I get 'inaccessible value' in the debugger followed by an 'Access Violation' error as soon as the object is accessed again. If anyone has ideas on how to solve this problem it would be appreciated. Below is the code to TCodeStruc if any of its fields/methods may be causing issues:
type
TCoreStruc = class(TObject)
public
LowerPointer : integer;
HigherPointer : integer;
Line : integer;
Word : integer;
Char : integer;
CoreType : ansistring;
IsPublic : boolean;
IsPrivate : boolean;
IsStatic : boolean;
IsFinal : boolean;
Name : ansistring;
NestedStruc : TCoreStruc;
constructor Create(Name, CoreType : ansistring; NestedStruc : TCoreStruc; IsPublic, IsPrivate, IsStatic, IsFinal : boolean);
procedure UpdateValues(NestedStruc : TCoreStruc; IsPublic, IsPrivate, IsStatic, IsFinal : boolean);
procedure SetPosition(Line, Word, Char : integer);
end;
Here is an example using your structure.
A few notes about this:
There are lots of different ways to go about this. Take David Heffernan's advice and do some searches for serialization. I use the approach include below in one of my applications but other include using RTTI/Persistent objects to iterate the published properties of an object. There are libraries that will iterate object for you and do all the work.
You need to actually write out to the string the sizes of dynamic objects. That includes things like arrays and strings.
In my example each object knows how to read and write itself to the stream.
I use a struct for the fixed length parts of the object. That saves me from needing to write each data element individually.
Strings need to be written on their own to include their size (you could used a fixed length buffer like Delphi short strings but it is not that much work to write out a regular string. You need to decide what type of format you want string data written in. I picked UTF8 for my application.
For your other arrays you can write their data (including length) after the first array is written out. Sometime there will be a header section that includes the lengths for all the dynamic sections at the top, others will write the length write before the structure starts. The key part is to always write things in the same order and included somewhere it can be re-read how many there are.
There is no error checking or verification in the file structure below. If anything is different between the read and write it will blow up - probably with a stream read error.
Any change to the structures will cause old files to not be read properly. There are a number of ways to version a file to ensure you can still read old formats. Not included here.
In your application you would pass a TFileStream to the read and write function. I like to write the actual read/write functions with just a TStream. Then the object does not care where the data is coming from. It could be file, or it could already be in memory.
If you drop the following unit into a console application you should be able to add a call to Main and step through the example.
unit CoreStruct;
interface
uses Classes, Types;
type
TCoreStructData = packed record
LowerPointer : integer;
HigherPointer : integer;
Line : integer;
Word : integer;
Char : integer;
IsPublic : boolean;
IsPrivate : boolean;
IsStatic : boolean;
IsFinal : boolean;
HasNested: boolean;
end;
TCoreStruc = class(TObject)
private
FCoreData: TCoreStructData;
FNestedStruc : TCoreStruc;
procedure SetNestedStruc(AValue: TCoreStruc);
public
CoreType : String;
Name : String;
constructor Create(); overload;
procedure WriteToStream(Stream: TStream);
procedure ReadFromStream(Stream: TStream);
//constructor Create(Name, CoreType : ansistring; NestedStruc : TCoreStruc; IsPublic, IsPrivate, IsStatic, IsFinal : boolean); overload;
//procedure UpdateValues(NestedStruc : TCoreStruc; IsPublic, IsPrivate, IsStatic, IsFinal : boolean);
//procedure SetPosition(Line, Word, Char : integer);
property LowerPointer: integer read FCoreData.LowerPointer write FCoreData.LowerPointer;
property HigherPointer: integer read FCoreData.HigherPointer write FCoreData.HigherPointer;
property Line: integer read FCoreData.Line write FCoreData.Line;
property Word: integer read FCoreData.Word write FCoreData.Word;
property Char: integer read FCoreData.Char write FCoreData.Char;
property NestedStruc: TCoreStruc read FNestedStruc write SetNestedStruc;
end;
procedure Main();
implementation
function ReadUTF8StringFromStream(const Stream: TStream): String;
var
n: Integer;
Buffer8: Utf8String;
begin
Result := '';
Stream.ReadBuffer(n, SizeOf(n));
if n = 0 then
Exit;
SetLength(Buffer8, n);
Stream.ReadBuffer(Pointer(Buffer8)^, n);
Result := String(Buffer8);
end;
procedure WriteUtf8StringToStream(const Data: String; Stream: TStream);
var
Buffer8: Utf8String;
n: Integer;
begin
// When writing strings we need to make sure the length of the
// string is written out to the stream. That goes first so the
// reader knows how long the buffer is.
//
// You could you different formats to write to the file depending on
// needs. I like using UTF8 when writing out to file, but it does
// require an extra buffer copy when turning it back into a native
// Delphi unicode string.
Buffer8 := Utf8String(Data);
n := Length(Buffer8);
Stream.WriteBuffer(n, SizeOf(n));
Stream.WriteBuffer(Pointer(Buffer8)^, n);
end;
procedure Main();
var
Structs: array of TCoreStruc;
ArraySize: integer;
DataStream: TMemoryStream;
ArraySize_A: integer;
Structs_A: array of TCoreStruc;
i: integer;
begin
// Create and write some data
SetLength(Structs, 3);
Structs[0] := TCoreStruc.Create();
Structs[0].HigherPointer := 1;
Structs[0].Name := 'Testing';
Structs[0].NestedStruc := TCoreStruc.Create();
Structs[0].NestedStruc.HigherPointer := 100;
Structs[1] := TCoreStruc.Create();
Structs[1].HigherPointer := 2;
Structs[2] := TCoreStruc.Create();
Structs[2].HigherPointer := 3;
DataStream := TMemoryStream.Create();
// We need to start with the count we are writing out so
// the reader knows how many times to loop.
ArraySize := Length(Structs);
DataStream.WriteBuffer(ArraySize, SizeOf(integer));
for i := 0 to ArraySize - 1 do
begin
Structs[i].WriteToStream(DataStream);
end;
// Read the data into a new set of objects
DataStream.Position := 0;
DataStream.ReadBuffer(ArraySize_A, SizeOf(integer));
SetLength(Structs_A, ArraySize_A);
for i := 0 to ArraySize_A - 1 do
begin
Structs_A[i] := TCoreStruc.Create();
Structs_A[i].ReadFromStream(DataStream);
end;
end;
{ TCoreStruc }
constructor TCoreStruc.Create;
begin
Self.LowerPointer := 0;
Self.HigherPointer := 0;
Self.Line := 0;
Self.Word := 0;
Self.Char := 0;
Self.NestedStruc := nil;
end;
procedure TCoreStruc.WriteToStream(Stream: TStream);
begin
Stream.WriteBuffer(FCoreData, SizeOf(TCoreStructData));
WriteUtf8StringToStream(Name, Stream);
WriteUtf8StringToStream(CoreType, Stream);
if FCoreData.HasNested = true then
begin
FNestedStruc.WriteToStream(Stream)
end;
end;
procedure TCoreStruc.ReadFromStream(Stream: TStream);
begin
Stream.ReadBuffer(FCoreData, SizeOf(TCoreStructData));
Name := ReadUtf8StringFromStream(Stream);
Name := ReadUtf8StringFromStream(Stream);
if FCoreData.HasNested = true then
begin
FNestedStruc := TCoreStruc.Create();
FNestedStruc.ReadFromStream(Stream);
end;
end;
procedure TCoreStruc.SetNestedStruc(AValue: TCoreStruc);
begin
FNestedStruc := AValue;
if AValue = nil then
FCoreData.HasNested := false
else
FCoreData.HasNested := true;
end;
end.

How to set value to a dynamic array using the rtti? [duplicate]

I have a question. I am a newbie with Run Time Type Information from Delphi 2010. I need to set length to a dynamic array into a TValue. You can see the code.
Type TMyArray = array of integer;
TMyClass = class
publihed
function Do:TMyArray;
end;
function TMyClass.Do:TMyArray;
begin
SetLength(Result,5);
for i:=0 to 4 Result[i]=3;
end;
.......
.......
......
y:TValue;
Param:array of TValue;
.........
y=Methods[i].Invoke(Obj,Param);//delphi give me a DynArray type kind, is working, Param works to any functions.
if Method[i].ReturnType.TypeKind = tkDynArray then//is working...
begin
I want to set length for y to 10000//i don't know how to write.
end;
I don't like Generics Collections.
TValue wasn't designed for arbitrary manipulation of its contents (it would have more helpers for e.g. setting record fields etc. if so), but rather for transporting values between concrete static types and dynamic RTTI. In this respect, TValue.SetArrayElement is an anomaly, and in hindsight, perhaps should not have been included. However, what you ask is possible:
uses Rtti;
type
TMyArray = array of Integer;
TMyClass = class
function Go: TMyArray;
end;
function TMyClass.Go: TMyArray;
var
i: Integer;
begin
SetLength(Result, 5);
for i := 0 to 4 do
Result[i] := 3;
end;
procedure P;
var
ctx: TRttiContext;
v: TValue;
len: Longint;
i: Integer;
begin
v := ctx.GetType(TMyClass).GetMethod('Go').Invoke(TMyClass.Create, []);
Writeln(v.ToString);
len := 10;
DynArraySetLength(PPointer(v.GetReferenceToRawData)^, v.TypeInfo, 1, #len);
Writeln(v.GetArrayLength);
for i := 0 to v.GetArrayLength - 1 do
Writeln(v.GetArrayElement(i).ToString);
end;
begin
P;
end.

Pass record parameter without declaring it first as a variable

If I am trying to call a procedure which has a record type (not object) as a parameter, is it possible to somehow pass details of that parameter "inline" without having to declare a variable of that type first?
eg assume I have this simple record type:
type TMyRecord = record
AString: string;
AnInt: Integer;
end;
and this procedure declaration:
procedure MyProcedure(Rec: TMyRecord);
If I want to call MyProcedure do I have to declare a variable of type TMyRecord or can I do something like:
MyProcedure(TMyRecord("Test", 10));
That doesn't work (XE2) (get a compiler error about it expecting a ")").
So, can I do something like that? Or not possible.
Thanks
It is possible using the advanced record structure.
For more information about advanced records, see the Records (advanced) section in Delphi help.
This is a small prototype to see how it works in your case to preinitialize a record in a function/procedure call :
Type
TRecord = record
AString : String;
AnInt : Integer;
Constructor Create( Const s : String; i : Integer);
end;
constructor TRecord.Create(const s: String; i: Integer);
begin
AString := s;
AnInt := i;
end;
procedure DoSomething( theRec : TRecord);
begin
WriteLn(theRec.AString, ' ',theRec.AnInt);
end;
begin
DoSomeThing( TRecord.Create('S',1));
ReadLn;
end.
Looking at the Delphi RTL, see the definitions of the record types TPoint and TRect in unit system.types (XE2).
They define some overloaded Create constructors, which are used in lots of places to preinitialize the record structures in function/procedure calls.
The question you are asking relates to code readability and there is a solution that avoids having to create a variable. The VCL uses this solution with the records TPoint and TRect.
Consider the definition of TPoint:
type
TPoint = record
X,Y integer
end;
To pass a TPoint to a procedure you might do:
var
MyPoint : TPoint;
begin
MyPoint.X := 5;
MyPoint.Y := 7;
DoSomething( MyPoint );
end;
This is fine but takes 3 lines when one is also possible using the factory function Point:
begin
DoSomething( Point(5,7) );
end;
In Delphi, a function has been declared as follows:
function Point( X, Y : integer ) : TPoint;
begin
Result.X := X;
Result.Y := Y;
end;
You can then call this function 'inline' to create the record 'on the fly' to to quickly
You will see the same has been provided for TRect etc. I often put such a factory function together with the record declaration as follows, even if I don't plan to use them yet:
type
TMyRecord = record
A : integer;
B : string;
end;
function MyRecord( A : integer; const B : string ) : TMyRecord;
begin
Result.A := A;
Result.B := B;
end;
Use of this technique can improved the readability of code and also ensures that you don't accidently omit setting a record element.
Just having fun with John Easley's idea:
type TRec = record
X: string;
Y: Integer;
end;
procedure TestRec(const Rec: array of const);
var
R: TRec;
begin
R.X:= string(Rec[0].VUnicodeString);
R.Y:= Rec[1].VInteger;
ShowMessage(R.X + IntToStr(R.Y));
end;
procedure TForm1.Button7Click(Sender: TObject);
begin
TestRec(['Test', 22]);
end;
It is possible to pass record fields as array of const parameters and assign these parameters to local record variable.
It would be nice! But, no.
If passing things inline is really your objective, then perhaps Open Array Parameters would suit you.
Procedure MyProcedure(const Vars: Array of Variant);
begin
ShowMessage(VarToStr(Vars[0])+' '+VarToStr(Vars[1]));
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
MyProcedure(['Test', 12]);
end;
You could also pass an Array of Const, which is basically an array of TVarRec which is a variant record that also includes type information as VType. This is fun stuff..
An excellent article can be found on Rudy's Delphi Corner here:
Rudy's Delphi Corner, Open Array Parameters

Delphi: Records in Classes

Following situation:
type
TRec = record
Member : Integer;
end;
TMyClass = class
private
FRec : TRec;
public
property Rec : TRec read FRec write FRec;
end;
The following doesn't work (left side cannot be assigned to), which is okay since TRec is a value type:
MyClass.Rec.Member := 0;
In D2007 though the following DOES work:
with MyClass.Rec do
Member := 0;
Unfortunately, it doesn't work in D2010 (and I assume that it doesn't work in D2009 either). First question: why is that? Has it been changed intentionally? Or is it just a side effect of some other change? Was the D2007 workaround just a "bug"?
Second question: what do you think of the following workaround? Is it safe to use?
with PRec (#MyClass.Rec)^ do
Member := 0;
I'm talking about existing code here, so the changes that have to be made to make it work should be minimal.
Thanks!
That
MyClass.Rec.Member := 0;
doesn't compile is by design. The fact that the both "with"-constructs ever compiled was (AFAICT) a mere oversight. So both are not "safe to use".
Two safe solution are:
Assign MyClass.Rec to a temporary record which you manipulate and assign back to MyClass.Rec.
Expose TMyClass.Rec.Member as a property on its own right.
In some situtations like this where a record of a class needs 'direct manipulation' I've often resorted to the following:
PMyRec = ^TMyRec;
TMyRec = record
MyNum : integer
end;
TMyObject = class( TObject )
PRIVATE
FMyRec : TMyRec;
function GetMyRec : PMyRec;
PUBLIC
property MyRec : PMyRec << note the 'P'
read GetMyRec;
end;
function TMyObject.GetMyRec : PMyRec; << note the 'P'
begin
Result := #FMyRec;
end;
The benefit of this is that you can leverage the Delphi automatic dereferencing to make readable code access to each record element viz:
MyObject.MyRec.MyNum := 123;
I cant remember, but maybe WITH works with this method - I try not to use it!
Brian
The reason why it can't be directly assigned is here.
As for the WITH, it still works in D2009 and I would have expected it to work also in D2010 (which I can't test right now).
The safer approach is exposing the record property directly as Allen suggesed in the above SO post:
property RecField: Integer read FRec.A write FRec.A;
Records are values, they aren't meant to be entities.
They even have assignment-by-copy semantics! Which is why you can't change the property value in-place. Because it would violate the value type semantics of FRec and break code that relied on it being immutable or at least a safe copy.
They question here is, why do you need a value (your TRec) to behave like an object/entity?
Wouldn't it be much more appropriate for "TRec" to be a class if that is what you are using it for, anyways?
My point is, when you start using a language feature beyond its intent, you can easily find yourself in a situation where you have to fight your tools every meter on the way.
The reason it has been changed is that it was a compiler bug. The fact that it compiled didn't guarantee that it would work.
It would fail as soon as a Getter was added to the property
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm2 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
FPoint: TPoint;
function GetPoint: TPoint;
procedure SetPoint(const Value: TPoint);
{ Private declarations }
public
{ Public declarations }
property Point : TPoint read GetPoint write SetPoint;
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.Button1Click(Sender: TObject);
begin
with Point do
begin
X := 10;
showmessage(IntToStr(x)); // 10
end;
with Point do
showmessage(IntToStr(x)); // 0
showmessage(IntToStr(point.x)); // 0
end;
function TForm2.GetPoint: TPoint;
begin
Result := FPoint;
end;
procedure TForm2.SetPoint(const Value: TPoint);
begin
FPoint := Value;
end;
end.
You code would suddenly break, and you'd blame Delphi/Borland for allowing it in the first place.
If you can't directly assign a property, don't use a hack to assign it - it will bite back someday.
Use Brian's suggestion to return a pointer, but drop the With - you can eaisly do Point.X := 10;
Another solution is to use a helper function:
procedure SetValue(i: Integer; const Value: Integer);
begin
i := Value;
end;
SetValue(MyClass.Rec.Member, 10);
It's still not safe though (see Barry Kelly's comment about Getter/Setter)
/Edit: Below follows the most ugly hack (and probably the most unsafe as well) but it was so funny I had to post it:
type
TRec = record
Member : Integer;
Member2 : Integer;
end;
TMyClass = class
private
FRec : TRec;
function GetRecByPointer(Index: Integer): Integer;
procedure SetRecByPointer(Index: Integer; const Value: Integer);
public
property Rec : TRec read FRec write FRec;
property RecByPointer[Index: Integer] : Integer read GetRecByPointer write SetRecByPointer;
end;
function TMyClass.GetRecByPointer(Index: Integer): Integer;
begin
Result := PInteger(Integer(#FRec) + Index * sizeof(PInteger))^;
end;
procedure TMyClass.SetRecByPointer(Index: Integer; const Value: Integer);
begin
PInteger(Integer(#FRec) + Index * sizeof(PInteger))^ := Value;
end;
It assumes that every member of the record is (P)Integer sized and will crash of AV if not.
MyClass.RecByPointer[0] := 10; // Set Member
MyClass.RecByPointer[1] := 11; // Set Member2
You could even hardcode the offsets as constants and access directly by offset
const
Member = 0;
Member2 = Member + sizeof(Integer); // use type of previous member
MyClass.RecByPointer[Member] := 10;
function TMyClass.GetRecByPointer(Index: Integer): Integer;
begin
Result := PInteger(Integer(#FRec) + Index)^;
end;
procedure TMyClass.SetRecByPointer(Index: Integer; const Value: Integer);
begin
PInteger(Integer(#FRec) + Index)^ := Value;
end;
MyClass.RecByPointer[Member1] := 20;

Resources