I am learning Delphi reading Marco Cantu's book and it's super complete. It's very clear but I have a doubt about the keyword self. I already have experience with OOP and I have the basics of it. My question is very simple. Can I compare the keyword self (Delphi) to the keyword this (Java)?
When I read on the book about the self used inside record, I got in my mind something like self : Delphi = this : Java. Look at the code I created to make a test:
type
TMarioKart = packed record
Character: String;
Kart: String;
Tires: String;
Speed: double;
Competitive: boolean;
private
air-speed: integer;
ground-speed: integer;
water-speed: integer;
public
constructor Create(Character: string);
function ShowStats(a: TMarioKart):string; overload;
function ShowStats(a: TMarioKart; b: TMarioKart): string; overload;
end;
I am going to cut off the biggest part of the code, I am just showing the constructor here:
constructor TMarioKart.Create(Character: string);
begin
self.Character := Character;
end;
Using the keyword self here I am referring to the Character of the record, and not to the Character passed in the method. Is this the correct way to use the self? Could it be the brother of Java's this?
Self is very similar to this in Java, C++, or C#. However it is a little bit more invoked, as the following code will show.
In Delphi, you can have class methods that are not static but also have a Self pointer, which then obviously does not point to an instance of the class but to the class type itself that the method is called on.
See the output of this program:
program WhatIsSelf;
{$APPTYPE CONSOLE}
type
TAnimal = class
procedure InstanceMethod;
class procedure ClassMethod;
class procedure StaticClassMethod; static;
end;
TDog = class(TAnimal)
end;
class procedure TAnimal.ClassMethod;
begin
Writeln(Self.ClassName);
end;
procedure TAnimal.InstanceMethod;
begin
Writeln(Self.ClassName);
end;
class procedure TAnimal.StaticClassMethod;
begin
// Writeln(Self.ClassName); // would not compile with error: E2003 Undeclared identifier: 'Self'
end;
var
t: TAnimal;
begin
t := TDog.Create;
t.InstanceMethod;
t.ClassMethod;
TAnimal.ClassMethod;
TDog.ClassMethod;
Readln;
end.
Yes, Delphi's Self is the direct analogue of Java's this.
Formally speaking, Self is a normal identifier (that is automatically predeclared in some circumstances).
Self is automatically defined in the implementation of methods and class methods and its purpose there is similar to this in Java as already mentioned. The underlying technology is analogous to the Result variable in ordinary functions.
In other words, there is no self keyword (that's why it's typically written with an uppercase S). Whenever you want (and can [*]), you may introduce your own Self variable, method or class.
type
// TForm1 ommitted
Self = class
function Self: Integer;
end;
function Self.Self: Integer;
begin
Result := SizeOf(Self);
end;
You'll have of course difficulties to instantiate an object of this type within a method, in these cases, you have to use it through an adapter function (or procedure):
function UseSelf: Integer;
var
S: Self;
begin
S := Self.Create;
Result := S.Self;
S.Free;
end;
function VarSelf: Integer;
var
Self: Integer;
begin
Self := SizeOf(Result);
Result := Self;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(IntToStr(UseSelf)+' '+IntToStr(VarSelf));
end;
[*] You cannot declare a Self variable within methods, because there is already such a declaration and that's exactly what the error says:
Identifier redeclared: 'Self'
self is a special variable that points to the object that owns the currently executing code and it gives access to the current object. In Ruby programming language there is self too, however equivalent of it in JavaScript, C++, C# and Java is this and in VB is Me.
Related
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.
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.
Following up on my earlier question :
Generics and Marshal / UnMarshal. What am I missing here?
In "part #1" (the link above) TOndrej provided a nice solution - that failed on XE2.
Here I provide corrected source to correct that.
And I feel the need to expand this issue a bit more.
So I would like to hear you all how to do this :
First - To get the source running on XE2 and XE2 update 1 make these changes :
Marshal.RegisterConverter(TTestObject,
function (Data: TObject): String // <-- String here
begin
Result := T(Data).Marshal.ToString; // <-- ToString here
end
);
Why ??
The only reason I can see must be related to XE2 is having a lot more RTTI information available. And hence it will try and marshal the TObject returned.
Am I on the right track here? Please feel free to comment.
More important - the example does not implement an UnMarshal method.
If anyone can produce one and post it here I would love it :-)
I hope that you still have interest in this subject.
Kind Regards
Bjarne
In addition to the answer to this question, I've posted a workaround to your previous question here: Generics and Marshal / UnMarshal. What am I missing here?
For some reason, using the non-default constructor of the TJsonobject causes the issue in XE2 - using the default constructor "fixed" the problem.
First, you need to move your TTestobject to its own unit - otherwise, RTTI won't be able to find/create your object when trying to unmarshal.
unit uTestObject;
interface
uses
SysUtils, Classes, Contnrs, Generics.Defaults, Generics.Collections, DbxJson, DbxJsonReflect;
type
{$RTTI EXPLICIT METHODS([]) PROPERTIES([vcPublished]) FIELDS([vcPrivate])}
TTestObject=class(TObject)
private
aList:TStringList;
public
constructor Create; overload;
constructor Create(list: array of string); overload;
constructor Create(list:TStringList); overload;
destructor Destroy; override;
function Marshal:TJSonObject;
class function Unmarshal(value: TJSONObject): TTestObject;
published
property List: TStringList read aList write aList;
end;
implementation
{ TTestObject }
constructor TTestObject.Create;
begin
inherited Create;
aList:=TStringList.Create;
end;
constructor TTestObject.Create(list: array of string);
var
I:Integer;
begin
Create;
for I:=low(list) to high(list) do
begin
aList.Add(list[I]);
end;
end;
constructor TTestObject.Create(list:TStringList);
begin
Create;
aList.Assign(list);
end;
destructor TTestObject.Destroy;
begin
aList.Free;
inherited;
end;
function TTestObject.Marshal:TJSonObject;
var
Mar:TJSONMarshal;
begin
Mar:=TJSONMarshal.Create();
try
Mar.RegisterConverter(TStringList,
function(Data:TObject):TListOfStrings
var
I, Count:Integer;
begin
Count:=TStringList(Data).Count;
SetLength(Result, Count);
for I:=0 to Count-1 do
Result[I]:=TStringList(Data)[I];
end);
Result:=Mar.Marshal(Self) as TJSonObject;
finally
Mar.Free;
end;
end;
class function TTestObject.Unmarshal(value: TJSONObject): TTestObject;
var
Mar: TJSONUnMarshal;
L: TStringList;
begin
Mar := TJSONUnMarshal.Create();
try
Mar.RegisterReverter(TStringList,
function(Data: TListOfStrings): TObject
var
I, Count: Integer;
begin
Count := Length(Data);
Result:=TStringList.Create;
for I := 0 to Count - 1 do
TStringList(Result).Add(string(Data[I]));
end
);
//UnMarshal will attempt to create a TTestObject from the TJSONObject data
//using RTTI lookup - for that to function, the type MUST be defined in a unit
Result:=Mar.UnMarshal(Value) as TTestObject;
finally
Mar.Free;
end;
end;
end.
Also note that the constructor has been overloaded - this allows you to see that the code is functional without pre-pouplating the data in the object during creation.
Here is the implementation for the generic class list object
unit uTestObjectList;
interface
uses
SysUtils, Classes, Contnrs, Generics.Defaults, Generics.Collections,
DbxJson, DbxJsonReflect, uTestObject;
type
{$RTTI EXPLICIT METHODS([]) PROPERTIES([]) FIELDS([])}
TTestObjectList<T:TTestObject,constructor> = class(TObjectList<T>)
public
function Marshal: TJSonObject;
constructor Create;
class function Unmarshal(value: TJSONObject): TTestObjectList<T>; static;
end;
//Note: this MUST be present and initialized/finalized so that
//delphi will keep the RTTI information for the generic class available
//also, it MUST be "project global" - not "module global"
var
X:TTestObjectList<TTestObject>;
implementation
{ TTestObjectList<T> }
constructor TTestObjectList<T>.Create;
begin
inherited Create;
//removed the add for test data - it corrupts unmarshaling because the data is already present at creation
end;
function TTestObjectList<T>.Marshal: TJSonObject;
var
Marshal: TJsonMarshal;
begin
Marshal := TJSONMarshal.Create;
try
Marshal.RegisterConverter(TTestObjectList<T>,
function(Data: TObject): TListOfObjects
var
I: integer;
begin
SetLength(Result,TTestObjectlist<T>(Data).Count);
for I:=0 to TTestObjectlist<T>(Data).Count-1 do
Result[I]:=TTestObjectlist<T>(Data)[I];
end
);
Result := Marshal.Marshal(Self) as TJSONObject;
finally
Marshal.Free;
end;
end;
class function TTestObjectList<T>.Unmarshal(value: TJSONObject): TTestObjectList<T>;
var
Mar: TJSONUnMarshal;
L: TStringList;
begin
Mar := TJSONUnMarshal.Create();
try
Mar.RegisterReverter(TTestObjectList<T>,
function(Data: TListOfObjects): TObject
var
I, Count: Integer;
begin
Count := Length(Data);
Result:=TTestObjectList<T>.Create;
for I := 0 to Count - 1 do
TTestObjectList<T>(Result).Unmarshal(TJSONObject(Data[I]));
end
);
//UnMarshal will attempt to create a TTestObjectList<TTestObject> from the TJSONObject data
//using RTTI lookup - for that to function, the type MUST be defined in a unit,
//and, because it is generic, there must be a GLOBAL VARIABLE instantiated
//so that Delphi keeps the RTTI information avaialble
Result:=Mar.UnMarshal(Value) as TTestObjectList<T>;
finally
Mar.Free;
end;
end;
initialization
//force delphi RTTI into maintaining the Generic class information in memory
x:=TTestObjectList<TTestObject>.Create;
finalization
X.Free;
end.
There are several things that are important to note:
If a generic class is created at runtime, RTTI information is NOT kept unless there is a globally accessible object reference to that class in memory. See here: Delphi: RTTI and TObjectList<TObject>
So, the above unit creates such a variable and leaves it instantiated as discussed in the linked article.
The main procedure has been updated that shows both marshaling and unmarshaling the data for both objects:
procedure Main;
var
aTestobj,
bTestObj,
cTestObj : TTestObject;
aList,
bList : TTestObjectList<TTestObject>;
aJsonObject,
bJsonObject,
cJsonObject : TJsonObject;
s: string;
begin
aTestObj := TTestObject.Create(['one','two','three','four']);
aJsonObject := aTestObj.Marshal;
s:=aJsonObject.ToString;
Writeln(s);
bJsonObject:=TJsonObject.Create;
bJsonObject.Parse(BytesOf(s),0,length(s));
bTestObj:=TTestObject.Unmarshal(bJsonObject) as TTestObject;
writeln(bTestObj.List.Text);
writeln('TTestObject marshaling complete.');
readln;
aList := TTestObjectList<TTestObject>.Create;
aList.Add(TTestObject.Create(['one','two']));
aList.Add(TTestObject.Create(['three']));
aJsonObject := aList.Marshal;
s:=aJsonObject.ToString;
Writeln(s);
cJSonObject:=TJsonObject.Create;
cJSonObject.Parse(BytesOf(s),0,length(s));
bList:=TTestObjectList<TTestObject>.Unmarshal(cJSonObject) as TTestObjectList<TTestObject>;
for cTestObj in bList do
begin
writeln(cTestObj.List.Text);
end;
writeln('TTestObjectList<TTestObject> marshaling complete.');
Readln;
end;
Here is my own solution.
As I am very fond of polymorphism, I actually also want a solution that can be built into an object hierarchy. Lets say TTestObject and TTestObjectList is our BASE object. And from that we descend to TMyObject and also TMyObjectList. And furthermore I've made changes to both Object and List - added properties for Marshaller/UnMarshaller
TMyObject = class(TTestObject) and TMyObjectList<T:TMyObject> = class(TTestObjectList)
With this we now introduce some new problems. Ie. how to handle marshalling of different types between lines in the hierarchy and how to handle TJsonMarshal and TJsonUnMarshal as properties on TTestObject and List.
This can be overcome by introducing two new methods on TTestObject level. Two class functions called RegisterConverters and RegisterReverters. Then we go about and change the marshal function of TTestObjectList into a more simpel marshalling.
Two class functions and properties for both object and List.
class procedure RegisterConverters(aClass: TClass; aMar: TJSONMarshal); virtual;
class procedure RegisterReverters(aClass: TClass; aUnMar: TJSONUnMarshal); virtual;
property Mar: TJSONMarshal read FMar write SetMar;
property UnMar: TJSONUnMarshal read FUnMar write SetUnMar;
The Marshal function of List can now be done like this:
function TObjectList<T>.Marshal: TJSONObject;
begin
if FMar = nil then
FMar := TJSONMarshal.Create(); // thx. to SilverKnight
try
RegisterConverters; // Virtual class method !!!!
try
Result := FMar.Marshal(Self) as TJSONObject;
except
on e: Exception do
raise Exception.Create('Marshal Error : ' + e.Message);
end;
finally
ClearMarshal; // FreeAndNil FMar and FUnMar if assigned.
end;
end;
Sure we can still have a marshaller for our TTestObject - but the Marshal function of TTestObjectList will NOT use it. This way only ONE Marshaller will get created when calling Marshal of TTestObjectList (or descendants). And this way we end up getting marshalled ONLY the information we need to recreate our structure when doing it all backwards - UnMarshalling :-)
Now this actually works - but I wonder if anyone has any comments on this ?
Lets add a property "TimeOfCreation" to TMyTestObject:
property TimeOfCreation : TDateTime read FTimeOfCreation write FTimeOfCreation;
And set the property in the constructor.
FTimeofCreation := now;
And then we need a Converter so we override the virtual RegisterConverters of TTestObject.
class procedure TMyTestObject.RegisterConverters(aClass: TClass; aMar: TJSONMarshal);
begin
inherited; // instanciate marshaller and register TTestObject converters
aMar.RegisterConverter(aClass, 'FTimeOfCreation',
function(Data: TObject; Field: String): string
var
ctx: TRttiContext;
date: TDateTime;
begin
date := ctx.GetType(Data.ClassType).GetField(Field).GetValue(Data).AsType<TDateTime>;
Result := FormatDateTime('yyyy-mm-dd hh:nn:ss', date);
end);
end;
I end up with Very simple source like using TTestObject ie.
aList := TMyTestObjectList<TMyTestObject>.Create;
aList.Add(TMyTestObject.Create(['one','two']));
aList.Add(TMyTestObject.Create(['three']));
s := (aList.Marshal).ToString;
Writeln(s);
And now I have succeded in marshalling with polymorphism :-)
This also works with UnMarshalling btw. And Im in the process of rebuilding my FireBird ORM to produce source for all my objects like this.
The current OLD version can be found here :
http://code.google.com/p/objectgenerator/
Remember that it only works for FireBird :-)
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.
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.