How can I get Record's details using Rtti? - delphi

I'm coding a Rtti class, intended to simplify and generalize operations with Rtti:
tRTTI_Assistant = class (tObject)
private
fSourceObject : tObject;
...
property SourceObject : tObject read fSourceObject write fSourceObject;
...
end;
How can I declare a property that will receive a Record?

You cannot add a record class property that can take any record structure and use RTTI to resolve the inner details of the record.
For this to work you have to use another way. A TValue can be used with RTTI to resolve any type.
So declare:
tRTTI_Assistant = class (tObject)
private
//fSourceObject : tObject; // ! Use AnySource for objects as well
fAnySource : TValue;
...
//property SourceObject : tObject read fSourceObject write fSourceObject;
property AnySource: TValue read fAnySource write fAnySource;
...
end;
And call it:
myAssistant.AnySource := TValue.From(myRecord);
Now you can use RTTI for resolving not just record types, but any type.
See Convert Record to Serialized Form Data for sending via HTTP for examples how work with RTTI on a TValue:

I'm not shure what you exactly want to do. but i think Generics are what you are looking for.
type
TMyRecord = record
I:Integer;
S:string;
B:Boolean;
end;
TMyObject<T:record> = class
private
FMyRecord:T;
public
property MyRecord: T read FMyRecord write FMyRecord;
end;

Related

Indy 10.6 IdContext needs an ID field

In working with setting up TCPServer and FTPServer, the thing I noticed most is the need for a UserID field and a UserFlag field in IdContext. The simple addition of these would greatly facilitate setting up the components for multiple clients. You could create a descendant, but that takes a lot of unnecessary coding for something so easily added to the source code. I modified IdContext.pas as follows:
Protected
FUserFlag: Boolean;
FUserID: Integer;
...
Public
Property UserFlag: Boolean Read FUserFlag Write FUserFlag Default False;
Property UserID: Integer Read FUserID Write FUserID Default -1;
By using these I'm able to signal a state between events and I have the reference readily available whenever an event is fired. I tried to say something in the Indy project but I couldn't find anywhere to say it :/
Thank you for your suggestion, but I am not inclined to make these kind of additions to the base TIdContext class. They simply do not belong there. Deriving a custom class and assigning it to the server's ContextClass property is the correct and appropriate solution in this situation. This is why that property exists in the first place. It is really not that much coding, eg:
type
TMyContext = class(TIdServerContext)
protected
FUserFlag: Boolean;
FUserID: Integer;
...
public
Property UserFlag: Boolean Read FUserFlag Write FUserFlag;
Property UserID: Integer Read FUserID Write FUserID;
end;
procedure TMyForm.FormCreate(Sender: TObject);
begin
// must do this before activating the server...
IdTCPServer1.ContextClass := TMyContext;
end;
And then you can type-cast TIdContext object pointers to TMyContext when needed.
Various Indy servers do exactly this internally. For instance TIdFTPServer uses a TIdFTPServerContext class that has Account and Username properties for the logged in session.
That being said, if you do not want to derive a custom class, the base TIdContext class does already have a public Data property (or DataObject and DataValue properties in Delphi ARC-based compilers), which can be used for storing user-defined data, eg:
type
TMyData = class
protected
FUserFlag: Boolean;
FUserID: Integer;
...
public
Property UserFlag: Boolean Read FUserFlag Write FUserFlag;
Property UserID: Integer Read FUserID Write FUserID;
end;
procedure TMyForm.IdTCPServer1Connect(AContext: TIdContext);
begin
AContext.Data := TMyData.Create;
...
end;
And then you can simply type-cast AContext.Data to TMyData when needed.

Forward declarations for record types (or arrays)

I want to do this in XE5:
type
TMyRec = record
// fields
class function GetList: TMyRecArr; static;
end;
TMyRecArr = array of TMyRec;
I've already seen "Forward declarations for record types" and "how to do a typed forward declaration?", but they seem irrelevant since my problem is not passing the record as a parameter.
You cannot use a forward declaration to declare a record type or an array type. But not to fear. You can use a generic dynamic array, TArray<T>.
type
TMyRec = record
class function GetList: TArray<TMyRec>; static;
end;
This is actually better than declaring TMyRecArr as per the code in your question. That's because the generic TArray<T> has more flexible type identity than a traditional dynamic array type. You can use TArray<T> with generic types defined in libraries that are independent and unaware of your code.
Now, you could declare the type like this:
type
TMyRec = record
type TMyRecArray = array of TMyRec;
class function GetList: TMyRecArray; static;
end;
And then your array type is TMyRec.TMyRecArray. But I urge you not to do this. You'll have a type that can only be used with you code, and cannot be used with third party code.
In summary, TArray<T> is your friend.
Since declarations of pointers types are permitted before the type definition, slightly modifying your function you're allowed to do this:
type
PMyRecArr = ^TMyRecArr;
TMyRec = record
// fields
class procedure GetList(const arr: PMyRecArr); static;
end;
TMyRecArr = array of TMyRec;
The procedure implementation and its usage follow:
class procedure TMyRec.GetList(const arr: PMyRecArr);
begin
SetLength(arr^, 4);
end;
var
arr: TMyRecArr;
begin
TMyRec.GetList(#arr);
Writeln(Length(arr));//prints 4
end.
Well, I searched a little more, and found that I can use helpers to do this:
type
TMyRec = record
// fields
end;
TMyRecArr = array of TMyRec;
TMyRecHelper = record helper for TMyRec
class function GetList: TMyRecArr; static;
end;
Sure it lacks benefits of generics that David mentioned in his answer and comments, but it doesn't make the code auto-complete unusable! I mean one may comes to this conclusion that the flexibility that TArray<T> offers is not needed in some piece of code. Then it would be nothing more than increment in memory usage of the final running application or possibly lower performance.
Instead of trying to add forward decalrations to records (which up until now still isn't possible in Delphi), there is a way around this : move functions that reference another record type towards a record helper, declared where both record types are in scope.
type
RecordA = record
// [...]
end;
RecordB = record
// [...]
end;
RecordAHelper = record helper for RecordA
procedure Call(argument: RecordB);
end;
After obvioulsy implementing this, above allows one to write (and compile and run) : RecordA_variable.Call(RecordB_variable)

Pass a class with Generic parameter to another class in Delphi

I have 2 classes in Delphi XE5 and pass one to another :
TfrmBaseList = class(TForm)
private
FListOwner: TSystemBaseList<TSystemColumnEntity>;
public
constructor Create(AListOwner: TSystemBaseList<TSystemColumnEntity>); virtual;
end
TSystemBaseList<T: TSystemColumnEntity> = class(TPersistent)
public
procedure Execute;
property SelectedValues: TObjectList<T> read
end;
procedure TSystemBaseList<T>.Execute;
var
frmList: TfrmBaseList;
begin
//frmList := TfrmBaseList.Create(Self<T>)
//frmList := TfrmBaseList.Create(Self<TSystemColumnEntity>)
frmList := TfrmBaseList.Create(???????)
end;
How can I Pass TSystemBaseList to Constructor of TfrmBaseList class?
This constructor only create a Form and then assign AListOwner to FListOwner,
Can I change this constructor to property like this:
TfrmBaseList = class(TForm)
private
FListOwner: TSystemBaseList<TSystemColumnEntity>;
public
property ListOwner: TSystemBaseList<TSystemColumnEntity> read FListOwner write FListOwner;
end
And how do I set it?
The constructor expects a concrete instantiation, an instance of:
TSystemBaseList<TSystemColumnEntity>
You are supplying an uninstantiated generic instance of type:
TSystemBaseList<T>
You have to supply a concrete instance to that constructor. In its current form you cannot instantiate the form from TSystemBaseList<T>.Execute.
You might think that because T must derive from TSystemColumnEntity that TSystemBaseList<T> would be compatible with TSystemBaseList<TSystemColumnEntity>. But that is not the case because there is no generic variance supported. Read more on this topic here: Generics and variance.
One way forward would be to make the form type generic too. Although that does not play well with the IDE form designer. I suspect a more radical re-design is needed to solve your problem. I won't offer advice on that re-design because I don't know the problem.

Migrate Data from one class to a second class [duplicate]

This question already has answers here:
Copy object data fields into subclass instance
(2 answers)
Closed 8 years ago.
depending on the dataflow (data itself) i'm starting with a very simple data type "AboutMe", later depending on the data itself or the workflow I want to continue working using this data now in a class called "AboutMe_more". This procedure might happen 1..3 times in my program.
AboutMe= class
Name : String
end;
AboutMe_more = class(AboutMe)
gender : String;
Birth : TDate;
Aboutme_complete = class (AboutMe_more)
adresss : String;
salery : Real;
.....
end;
starting with the complete class is not a good idea in my case because there might be a different switch to an other desired class like
Aboutme_complete_option = class (AboutMe_more)
company : string;
city : String;
kids : String;
.....
end;
Q:
a) What is the best way to transfer data from one class to the derived class, not need for transfer data to parent class .
b) Is the way a good programming style or does the need for that datamovement indicate a poor class construction / design ?
It almost looks like you are trying to model a database using objects.
b) Is the way a good programming style or does the need for that datamovement indicate a poor class construction / design ?
The problem here (as mentioned by #jpfollenius) is that you'll end up with a very deep hierarchy of objects.
a) What is the best way to transfer data from one class to the derived class [...]?
I don't know about the best way, but in Delphi transferring data from one object to another is often done using an overloaded Assign.
TMyObject = class(TPersistent)
procedure Assign(Source: TPersistent); overload;
....
implementation
procedure TMyObject.Assign(Source: TPersistent);
begin
inherited Assign(Source);
if (Source is TMyObject) then begin
Self.Field1:= Source.Field1;
Self.Field2:= Source.Field2;
end;
end;
More sensible approach
Inheritance is only one way to address this problem.
A more appropriate way would be to use encapsulation.
Here you have an object and an interface which holds the data and container objects which give you access to that data.
Delphi has a very nice mechanism for that because it allows you to delegate the implementation of an interface to a contained object.
An example using 2 data objects with interfaces and a container object.
type
IData1 = interface
['{3F996D68-1FD0-4490-AE60-8F735A9DFFE8}'] //Use ctrl+alt+g to generate a number
function GetData1: integer;
procedure SetData1(value: integer);
property Data1: integer read GetData1 write SetData1;
end;
IData2 = interface
['{3F996D68-1FD0-4490-AE60-8F735A9DFFE9}']
function GetData2: integer;
procedure SetData2(value: integer);
property Data2: integer read GetData2 write SetData2;
end;
TData1 = class(TInterfacedObject, IData1);
private
FData1: integer;
protected
function GetData1: integer;
procedure SetData1(value: integer);
public
property Data: integer read GetData1 write SetData1;
end;
TData2 = class(TInterfacedObject, IData2);
{see TData1 above}
TContainer = class(TInterfacedObject, IData1, IData2)
private
FData1: TData1;
FData2: TData2;
public
constructor Create();
property Data1: TData1 read FData1 implements IData1; //delegates implementation to object FData.
property Data2: TData2 read FData2 implements IData2;
end;
Copied from this question: https://stackoverflow.com/questions/6063274/hidden-features-in-the-delphi-language (now sadly deleted).

how to call inherited constructor of TObjectDictionary in Delphi

I created the following class, after reading about the significant performance improvement of TDictionary over TStringList:
TAnsiStringList = class(TObjectDictionary<AnsiString,TObject>)
public
constructor Create(const OwnsObjects: Boolean = True); reintroduce;
destructor Destroy; override;
procedure Add(const AString: AnsiString);
procedure AddObject(const AString: AnsiString; AObject: TObject);
end;
I coded the constructor like this:
{ TAnsiStringList }
constructor TAnsiStringList.Create(const OwnsObjects: Boolean = True);
begin
if OwnsObjects then
inherited Create([doOwnsKeys,doOwnsValues])
else
inherited Create;
end;
...expecting that this TObjectDictionary constructor would be called:
constructor Create(Ownerships: TDictionaryOwnerships; ACapacity: Integer = 0); overload;
...if the Ownerships parameter were specified. If the Ownerships parameter is not specified, I expected that the following inherited TDictionary constructor would be called:
constructor Create(ACapacity: Integer = 0); overload;
The code compiles and runs, but when I call
inherited Create([doOwnsKeys,doOwnsValues]) I get the following error:
Invalid class typecast
Does anyone see what I'm doing wrong, and is there a proper way to do this?
TIA
The problem is that you are asking the container to call Free on your keys when items are removed. But your keys are not classes and so that is an invalid request. This is trapped at runtime rather than compile time because the ownership is not determined until runtime.
You only want doOwnsValues and should remove doOwnsKeys.
if OwnsObjects then
inherited Create([doOwnsValues])
else
inherited Create;
For what it is worth, if you are trying to maked an AnsiString equivalent to TStringList, then your approach is flawed. A string list is an ordered container, but a dictionary is not. You won't be able to index by integer, iterate in order and so on. I also do not understand why you would want to force all consumers of this class to declare the objects to be of type TObject. You should leave that parameter free for the consumer to specify. That's the beauty of generics.
Perhaps you don't want an ordered container, in which case a dictionary is what you need. But in that case you simply don't need to create a new class. You can simply use TObjectDictionary as is.
If you are dead set on creating a sub class then I'd do it like this:
type
TAnsiStringDict<T: class> = class(TObjectDictionary<AnsiString, T>)
That will allow the consumer of the class to decide which type of objects they put in the dictionary, and maintain compile time type safety.
So, when you want a dictionary of list boxes your declare a variable like this:
var
ListBoxDict: TAnsiStringDict<TListBox>;

Resources