📜 ⬆️ ⬇️

Working with class variables of the heir class in the base class

Why do you need it?

I encountered such a need, for example, when serializing classes. There was a desire to take out all the routine code and information about the heir classes to the base class. Well, laziness, after all, to prescribe the same for each child class. So I wondered if the generics could be screwed here.

And so, the necessary class variables can be wrapped in a record, dynamically allocate memory for them, and then used through a pointer. This pointer will be stored in the class variable of the intermediate generalized class. This generic class will become the new unique base class for various classes of the village.

To access such data from a specific instance of a class, you can add a pointer field that will be initialized in the constructor of the generic class. And to transfer this pointer to the class base class methods use an additional parameter. The substitution of the value of this parameter, possibly instead of the hidden ClassSelf, will be carried out in the overloaded methods of the intermediate generalized class.
')
The implementation of the basic and generic classes:
unit MyStore; interface uses System.Generics.Collections; type TDataBase = class abstract protected type TDataType = class of TDataBase; TDataList = TList<TDataBase>; TDataInfo = array of Integer; PClassVar = ^TClassVar; TClassVar = record cType :TDataType; cObjs :TDataList; cInfo :TDataInfo; end; protected fVar :PClassVar; class function Init(var cVar :PClassVar):Pointer; overload; class procedure Done(var cVar :PClassVar); overload; static; class procedure AddFild( cVar :PClassVar; var Fild); overload; static; public Tag :Integer; procedure Save; constructor Create; virtual; destructor Destroy; override; end; TDataProx<T:class> = class(TDataBase) protected class var cVar :TDataBase.PClassVar; class function Init:T; overload; inline; class procedure Done; overload; inline; class procedure AddFild(var Fild); overload; inline; public constructor Create; overload; override; class function Objs:TList<T>; inline; end; implementation { TDataBase } class procedure TDataBase.AddFild(cVar: PClassVar; var Fild); // static; begin with cVar^ do begin SetLength(cInfo,Length(cInfo)+1); cInfo[Length(cInfo)-1]:=Integer(PByte(@Fild) - PByte(@cType)); end; end; procedure TDataBase.Save; begin with fVar^ do begin end; end; constructor TDataBase.Create; begin with fVar^ do cObjs.Add(Self); end; destructor TDataBase.Destroy; begin with fVar^ do cObjs.Extract(Self); inherited; end; class procedure TDataBase.Done(var cVar: PClassVar); // static; var Obj :TDataBase; begin with cVar^ do begin for Obj in cObjs do begin Obj.Save; Obj.Free; end; cObjs.Free; Finalize(cInfo); end; Dispose(cVar); end; class function TDataBase.Init(var cVar: PClassVar):Pointer; // uses ClassSelf begin New(cVar); with cVar^ do begin cType:=Self; cObjs:=TDataList.Create; Initialize(cInfo); Result:=@cType; // synthetic Object :) end; AddFild(cVar,TDataBase(Result).Tag); end; { TDataProx<T> } class procedure TDataProx<T>.AddFild(var Fild); // inline; begin AddFild(cVar, Fild); end; constructor TDataProx<T>.Create; begin fVar := cVar; // ! inherited; // ! after fVar := cVar; end; class procedure TDataProx<T>.Done; // inline; begin Done(cVar); end; class function TDataProx<T>.Init:T; // inline; begin Result:=Init(cVar); end; class function TDataProx<T>.Objs: TList<T>; // inline begin Result:=TList<T>(cVar.cObjs); end; end. 


Usage example:

 unit MyData; interface uses MyStore; type TDataA = class(TDataProx<TDataA>) Data :Integer; Note: string; class constructor Init; class destructor Done; class procedure Work; end; TDataB = class(TDataProx<TDataB>) Data :Double; Memo :array of string; class constructor Init; class destructor Done; end; implementation { DataA } class destructor TDataA.Done; begin Done; end; class constructor TDataA.Init; begin with Init do begin AddFild(Data); AddFild(Note); end; end; class procedure TDataA.Work; var Obj :TDataA; begin for Obj in Objs do Inc(Obj.Data,Obj.Tag); end; { DataB } class destructor TDataB.Done; begin Done; end; class constructor TDataB.Init; begin with Init do begin AddFild(Data); AddFild(Memo); end; end; end. 

Source: https://habr.com/ru/post/181914/


All Articles