We have a funny one.
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
ITestInterface = interface(IInvokable)
['{4059D1CD-A342-48EE-B796-84B8B5589AED}']
function GetPort: string;
function GetRoot: string;
end;
TTestInterface = class(TInterfacedObject, ITestInterface)
private
FPort: string;
FRoot: string;
public
constructor Create(FileName: TFileName);
destructor Destroy; override;
function GetPort: string;
function GetRoot: string;
end;
{ TTestInterface }
constructor TTestInterface.Create(FileName: TFileName);
begin
FPort := '8080';
FRoot := 'top';
end;
destructor TTestInterface.Destroy;
begin
// ^ Place Breakpoint here
inherited;
end;
function TTestInterface.GetPort: string;
begin
Result := FPort;
end;
function TTestInterface.GetRoot: string;
begin
Result := FRoot;
end;
type
TTestService = class
protected
FTest : TTestInterface;
public
constructor Create;
destructor Destroy; override;
procedure Process;
end;
{ TTestService }
constructor TTestService.Create;
begin
FTest := TTestInterface.Create('');
(FTest as IInterface)._AddRef;
end;
destructor TTestService.Destroy;
begin
FTest.Free;
inherited;
end;
procedure TTestService.Process;
begin
writeln( 'Config Root: ', FTest.GetRoot );
writeln( 'Config Port: ', FTest.GetPort );
end;
var
TS : TTestService;
begin
TS := TTestService.Create;
try
TS.Process;
finally
TS.Free;
end;
end.
When this application finishes it generates an Invalid Pointer Operation.
The really strange part is that setting a break point on the destructor, you can see that it generates the error the first time it gets called, which rules out it being freed twice. It is almost as if the object is dumped from memory without calling the destructor at all.
By removing the _AddRef everything works as expected.
We managed to produce this on Delphi 6. Can anyone confirm this behavior on any other version?
Use two variables: one for the class, and one for the interface.
Use the interface variable to manage the instance lifetime. Don't call free, but set the interface variable to nil (or out of scope) to let the instance running.
Use the class variable to have direct raw access to the instance, if needed - but it shouldn't be the case, or at least let the class be accessible only from protected/private members of the owner class.
So your code becomes:
type
TTestService = class
protected
FTest: ITestInterface;
FTestInstance : TTestInterface;
public
constructor Create;
procedure Process;
end;
{ TTestService }
constructor TTestService.Create;
begin
FTestInstance := TTestInterface.Create('');
FTest := FTestInstance;
end;
procedure TTestService.Process;
begin
writeln( 'Config Root: ', FTest.GetRoot );
writeln( 'Config Port: ', FTest.GetPort );
end;
var
TS : TTestService;
begin
TS := TTestService.Create;
try
TS.Process;
finally
TS.Free;
end;
end.
The problem is that you are manually freeing an interfaced object that has a reference count greater than zero. The exception is raised here :
procedure TInterfacedObject.BeforeDestruction;
begin
if RefCount <> 0 then {!! RefCount is still 1 - you made it that way!}
Error(reInvalidPtr);
end;
So... you could just call (FTest as IInterface)._Release; in the destructor in place of FTest.Free, but this feels like fixing one mistake by making another. Either you want reference counting or you don't - if you do, then you should work with the object in that way (using interface variables and letting scope and variable lifetime manage the object lifetime). If you don't want reference counting then disable it. Either way you should pick a lifetime management model and work with it in the normal way.
Case 1 : Disable Reference Counting
If you want to disable automatic reference counting and you're using Delphi 2009 or higher you can simply do this by inheriting from TSingletonImplementation instead of TInterfacedObject :
TTestInterface = class(TSingletonImplementation, ITestInterface)
private
FPort: string;
FRoot: string;
public
constructor Create(FileName: TFileName);
destructor Destroy; override;
function GetPort: string;
function GetRoot: string;
end;
Otherwise, you can implement this yourself by adding the required methods :
TTestInterface = class(TObject, ITestInterface)
private
FPort: string;
FRoot: string;
{ ** Add interface handling methods ** }
protected
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
{ ** ---------------------- ** }
public
constructor Create(FileName: TFileName);
destructor Destroy; override;
function GetPort: string;
function GetRoot: string;
end;
which you implement as :
function TTestInterface.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
if GetInterface(IID, Obj) then
Result := S_OK
else
Result := E_NOINTERFACE;
end;
function TTestInterface._AddRef: Integer;
begin
Result := -1;
end;
function TTestInterface._Release: Integer;
begin
Result := -1;
end;
Case 2 : Use Interface References Normally
If you absolutely need reference counting and you still need to access the concrete class members then the simplest solution is to strictly use interface variables, let your container class pin the object lifetime, and cast to the concrete type when needed. Lets introduce some state to the class :
TTestInterface = class(TInterfacedObject, ITestInterface)
private
FPort: string;
FRoot: string;
public
Foo : integer; { not an interface member...}
constructor Create(FileName: TFileName);
destructor Destroy; override;
function GetPort: string;
function GetRoot: string;
end;
Your container class then becomes :
type
TTestService = class
protected
FTest : ITestInterface;
public
constructor Create;
procedure Process;
end;
{ TTestService }
constructor TTestService.Create;
begin
FTest := TTestInterface.Create('');
end;
procedure TTestService.Process;
begin
writeln( 'Config Root: ', FTest.GetRoot );
writeln( 'Config Port: ', FTest.GetPort );
WriteLn( 'Foo : ', TTestInterface(FTest).Foo); {Cast to access class members}
end;
Note that the above cast of TTestInterface(FTest) only works in Delphi 2010 and higher. For versions older than this you must keep a separate object reference as in #ArnaudBouchez's answer. In either case, the point is to use interface references in the normal way to manage the object lifetime and to not rely on hacking the reference count manually.
Related
Is there a way to invoke Create of the subclass from the parent class? Below there is this Duplicate method in which I want the constructor of the subclass to be invoked instead, so that the test at the bottom succeeds.
type
IBla<T> = interface(IInvokable)
['{34E812BF-D021-422A-A051-A492F25534C4}']
function GetIntFromIface(): Integer;
function Duplicate(): IBla<T>;
end;
TClassA<T> = class(TInterfacedObject, IBla<T>)
protected
function GetInt(): Integer; virtual;
public
function GetIntFromIface(): Integer;
function Duplicate(): IBla<T>;
end;
TClassB = class(TClassA<Integer>, IBla<Integer>)
protected
function GetInt(): Integer; override;
end;
function TClassA<T>.Duplicate: IBla<T>;
begin
Exit(TClassA<T>.Create());
end;
function TClassA<T>.GetInt: Integer;
begin
Exit(1);
end;
function TClassA<T>.GetIntFromIface: Integer;
begin
Exit(GetInt());
end;
function TClassB.GetInt: Integer;
begin
Exit(2);
end;
procedure TestRandomStuff.Test123;
var
o1, o2: IBla<Integer>;
begin
o1 := TClassB.Create();
o2 := o1.Duplicate();
Assert.AreEqual(o2.GetIntFromIface, 2);
end;
You can do this using RTTI:
uses
System.Rtti;
....
function TClassA<T>.Duplicate: IBla<T>;
var
ctx: TRttiContext;
typ: TRttiType;
mthd: TRttiMethod;
inst: TValue;
begin
typ := ctx.GetType(ClassInfo);
mthd := typ.GetMethod('Create');
inst := mthd.Invoke((typ as TRttiInstanceType).MetaclassType, []);
inst.AsObject.GetInterface(IBla<T>, Result);
end;
There is quite probably a cleaner way to invoke a constructor using RTTI (I know next to nothing about RTTI in Delphi), so you might do well to read around that topic rather than taking the above as being the canonical way to do this.
Of course, this assumes that all subclasses use a parameterless constructor defined in TObject. That might be rather limiting. I would not be surprised if you found yourself having to re-think the design in a more fundamental manner.
If none of your subclasses implement constructors then you could make it even simpler, and not use RTTI at all:
function TClassA<T>.Duplicate: IBla<T>;
begin
ClassType.Create.GetInterface(IBla<T>, Result);
end;
But be aware that this calls the constructor defined in TObject and will not call any constructor defined in a subclass.
This seems to work:
function TClassA<T>.Duplicate: IBla<T>;
begin
//Exit(TClassA<T>.Create());
Exit( ClassType.Create as TClassA<T> );
end;
The subtlety is that ClassType.Create will create (in this case) a TClassB and the original creates a TClassA< integer > which the compiler sees as different to TClassB, and hence calls TClassA< T >.GetInt rather than TClassB.GetInt.
Edit
But be aware that this calls the constructor defined in TObject and will not call any constructor defined in a subclass. (With thanks to David H)
However, here is solution that overcomes that restriction too:
interface
type
IBla<T> = interface(IInvokable)
['{34E812BF-D021-422A-A051-A492F25534C4}']
function GetIntFromIface(): Integer;
function Duplicate(): IBla<T>;
end;
TClassA<T> = class(TInterfacedObject, IBla<T>)
protected
function GetInt(): Integer; virtual;
public
constructor Create; virtual;
function GetIntFromIface(): Integer;
function Duplicate(): IBla<T>;
end;
//TClassB = class(TClassA<Integer>)
TClassB = class(TClassA<Integer>, IBla<Integer>)
protected
function GetInt(): Integer; override;
public
constructor Create; override;
function Duplicate(): IBla<Integer>;
end;
procedure Test123;
implementation
constructor TClassA<T>.Create;
begin
inherited Create;
end;
function TClassA<T>.Duplicate: IBla<T>;
begin
Exit(TClassA<T>.Create());
end;
function TClassA<T>.GetInt: Integer;
begin
Exit(1);
end;
function TClassA<T>.GetIntFromIface: Integer;
begin
Exit(GetInt());
end;
constructor TClassB.Create;
begin
inherited Create;
end;
function TClassB.Duplicate: IBla<Integer>;
begin
Result := TClassB.Create;
end;
function TClassB.GetInt: Integer;
begin
Exit(2);
end;
procedure Test123;
var
o1, o2: IBla<Integer>;
begin
o1 := TClassB.Create();
o2 := o1.Duplicate();
Assert( o2.GetIntFromIface = 2);
end;
I'm making a unit in which I throw a thread with BeginThread with a variable that is defined in the class.
Code:
unit practica;
interface
uses Windows;
type
TTest = class
private
public
probando: integer;
procedure iniciar_thread;
procedure load_now;
end;
implementation
procedure TTest.load_now;
begin
Sleep(probando);
end;
procedure TTest.iniciar_thread;
begin
BeginThread(nil, 0, #TTest.load_now, nil, 0, PDWORD(0)^);
end;
end.
Form :
procedure TForm1.testClick(Sender: TObject);
test:TTest;
begin
test := TTest.Create();
test.probando := 1000;
test.iniciar_thread;
end;
When compiling get no error, but when you run the function I get this:
Exception EAccessViolation in module test.exe
System Error. Code5
Runtime error 217
As I solve this?
You cannot use a non-static class method as the thread procedure for BeginThread(). Look at the declaration of BeginThread():
type
TThreadFunc = function(Parameter: Pointer): Integer;
function BeginThread(SecurityAttributes: Pointer; StackSize: LongWord;
ThreadFunc: TThreadFunc; Parameter: Pointer; CreationFlags: LongWord;
var ThreadId: TThreadID): Integer;
As you can see, it is expecting a stand-alone function, not a class method. Even if it did, your class method doesn't even have the correct signature anyway.
Try something more like this instead:
unit practica;
interface
type
TTest = class
private
FThread: Integer;
public
probando: integer;
procedure iniciar_thread;
procedure load_now;
end;
implementation
uses
Windows;
procedure TTest.load_now;
begin
Sleep(probando);
end;
function MyThreadFunc(Parameter: Pointer): Integer;
begin
TTest(Parameter).load_now;
end;
procedure TTest.iniciar_thread;
var
ThreadId: TThreadID;
begin
FThread := BeginThread(nil, 0, MyThreadFunc, Self, 0, ThreadId);
end;
end.
And don't forget to terminate your thread, CloseHandle() the thread handle returned by BeginThread(), and Free() your TTest object when you are done using everything.
Typically, you shouldn't use BeginThread() directly. You should derive a class from TThread instead:
unit practica;
interface
type
TTest = class
public
probando: integer;
procedure iniciar_thread;
end;
implementation
uses
Classes, Windows;
type
TMyThread = class(TThread)
private
FTest: TTest;
protected
procedure Execute; override;
public
constructor Create(ATest: TTest);
end;
constructor TMyThread.Create(ATest: TTest);
begin
inherited Create(False);
FreeOnTerminate := True;
FTest := ATest;
end;
procedure TMyThread.Execute;
begin
Sleep(FTest.probando);
end;
procedure TTest.iniciar_thread;
begin
TMyThread.Create(Self);
end;
end.
After reading many post on StackOverflow about the cons of using automatic reference counting for Interfaces, I started trying to manually reference counting each interface instantiation.
After trying for a full afternoon I give up!
Why do I get Access Violation when I call FreeAndNil(p)?
What follow is a complete listing of my simple unit.
unit fMainForm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm4 = class(TForm)
btn1: TButton;
procedure FormCreate(Sender: TObject);
procedure btn1Click(Sender: TObject);
end;
type
IPersona = interface(IInterface)
['{44483AA7-2A22-41E6-BA98-F3380184ACD7}']
function GetNome: string;
procedure SetNome(const Value: string);
property Nome: string read GetNome write SetNome;
end;
type
TPersona = class(TObject, IPersona)
strict private
FNome: string;
function GetNome: string;
procedure SetNome(const Value: string);
protected
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
public
constructor Create(const ANome: string);
destructor Destroy; override;
end;
var
Form4: TForm4;
implementation
{$R *.dfm}
procedure TForm4.FormCreate(Sender: TObject);
begin
ReportMemoryLeaksOnShutdown := True;
end;
procedure TForm4.btn1Click(Sender: TObject);
var
p: IPersona;
begin
p := TPersona.Create('Fabio');
try
ShowMessage(p.Nome);
finally
FreeAndNil(p);
end;
end;
constructor TPersona.Create(const ANome: string);
begin
inherited Create;
FNome := ANome;
end;
destructor TPersona.Destroy;
begin
inherited Destroy;
end;
function TPersona._AddRef: Integer;
begin
Result := -1
end;
function TPersona._Release: Integer;
begin
Result := -1
end;
function TPersona.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
if GetInterface(IID, Obj) then
Result := S_OK
else
Result := E_NOINTERFACE;
end;
function TPersona.GetNome: string;
begin
Result := FNome;
end;
procedure TPersona.SetNome(const Value: string);
begin
FNome := Value;
end;
end.
The access violation occurs because FreeAndNil receives an untyped var parameter that is expected to be an object reference. You are passing an interface reference which does not meet the requirement. Unfortunately you only find out at runtime. This is, in my view, the strongest point against the use of FreeAndNil.
Your reference counting disables lifetime management by the interface reference counting mechanism. In order to destroy an object you need to call its destructor. And in order to do that you must have access to the destructor. Your interface doesn't expose the destructor (and it should not). So, we can deduce that, in order to destroy the object, you need to have an object reference.
Here are some options:
var
obj: TPersona;
intf: IPersona;
....
obj := TPersona.Create('Fabio');
try
intf := obj;
//do stuff with intf
finally
obj.Free;
// or FreeAndNil(obj) if you prefer
end;
Or you can do it like this
var
intf: IPersona;
....
intf := TPersona.Create('Fabio');
try
//do stuff with intf
finally
(intf as TObject).Free;
end;
You cannot use FreeAndNil() with an interface reference, only an objct reference. Had you left the interface's reference count enabled, you would simply assign nil to the interface reference (or just let it go out of scope) to free the object correctly, eg:
type
TPersona = class(TInterfacedObject, IPersona)
strict private
FNome: string;
function GetNome: string;
procedure SetNome(const Value: string);
public
constructor Create(const ANome: string);
destructor Destroy; override;
end;
procedure TForm4.btn1Click(Sender: TObject);
var
p: IPersona;
begin
p := TPersona.Create('Fabio');
try
ShowMessage(p.Nome);
finally
p := nil;
end;
end;
But since you have disabled the reference count on the interface, you need to go back to using normal object reference variables in your code, eg:
procedure TForm4.btn1Click(Sender: TObject);
var
p: TPersona;
intf: IPersona;
begin
p := TPersona.Create('Fabio');
try
if Supports(p, IPersona, intf) then
ShowMessage(intf.Nome);
finally
FreeAndNil(p);
end;
end;
When working with lists of items where the lists just serve as a temporary container - which list types would you recommend me to use?
I
don't want to destroy the list manually
would like to use a built-in list type (no frameworks, libraries, ...)
want generics
Something which would make this possible without causing leaks:
function GetListWithItems: ISomeList;
begin
Result := TSomeList.Create;
// add items to list
end;
var
Item: TSomeType;
begin
for Item in GetListWithItems do
begin
// do something
end;
end;
What options do I have? This is about Delphi 2009 but for the sake of knowledge please also mention if there is something new in this regard in 2010+.
An (somehow ugly) workaround for this is to create an 'autodestroy' interface along with the list. It must have the same scope so that when the interface is released, your list is destroyed too.
type
IAutoDestroyObject = interface
end;
TAutoDestroyObject = class(TInterfacedObject, IAutoDestroyObject)
strict private
FValue: TObject;
public
constructor Create(obj: TObject);
destructor Destroy; override;
end;
constructor TAutoDestroyObject.Create(obj: TObject);
begin
inherited Create;
FValue := obj;
end;
destructor TAutoDestroyObject.Destroy;
begin
FreeAndNil(FValue);
inherited;
end;
function CreateAutoDestroyObject(obj: TObject): IAutoDestroyObject;
begin
Result := TAutoDestroyObject.Create(obj);
end;
FList := TObjectList.Create;
FListAutoDestroy := CreateAutoDestroyObject(FList);
Your usage example gets more complicated, too.
type
TSomeListWrap = record
List: TSomeList;
AutoDestroy: IAutoDestroyObject;
end;
function GetListWithItems: TSomeListWrap;
begin
Result.List := TSomeList.Create;
Result.AutoDestroy := CreateAutoDestroyObject(Result.List);
// add items to list
end;
var
Item: TSomeItem;
begin
for Item in GetListWithItems.List do
begin
// do something
end;
end;
Inspired by Barry Kelly's blog post here you could implement smart pointers for your purpose like this :
unit Unit80;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Generics.Collections;
type
TMyList =class( TList<Integer>)
public
destructor Destroy; override;
end;
TLifetimeWatcher = class(TInterfacedObject)
private
FWhenDone: TProc;
public
constructor Create(const AWhenDone: TProc);
destructor Destroy; override;
end;
TSmartPointer<T: class> = record
strict private
FValue: T;
FLifetime: IInterface;
public
constructor Create(const AValue: T); overload;
class operator Implicit(const AValue: T): TSmartPointer<T>;
property Value: T read FValue;
end;
TForm80 = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
function getList : TSmartPointer<TMyList>;
{ Public declarations }
end;
var
Form80: TForm80;
implementation
{$R *.dfm}
{ TLifetimeWatcher }
constructor TLifetimeWatcher.Create(const AWhenDone: TProc);
begin
FWhenDone := AWhenDone;
end;
destructor TLifetimeWatcher.Destroy;
begin
if Assigned(FWhenDone) then
FWhenDone;
inherited;
end;
{ TSmartPointer<T> }
constructor TSmartPointer<T>.Create(const AValue: T);
begin
FValue := AValue;
FLifetime := TLifetimeWatcher.Create(procedure
begin
AValue.Free;
end);
end;
class operator TSmartPointer<T>.Implicit(const AValue: T): TSmartPointer<T>;
begin
Result := TSmartPointer<T>.Create(AValue);
end;
procedure TForm80.Button1Click(Sender: TObject);
var i: Integer;
begin
for I in getList.Value do
Memo1.Lines.Add(IntToStr(i));
end;
{ TMyList }
destructor TMyList.Destroy;
begin
ShowMessage('Kaputt');
inherited;
end;
function TForm80.getList: TSmartPointer<TMyList>;
var
x: TSmartPointer<TMyList>;
begin
x := TMyList.Create;
Result := x;
with Result.Value do
begin
Add(1);
Add(2);
Add(3);
end;
end;
end.
Look at getList and Button1click to see its usage.
To fully support what you're after the language would need to support 2 things:
Garbage collector. That's the only thing that gives you the freedom to USE something without bothering with freeing it. I'd welcome a change in Delphi that gave us even partial support for this.
The possibility to define local, initialized variables. Again, I'd really love to see something along those lines.
Meanwhile, the closest you can get is to use Interfaces in place of garbage collection (because interfaces are reference-counted, once they go out of scope they'll be released). As for initialized local variables, you could use a trick similar to what I'm describing here: Declaring block level variables for branches in delphi
And for the sake of fun, here's a Console application that demonstrates the use of "fake" local variables and Interfaces to obtain temporary lists that are readily initialized will be automatically freed:
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils, Generics.Collections;
type
ITemporaryLocalVar<T:constructor> = interface
function GetL:T;
property L:T read GetL;
end;
TTemporaryLocalVar<T:constructor> = class(TInterfacedObject, ITemporaryLocalVar<T>)
public
FL: T;
constructor Create;
destructor Destroy;override;
function GetL:T;
end;
TTempUse = class
public
class function L<T:constructor>: ITemporaryLocalVar<T>;
end;
{ TTemporaryLocalVar<T> }
constructor TTemporaryLocalVar<T>.Create;
begin
FL := T.Create;
end;
destructor TTemporaryLocalVar<T>.Destroy;
begin
TObject(FL).Free;
inherited;
end;
function TTemporaryLocalVar<T>.GetL: T;
begin
Result := FL;
end;
{ TTempUse }
class function TTempUse.L<T>: ITemporaryLocalVar<T>;
begin
Result := TTemporaryLocalVar<T>.Create;
end;
var i:Integer;
begin
try
with TTempUse.L<TList<Integer>> do
begin
L.Add(1);
L.Add(2);
L.Add(3);
for i in L do
WriteLn(i);
end;
ReadLn;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
The standard list classes, like TList, TObjectList, TInterfaceList, etc, do not implement automated lifecycles, so you have to free them manually when you are done using them. If you want a list class that is accessible via an interface, you have to implement that yourself, eg:
type
IListIntf = interface
...
end;
TListImpl = class(TInterfacedObject, IListIntf)
private
FList: TList;
...
public
constructor Create; override;
destructor Destroy; override;
...
end;
constructor TListImpl.Create;
begin
inherited;
FList := TList.Create;
end;
destructor TListImpl.Destroy;
begin
FList.Free;
inherited;
end;
function GetListWithItems: IListIntf;
begin
Result := TListImpl.Create;
// add items to list
end;
Another option is to implement a generic IEnumerable adapter (as one of the ways to satisfy the for .. in compiler requirement) and rely on reference counting of the interface. I don't know if the following works in Delphi 2009, it seems to work in Delphi XE:
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils, Classes,
Generics.Collections;
type
// IEnumerator adapter for TEnumerator
TInterfacedEnumerator<T> = class(TInterfacedObject, IEnumerator<T>)
private
FEnumerator: TEnumerator<T>;
public
constructor Create(AEnumerator: TEnumerator<T>);
destructor Destroy; override;
function IEnumerator<T>.GetCurrent = GetCurrent2;
{ IEnumerator }
function GetCurrent: TObject;
function MoveNext: Boolean;
procedure Reset;
{ IEnumerator<T> }
function GetCurrent2: T;
end;
// procedure used to fill the list
TListInitProc<T> = reference to procedure(List: TList<T>);
// IEnumerable adapter for TEnumerable
TInterfacedEnumerable<T> = class(TInterfacedObject, IEnumerable<T>)
private
FEnumerable: TEnumerable<T>;
public
constructor Create(AEnumerable: TEnumerable<T>);
destructor Destroy; override;
class function Construct(InitProc: TListInitProc<T>): IEnumerable<T>;
function IEnumerable<T>.GetEnumerator = GetEnumerator2;
{ IEnumerable }
function GetEnumerator: IEnumerator; overload;
{ IEnumerable<T> }
function GetEnumerator2: IEnumerator<T>; overload;
end;
{ TInterfacedEnumerator<T> }
constructor TInterfacedEnumerator<T>.Create(AEnumerator: TEnumerator<T>);
begin
inherited Create;
FEnumerator := AEnumerator;
end;
destructor TInterfacedEnumerator<T>.Destroy;
begin
FEnumerator.Free;
inherited Destroy;
end;
function TInterfacedEnumerator<T>.GetCurrent: TObject;
begin
Result := TObject(GetCurrent2);
end;
function TInterfacedEnumerator<T>.GetCurrent2: T;
begin
Result := FEnumerator.Current;
end;
function TInterfacedEnumerator<T>.MoveNext: Boolean;
begin
Result := FEnumerator.MoveNext;
end;
procedure TInterfacedEnumerator<T>.Reset;
begin
// ?
end;
{ TInterfacedEnumerable<T> }
class function TInterfacedEnumerable<T>.Construct(InitProc: TListInitProc<T>): IEnumerable<T>;
var
List: TList<T>;
begin
List := TList<T>.Create;
try
if Assigned(InitProc) then
InitProc(List);
Result := Create(List);
except
List.Free;
raise;
end;
end;
constructor TInterfacedEnumerable<T>.Create(AEnumerable: TEnumerable<T>);
begin
inherited Create;
FEnumerable := AEnumerable;
end;
destructor TInterfacedEnumerable<T>.Destroy;
begin
FEnumerable.Free;
inherited Destroy;
end;
function TInterfacedEnumerable<T>.GetEnumerator: IEnumerator;
begin
Result := GetEnumerator2;
end;
function TInterfacedEnumerable<T>.GetEnumerator2: IEnumerator<T>;
begin
Result := TInterfacedEnumerator<T>.Create(FEnumerable.GetEnumerator);
end;
type
TSomeType = record
X, Y: Integer;
end;
function GetList(InitProc: TListInitProc<TSomeType>): IEnumerable<TSomeType>;
begin
Result := TInterfacedEnumerable<TSomeType>.Construct(InitProc);
end;
procedure MyInitList(List: TList<TSomeType>);
var
NewItem: TSomeType;
I: Integer;
begin
for I := 0 to 9 do
begin
NewItem.X := I;
NewItem.Y := 9 - I;
List.Add(NewItem);
end;
end;
procedure Main;
var
Item: TSomeType;
begin
for Item in GetList(MyInitList) do // you could also use an anonymous procedure here
Writeln(Format('X = %d, Y = %d', [Item.X, Item.Y]));
Readln;
end;
begin
try
ReportMemoryLeaksOnShutdown := True;
Main;
except
on E: Exception do
begin
ExitCode := 1;
Writeln(Format('[%s] %s', [E.ClassName, E.Message]));
end;
end;
end.
No, not 'out of the box' in Delphi.
I know that you don't need a library but you may be interessed by the principle of TDynArray.
In Jedi Code Library, exist the Guard function that already implements what
Gabr's code does.
TMyBaseClass=class
constructor(test:integer);
end;
TMyClass=class(TMyBaseClass);
TClass1<T: TMyBaseClass,constructor>=class()
public
FItem: T;
procedure Test;
end;
procedure TClass1<T>.Test;
begin
FItem:= T.Create;
end;
var u: TClass1<TMyClass>;
begin
u:=TClass1<TMyClass>.Create();
u.Test;
end;
How do I make it to create the class with the integer param. What is the workaround?
Just typecast to the correct class:
type
TMyBaseClassClass = class of TMyBaseClass;
procedure TClass1<T>.Test;
begin
FItem:= T(TMyBaseClassClass(T).Create(42));
end;
Also it's probably a good idea to make the constructor virtual.
You might consider giving the base class an explicit method for initialization instead of using the constructor:
TMyBaseClass = class
public
procedure Initialize(test : Integer); virtual;
end;
TMyClass = class(TMyBaseClass)
public
procedure Initialize(test : Integer); override;
end;
procedure TClass1<T>.Test;
begin
FItem:= T.Create;
T.Initialize(42);
end;
Of course this only works, if the base class and all subclasses are under your control.
Update
The solution offered by #TOndrej is far superior to what I wrote below, apart from one situation. If you need to take runtime decisions as to what class to create, then the approach below appears to be the optimal solution.
I've refreshed my memory of my own code base which also deals with this exact problem. My conclusion is that what you are attempting to achieve is impossible. I'd be delighted to be proved wrong if anyone wants to rise to the challenge.
My workaround is for the generic class to contain a field FClass which is of type class of TMyBaseClass. Then I can call my virtual constructor with FClass.Create(...). I test that FClass.InheritsFrom(T) in an assertion. It's all depressingly non-generic. As I said, if anyone can prove my belief wrong I will upvote, delete, and rejoice!
In your setting the workaround might look like this:
TMyBaseClass = class
public
constructor Create(test:integer); virtual;
end;
TMyBaseClassClass = class of TMyBaseClass;
TMyClass = class(TMyBaseClass)
public
constructor Create(test:integer); override;
end;
TClass1<T: TMyBaseClass> = class
private
FMemberClass: TMyBaseClassClass;
FItem: T;
public
constructor Create(MemberClass: TMyBaseClassClass); overload;
constructor Create; overload;
procedure Test;
end;
constructor TClass1<T>.Create(MemberClass: TMyBaseClassClass);
begin
inherited Create;
FMemberClass := MemberClass;
Assert(FMemberClass.InheritsFrom(T));
end;
constructor TClass1<T>.Create;
begin
Create(TMyBaseClassClass(T));
end;
procedure TClass1<T>.Test;
begin
FItem:= T(FMemberClass.Create(666));
end;
var
u: TClass1<TMyClass>;
begin
u:=TClass1<TMyClass>.Create(TMyClass);
u.Test;
end;
Another more elegant solution, if it is possible, is to use a parameterless constructor and pass in the extra information in a virtual method of T, perhaps called Initialize.
What seems to work in Delphi XE, is to call T.Create first, and then call the class-specific Create as a method afterwards. This is similar to Rudy Velthuis' (deleted) answer, although I don't introduce an overloaded constructor. This method also seems to work correctly if T is of TControl or classes like that, so you could construct visual controls in this fashion.
I can't test on Delphi 2010.
type
TMyBaseClass = class
FTest: Integer;
constructor Create(test: integer);
end;
TMyClass = class(TMyBaseClass);
TClass1<T: TMyBaseClass, constructor> = class
public
FItem: T;
procedure Test;
end;
constructor TMyBaseClass.Create(test: integer);
begin
FTest := Test;
end;
procedure TClass1<T>.Test;
begin
FItem := T.Create; // Allocation + 'dummy' constructor in TObject
try
TMyBaseClass(FItem).Create(42); // Call actual constructor as a method
except
// Normally this is done automatically when constructor fails
FItem.Free;
raise;
end;
end;
// Calling:
var
o: TClass1<TMyClass>;
begin
o := TClass1<TMyClass>.Create();
o.Test;
ShowMessageFmt('%d', [o.FItem.FTest]);
end;
type
TBase = class
constructor Create (aParam: Integer); virtual;
end;
TBaseClass = class of TBase;
TFabric = class
class function CreateAsBase (ConcreteClass: TBaseClass; aParam: Integer): TBase;
class function CreateMyClass<T: TBase>(aParam: Integer): T;
end;
TSpecial = class(TBase)
end;
TSuperSpecial = class(TSpecial)
constructor Create(aParam: Integer); override;
end;
class function TFabric.CreateAsBase(ConcreteClass: TBaseClass; aParam: Integer): TBase;
begin
Result := ConcreteClass.Create (aParam);
end;
class function TFabric.CreateMyClass<T>(aParam: Integer): T;
begin
Result := CreateAsBase (T, aParam) as T;
end;
// using
var
B: TBase;
S: TSpecial;
SS: TSuperSpecial;
begin
B := TFabric.CreateMyClass <TBase> (1);
S := TFabric.CreateMyClass <TSpecial> (1);
SS := TFabric.CreateMyClass <TSuperSpecial> (1);