Structure Delphi Generics Class - delphi

I'm new to generics and need some help to structure a class and implement methods.
I'm trying to use generics to serialize any TObject-JSON. Moreover, I want to be able to reuse the code.
These are my questions:
How do I create a generic constructor? I want to be able to use Self or Default(T), but it returns just nil.
V := Marshal.Marshal(ReturnObject) - This method requires a TObject, but I do not know how to reference the current object that was passed in.
How can I use this inside a method? Look at the code snipped below, marked with "Question 3".
This is my code:
TFileOperationResult = class(TObject)
private
FSuccess: Boolean;
//Error: PException;
FLastError: Integer;
function GetFailure: Boolean;
property Failure: Boolean read GetFailure;
public
property Success: Boolean read FSuccess write FSuccess;
property LastError: Integer read FLastError write FLastError;
end;
TResponseObject<T: class> = class(TObject)
private
FReturnObject: T;
function GetReturnObject: T;
function BaseStringsConverter(Data: TObject): TListOfStrings;
public
constructor Create; overload;
property ReturnObject: T read GetReturnObject;
procedure Serialize;
end;
constructor TResponseObject<T>.Create;
begin
// Question 1 - What should go in here?
end;
function TResponseObject<T>.GetReturnObject: T;
begin
Result := Default(T);// Is this correct?
end;
procedure TResponseObject<T>.Serialize;
var
Marshal: TJSONMarshal;
V: TJSONValue;
begin
Marshal := TJSONMarshal.Create(TJSONConverter.Create);
Marshal.RegisterConverter(TResponseObject<T>, BaseStringsConverter);
V := Marshal.Marshal(ReturnObject); // Question 2 - How Can I refer to 'Self'?
OutPut := V.ToString;
Marshal.Free;
end;
Calling code:
procedure TForm1.Test;
var
FileOperationResult: TResponseObject<TFileOperationResult>;
begin
FileOperationResult := TResponseObject<TFileOperationResult>.Create;
FileOperationResult.Serialize;
end;
Question 3:
procedure TForm1.MoveCopyFile<THowNowResponse>(ASource, DDestination: String);
var
FileOperationResult: TFileOperationResult;
begin
FileOperationResult := TFileOperationResult.Create;
// What to do?
end;
Any other comments are much appreciated.

It's hard to tell exactly what you're trying to do here, but I can make a guess. For TResponseObject, you want an object that can contain another object and operate on it. In that case, you probably want to pass it in to the constructor, like so:
constructor TResponseObject<T>.Create(value: T);
begin
FReturnObject := value;
end;
Likewise, if you make a GetReturnObject method, it should probably return the value of the FReturnObject field. (Or you could make the read accessor of the property just reference FReturnObject directly.)
function TResponseObject<T>.GetReturnObject: T;
begin
Result := FReturnObject;
end;
It's really hard to answer #3 since I don't know what you're trying to do with this, but hopefully my answers to the first two will help you get back on track. Just remember that generics don't have to be confusing; they're basically just type substitution. Anywhere you'd use a normal type in a non-generic routine, you can replace it with a <T> to create a generic routine, and then substitute any type that fits the constraints for that particular T.

Related

Delphi Generics method

I am using generics to define a list of objects that themselves hold generic lists. I have written a method for retrieving the aggregate of each of these lists using specific methods that basically do the same thing. Below is the structure:
unit Unit1;
interface
uses
System.Generics.Collections;
type
TListType=(ltSType,ltFType);
TMyList<T> = class(TList<T>)
{Do some stuff in here to help load lists etc}
end;
TListObject=class(TObject)
private
FSList:TMyList<string>;
FIList:TMyList<integer>;
function GetSList: TMyList<string>;
function GetIList: TMyList<integer>;
public
property MySList:TMyList<string> read GetSList;
property MyIList:TMyList<integer> read GetIList;
constructor create;
end;
TListOfObject<T:TListObject> = class(TObjectList<T>)
public
Function AggrSList:TMyList<string>;
Function AggrIList:TMyList<integer>;
end;
implementation
{ TListObject }
constructor TListObject.create;
begin
FSList:=TMyList<string>.create;
FIList:=TMyList<integer>.create;
end;
function TListObject.GetIList: TMyList<integer>;
begin
result:=FIlist;
end;
function TListObject.GetSList: TMyList<string>;
begin
result:=FSList;
end;
{ TListOfObject<T> }
function TListOfObject<T>.AggrIList: TMyList<integer>;
var
i,j:integer;
begin
result:=TMyList<integer>.create;
for I := 0 to count-1 do
for j := 0 to items[i].MyIList.Count-1 do
result.Add(items[i].MyIList[j]);
end;
function TListOfObject<T>.AggrSList: TMyList<string>;
var
i,j:integer;
begin
result:=TMyList<string>.create;
for I := 0 to count-1 do
for j := 0 to items[i].MySList.Count-1 do
result.Add(items[i].MySList[j]);
end;
end.
I am still fairly new to generics but feel that the aggregate methods AggrIList and AggrSlist could be written using generics that uses a single method to extract the data then is cast at the result.
Is this possible and how would I approach this? I plan to do some more advanced functions that would benefit from this approach as well.
You can use (pseudocode):
TNestedList<T> = class(TList<TList<T>>)
function GetAggregateList : TList<T>;
end;
function TNestedList<T>.GetAggregateList : TList<T>;
var
List : TList<T>;
Item : T;
begin
Result := TList<T>.Create;
for List in Self do
for Item in List do
Result.Add(Item);
end;
No need to implement GetAggregateList for specific types - that's what you use generics for.
Also note that it might be better to either call your method CreateAggregateList (to make clear that the caller is responsible for destroying the created list) or to pass in an already created list instance into a procedure:
procedure GetAggregateList(List : TList<T>);
...which leaves list ownership completely to the caller and is quite a common pattern in Delphi.

delphi tlist object method calling

all,
In Delphi, I created a simple class called T_Test (see below).
T_Test = class(TObject)
private
F_Int : Integer;
public
constructor Create(inInt: Integer);
destructor Destroy; override;
property Int: Integer read F_Int write F_Int;
function showInt : String;
end;
constructor T_Test.Create(inInt: Integer);
begin
F_Int := inInt;
end;
destructor T_Test.Destroy;
begin
self.Free;
end;
function T_Test.showInt : String;
var outputLine : String;
begin
result := IntToStr(Int);
outputLine := result;
Form1.Memo1.Lines.Add(outputLine);
end;
Then I have a procedure in which I want to make a TList of T_Test object and call the
showInt method function on them.
I tried like this :
procedure testTlist;
var
a, b: T_Test;
i : Integer;
begin
a := T_Test.Create(5);
b := T_Test.Create(10);
listTest := TList.Create;
listTest.Add(a);
listTest.Add(b);
listTest[i].showInt;
end;
But I keep getting an an that says I have to use a Record, Object or Class Type on the
call of 'listTest[i].showInt'
Does anyone know how to call this method ?
Cast the listTest[i] pointer back to T_Test and then call its method:
T_Test(listTest[i]).showInt;
Alternatively, if available, use a templated TObjectList class to store T_Test instances directly.
Martin's answer is correct. But it's worth noting that if you might be adding different classes to your list, a more robust fragment of code would be ...
var pMmember: pointer;
pMember := listTest[i];
if TObject( pMember) is T_Test then
T_Test( pMember).ShowInt;
Martin's point about TObjectList is quiet correct. Another option to consider would be TList<T_Test>. David's comment about the error in your destructor is correct too.
I note that you did not initialise the value of i. So the fragment above is pretending you did. If you also wanted to check that the index variable was at a valid value, and not call ShowInt if it was invalid, then you could do something like this...
if (i >= 0) and (i < listTest.Count) and (TObject(listTest[i]) is T_Test) then
T_Test(listTest[i]).ShowInt;
The above code fragment relies on short-circuit boolean evaluation.

How i can determine if an abstract method is implemented?

I'm using a very large delphi third party library without source code, this library has several classes with abstract methods. I need to determine when an abtract method is implemented by a Descendant class in runtime to avoid the EAbstractError: Abstract Error and shows a custom message to the user or use another class instead.
for example in this code I want to check in runtime if the MyAbstractMethod is implemented.
type
TMyBaseClass = class
public
procedure MyAbstractMethod; virtual; abstract;
end;
TDescendantBase = class(TMyBaseClass)
public
end;
TChild = class(TDescendantBase)
public
procedure MyAbstractMethod; override;
end;
TChild2 = class(TDescendantBase)
end;
How I can determine if an abstract method is implemented in a Descendant class in runtime?
you can use the Rtti, the GetDeclaredMethods function get a list of all the methods that are declared in the reflected (current) type. So you can check if the method is present in the list returned by this function.
function MethodIsImplemented(const AClass:TClass;MethodName : string): Boolean;
var
m : TRttiMethod;
begin
Result := False;
for m in TRttiContext.Create.GetType(AClass.ClassInfo).GetDeclaredMethods do
begin
Result := CompareText(m.Name, MethodName)=0;
if Result then
break;
end;
end;
or you can compare the Parent.Name property of the TRttiMethod and check if match with the current class name.
function MethodIsImplemented(const AClass:TClass;MethodName : string): Boolean;
var
m : TRttiMethod;
begin
Result := False;
m:=TRttiContext.Create.GetType(AClass.ClassInfo).GetMethod(MethodName);
if m<>nil then
Result:=CompareText(AClass.ClassName,m.Parent.Name)=0;
end;
function ImplementsAbstractMethod(AObj: TMyBaseClass): Boolean;
type
TAbstractMethod = procedure of object;
var
BaseClass: TClass;
BaseImpl, Impl: TAbstractMethod;
begin
BaseClass := TMyBaseClass;
BaseImpl := TMyBaseClass(#BaseClass).MyAbstractMethod;
Impl := AObj.MyAbstractMethod;
Result := TMethod(Impl).Code <> TMethod(BaseImpl).Code;
end;
Look at the implementation of the 32-bit version of the TStream.Seek() method in the VCL source code (in Classes.pas). It performs a check to make sure the 64-bit version of Seek() has been overridden before calling it. It doesn't involve TRttiContext lookups to do that, just a simple loop through its Parent/Child VTable entries, similar to how Zoƫ's answer shows.

Instance reference in Delphi?

What's the Delphi equivalent of 'this' in C++? Could you please give some examples of its use?
In delphi Self is the equivalent of this. It is also assignable as described in here.
In most cases, you should not use self in the methods.
In fact, it's like if there was an implicit self. prefix when you access the class properties and methods, within a class method:
type
TMyClass = class
public
Value: string;
procedure MyMethod;
procedure AddToList(List: TStrings);
end;
procedure TMyClass.MyMethod;
begin
Value := 'abc';
assert(self.Value='abc'); // same as assert(Value=10)
end;
The self is to be used when you want to specify the current object to another method or object.
For instance:
procedure TMyClass.AddToList(List: TStrings);
var i: integer;
begin
List.AddObject(Value,self);
// check that the List[] only was populated via this method and this object
for i := 0 to List.Count-1 do
begin
assert(List[i]=Value);
assert(List.Objects[i]=self);
end;
end;
this above code will add an item to the TStrings list, with List.Objects[] pointing to the TMyClass instance. And it will check this has been the case for all items of the List.

Delphi call method based on RTTI information

Hey all, first sorry for my bad english.
Consider the following (not actual code):
IMyInterface = Interface(IInterfce)
procedure Go();
end;
MyClass = class(IMyInterface)
procedure Go();
end;
MyOtherClass = class
published
property name: string;
property data: MyClass;
end;
I'm setting "MyOtherClass" properties using RTTI. For the string property it's easy, but my question is:
How can I get a reference to the "data" (MyClass) property so I can call the Go() method?
I want to do something like this (pseudo-code):
for i:= 0 to class.Properties.Count
if (propertyType is IMyInterface) then
IMyInterface(class.properties[i]).Go()
(if only this was C# :( )
PS.: this is in delphi 7 (i know, even worse)
If the string property is easy, as you say, then I assume you're calling GetStrProp and SetStrProp from the TypInfo unit. Class-type properties can be equally easy with GetObjectProp and SetObjectProp.
if Supports(GetObjectProp(Obj, 'data'), IMyInterface, Intf) then
Intf.Go;
If you don't really need the interface, and you know that the data property has type TMyClass, then you can go a little more directly:
(GetObjectProp(Obj, 'data') as TMyClass).Go;
That requires the property to have a non-null value.
If you don't know the name of the property you want, then you can use some other things in TypInfo to search for it. For example, here is a function that will find all the published properties of an object that have values that implement IMyInterface; it calls Go on each of them in no particular order.
procedure GoAllProperties(Other: TObject);
var
Properties: PPropList;
nProperties: Integer;
Info: PPropInfo;
Obj: TObject;
Intf: IMyInterface;
Unk: IUnknown;
begin
// Get a list of all the object's published properties
nProperties := GetPropList(Other.ClassInfo, Properties);
if nProperties > 0 then try
// Optional: sort the list
SortPropList(Properties, nProperties);
for i := 0 to Pred(nProperties) do begin
Info := Properties^[i];
// Skip write-only properties
if not Assigned(Info.GetProc) then
continue;
// Check what type the property holds
case Info.PropType^^.Kind of
tkClass: begin
// Get the object reference from the property
Obj := GetObjectProp(Other, Info);
// Check whether it implements IMyInterface
if Supports(Obj, IMyInterface, Intf) then
Intf.Go;
end;
tkInterface: begin
// Get the interface reference from the property
Unk := GetInterfaceProp(Obj, Info);
// Check whether it implements IMyInterface
if Supports(Unk, IMyInterface, Intf) then
Intf.Go;
end;
end;
end;
finally
FreeMem(Properties);
end;
end;
You can get an array of all published propertied by calling GetPropInfos(MyClass.ClassInfo). This is an array of PPropInfo pointers. And you can get at type-specific data from a PPropInfo by calling GetTypeData on it, which returns a PTypeData. The record it points to will have the information you're looking for about the class reference.

Resources