Problem with generics and interfaces and inheritance - delphi

The following example is giving me this error:
[DCC Error] Unit2.pas(54): E2010 Incompatible types: 'IBar' and 'Unit2.TFoo<Unit2.IBar>'
I think the problem is somewhere around the Self.Create
Because after many tries to get it compiled I accidentally entered FFoo := TBar(Self).Create; and it compiled and worked.
I'm using Delphi XE
IFoo = interface
end;
TFoo<T: IInterface> = class(TInterfacedObject, IFoo)
private class var
FFoo: T;
public class
function Instance: T;
end;
IBar = interface(IFoo)
end;
TBar = class(TFoo<IBar>, IBar)
end;
class function TFoo<T>.Instance: T;
begin
if not Assigned(FFoo) then
begin
FFoo := Self.Create;
end;
Result := FFoo;
end;

The problem is in this line with the TBar declaration:
FFoo := Self.Create;
To understand, let's explain the types behind the code [noted like this]:
FFoo:[IBar] := Self:[TFoo(IBar)].Create():[TFoo<IBar>]
So, tu summarize, we have : [IBar] := [TFoo<IBar>]
Are these types compatible ?
A [TFoo] only implements IFoo interface, no IBar as it is stated in your code
TFoo<T: IInterface> = class(TInterfacedObject, IFoo)
This is the compilation error !
UPDATE : Solution 1
To fix the issue : change the TBar declaration
TBar = class(TFoo<IFoo>, IBar)
end;
UPDATE : Solution 2
Replace the FFoo := Self.Create by
FFoo := Self.Create.Instance;
and so it works !

Your TFoo does not implement T as interface. That's why FFoo and an instance of TFoo is not compatible. If you want to assign an instance of TFoo to FFoo you need to hardcast it.

Related

Generic method in property write

I came up with the following for an easy to expand "bit bucket":
unit BitBucket;
interface
type TBitBucket = class
private
class procedure ThrowAway<T>(value: T); static;
public
class property Integer: Integer write ThrowAway;
class property String_: String write ThrowAway;
class property Extended: Extended write ThrowAway;
class property Boolean: Boolean write ThrowAway;
end;
implementation
class procedure TBitBucket.ThrowAway<T>(value: T);
begin
end;
end.
However, although there's no squiggly underlines in the IDE, it won't compile, with the following errors:
[dcc32 Error] BitBucket.pas(9): E2008 Incompatible types
[dcc32 Error] BitBucket.pas(10): E2008 Incompatible types
[dcc32 Error] BitBucket.pas(11): E2008 Incompatible types
[dcc32 Error] BitBucket.pas(12): E2008 Incompatible types
Is there a trick I'm missing that will make this compile? I've tried specifying the generic type argument to ThrowAway, but that causes even more errors. The obvious alternative is to write a ThrowAway method for every type, but that would quickly lead to a lot of code to do effectively nothing.
For those wondering why, in delphi, you can use a compiler switch to prevent use of functions without assigning their return value for compatibility with older code. With a BitBucket you can say BitBucket.Integer := FunctionThatHasSideEffectsAndReturnsAnInteger(...);, without having to create a new variable. I also think it's just funny.
You are confusing a generic with a variant. You need something like this:
unit BitBucket;
interface
type
TBitBucket<T> = class
private
class procedure ThrowAway(const Value: T); static;
class var FVar: T;
public
class property MyProperty: T read FVar write ThrowAway;
end;
implementation
class procedure TBitBucket<T>.ThrowAway(const Value: T);
begin
FVar := Value;
end;
end.
The type is not decided until runtime when you access it i.e
TBitBucket<Integer>.MyProperty := 2;
This is the shortest solution i could come up with
type TBitBucket = class
class var ThrowAway: variant;
end;
Usage
type test = class
procedure Test;
end;
implementation
{ test }
procedure test.Test;
begin
TBitBucket.ThrowAway := 'AString';
TBitBucket.ThrowAway := 1;
TBitBucket.ThrowAway := 1.1234;
TBitBucket.ThrowAway := true;
end;
Tvalue example
And here a example with TValue instead from system.RTTI allowing to put objects into the bucket
type TBitBucket = class
class var ThrowAway: Tvalue;
end;
type test = class
procedure Test;
end;
implementation
{ test }
procedure test.Test;
begin
TBitBucket.ThrowAway := 'AString';
TBitBucket.ThrowAway := 1;
TBitBucket.ThrowAway := 1.1234;
TBitBucket.ThrowAway := true;
TBitBucket.ThrowAway := TObject.Create;
end;

"Abstract Error" shows up when calling methods from child classes

I want to instantiate classes based on a parameter. Both classes are derived from TSample so I define my code as:
var T: TSample;
then I do
T := TMySample.Create;
or
T := TYourSample.Create;
and calling T.Hello gives an "Abstract Error".
type TSample = class
public
procedure Hello; virtual; abstract;
end;
TMySample = class(TSample)
public
procedure Hello;
end;
TYourSample = class(TSample)
public
procedure Hello;
end;
...
procedure TForm1.Button1Click(Sender: TObject);
var T: TSample;
a: Integer;
begin
if a = 1 then T := TMySample.Create
else T := TYourSample.Create;
T.Hello; //Abstract Error here
T.Free;
end;
procedure TMySample.Hello;
begin
showmessage('My');
end;
procedure TYourSample.Hello;
begin
showmessage('Your');
end;
You forgot to declare the overridden methods as, well, overridden:
TMySample = class(TSample)
public
procedure Hello; override; // <--
end;
TYourSample = class(TSample)
public
procedure Hello; override; // <--
end;
Actually, the compiler warned you about this, but you didn't listen :)
[dcc32 Warning] Unit1.pas(25): W1010 Method 'Hello' hides virtual method of base type 'TSample'
[dcc32 Warning] Unit1.pas(30): W1010 Method 'Hello' hides virtual method of base type 'TSample'
Also, you probably already know this, but there are two issues with your sample code:
Since local variables of non-managed types are not initialized, the value of a is undefined.
You don't protect the TSample object, so you might leak resources. (In fact, in this case, you will due to the exception!)
Fixed:
a := 123;
if a = 1 then
T := TMySample.Create
else
T := TYourSample.Create;
try
T.Hello; //Abstract Error here
finally
T.Free;
end;

Passing self as parameter in Delphi [duplicate]

This question already has answers here:
Two classes with two circular references
(2 answers)
Closed 9 years ago.
I would like to pass "self" as parameter to a method of another class (in a different unit). However the type of the first class is unknown in the second one, because I can't put the first unit into the uses section of the second unit. So I define the parameters type as pointer but when I try to call a method from the first class the Delphi 7 parser tells me that the classtyp is required.
So how should I solve this problem?
By making the class known in the implementaion part you can cast the given reference.
unit UnitY;
interface
uses Classes;
type
TTest=Class
Constructor Create(AUnKnowOne:TObject);
End;
implementation
uses UnitX;
{ TTest }
constructor TTest.Create(AUnKnowOne: TObject);
begin
if AUnKnowOne is TClassFromUnitX then
begin
TClassFromUnitX(AUnKnowOne).DoSomeThing;
end
else
begin
// ....
end;
end;
end.
I like the interface approach for this type of problem. Unless your units are very tightly coupled, in which case they should probably share a unit, interfaces are tidy ways of exchanging relevant parts of classes without having to have full knowledge of each type.
Consider :
unit UnitI;
interface
type
IDoSomething = Interface(IInterface)
function GetIsFoo : Boolean;
property isFoo : Boolean read GetIsFoo;
end;
implementation
end.
and
unit UnitA;
interface
uses UnitI;
type
TClassA = class(TInterfacedObject, IDoSomething)
private
Ffoo : boolean;
function GetIsFoo() : boolean;
public
property isFoo : boolean read GetIsFoo;
procedure DoBar;
constructor Create;
end;
implementation
uses UnitB;
constructor TClassA.Create;
begin
Ffoo := true;
end;
function TClassA.GetIsFoo() : boolean;
begin
result := Ffoo;
end;
procedure TClassA.DoBar;
var SomeClassB : TClassB;
begin
SomeClassB := TClassB.Create;
SomeClassB.DoIfFoo(self);
end;
end.
and notice that TClassB does not have to know anything about TClassA or the unit that contains it - it simply accepts any object that abides by the IDoSomething interface contract.
unit UnitB;
interface
uses UnitI;
type
TClassB = class(TObject)
private
Ffoobar : integer;
public
procedure DoIfFoo(bar : IDoSomething);
constructor Create;
end;
implementation
constructor TClassB.Create;
begin
Ffoobar := 3;
end;
procedure TClassB.DoIfFoo(bar : IDoSomething);
begin
if bar.isFoo then Ffoobar := 777;
end;
end.

Not able to assign a TMyClass object to a variable of its base interface type

Why I cannot assign a class to a variable of its base interface type.
Consider we have the following:
IInt1 = interface
procedure Test1;
end;
IInt2 = interface(IInt1)
procedure Test2;
end;
TCustomClass = class(TInterfacedObject)
end;
TMyClass = class(TCustomClass, IInt2)
procedure Test1;
procedure Test2;
end;
var
Obj: IInt1;
begin
Obj := TMyClass.Create; // <!-- E2010 Incompatible types: 'IInt1' and 'TMyClass'
...
end;
E2010 Incompatible types: 'IInt1' and 'TMyClass'
Is this a bug in the compiler or simply not an allowed action. Can this be worked around?
Include IInt1 in the list of implemented interfaces in your declaration:
TMyClass = class(TCustomClass, IInt1, IInt2)
end;
There are three errors above, as follows:
The class declarations must contain all members required by the interface. TMyClass must therefore have procedures Test1 and Test2 defined.
Interfaces must have a GUID, as shown in the code below. Place your cursor where you want the GUID to appear, and press CTRL-SHIFT-G to get the IDE to create this for you.
When you assign the new class, you need to use the AS keyword for the typecast.
Your snippet of code, therefore, should look like this
IInt1 = interface
['{218BE5BB-0784-4E4A-923C-F1F1E116F38B}']
procedure Test1;
end;
IInt2 = interface(IInt1)
['{635D00B2-AC59-43A9-A7DB-660F53EBCDD8}']
procedure Test2;
end;
TCustomClass = class(TInterfacedObject)
private
procedure Test1;
end;
TMyClass = class(TCustomClass, IInt2)
private
procedure Test2;
end;
...
var
Obj: IInt1;
begin
Obj := TMyClass.Create AS IInt1;
...

Interface helpers or delegating interface parent

If I have an existing IInterface descendant implemented by a third party, and I want to add helper routines, does Delphi provide any easy way to do so without redirecting every interface method manually? That is, given an interface like so:
IFoo = interface
procedure Foo1;
procedure Foo2;
...
procedure FooN;
end;
Is anything similar to the following supported?
IFooHelper = interface helper for IFoo
procedure Bar;
end;
or
IFooBar = interface(IFoo)
procedure Bar;
end;
TFooBar = class(TInterfacedObject, IFoo, IFooBar)
private
FFoo: IFoo;
public
procedure Bar;
property Foo: IFoo read FFoo implements IFoo;
end;
I'm specifically wondering about ways to that allow me to always call Foo1, Foo2, and Bar with a single variable reference (IFoo, IFooBar, or TFooBar), without switching between them, and without adding all of IFoo's methods to TFooBar.
Change your class to read:
TFooBar = Class(TInterfacedObject, IFoo, IFooBar)
private
FFoo: IFoo;
public
procedure Bar;
property Foo: IFoo read FFoo implements IFoo ;
end;
You will also need a constructor or some method to create the instance of IFoo and place it in FFoo.
You cannot access the methods of IFoo through a reference to TFooBar because TFooBar doesn't implement IFoo - it delegates IFoo. But you shouldn't be using a TFooBar reference to access an interfaced object anyway, that's the whole point of using interfaces in the first place!
NOTE: To deliberately prevent this I adopt a convention of implementing interface methods on my classes as "protected", specifically to deny access to those implementation details except as surfaced via the interface itself.
Where-ever you are currently obtaining a reference to TFooBar, change this to instead obtain the IFooBar interface implemented by TFooBar and you will be sorted.
I'm not sure I understood all of your concerns, but here's my suggestion anyway :
IFooBar = interface(IFoo)
procedure Bar;
end;
TFooDelegate = class(TInterfacedObject, IFoo )
private
FFoo: IFoo;
public
property Foo: IFoo read FFoo implements IFoo;
end;
TFooBar = class( TFooDelegate, IFooBar )
procedure Bar;
end;
see
https://docwiki.embarcadero.com/RADStudio/Sydney/en/Implementing_Interfaces:_Delphi_and_C%2B%2B
on how to use TAggregatedObject together with "implements" syntax at a property
quoting from there:
unit Unit1;
interface
type
// Interface that exposes an 'Add' method
IAdder = interface
['{D0C74612-9E4D-459A-9304-FACE27E3577D}']
function Add(I, J: Integer): Integer;
end;
// Aggregatee that implements IAdder
TAdder = class(TAggregatedObject, IAdder)
function Add(I, J: Integer): Integer;
end;
// Aggregator - implements IAdder via TAdder
TPasClass = class(TInterfacedObject, IAdder)
FAdder: TAdder;
function GetAdder: TAdder;
public
destructor Destroy; override;
property Adder: TAdder read GetAdder write FAdder implements IAdder;
end;
function TestAdd(I, J: Integer): Integer;
implementation
{ TAdder }
function TAdder.Add(I, J: Integer): Integer;
begin
Result := I+J;
end;
{ TPasClass }
destructor TPasClass.Destroy;
begin
FAdder.Free;
inherited;
end;
function TPasClass.GetAdder: TAdder;
begin
if FAdder = nil then
FAdder := TAdder.Create(Self as IInterface);
Result := FAdder;
end;
// Adds using TClass' IAdder
function TestAdd(I, J: Integer): Integer;
var
punk: IInterface;
begin
punk := TPasClass.Create as IInterface;
Result := (punk as IAdder).Add(I, J);
end;
end.
however it would be nice if one could do:
TSomeClassIFoo123Helper = class helper(IFoo1, IFoo2, IFoo3) for TSomeClass
procedure Bar1;
procedure Bar2;
procedure Bar3;
end;
injecting IFoo1, IFoo2, IFoo3 interface implementations into TSomeClass. The helpers support inheritance, but can only specify another helper class at the list, not one ore more interfaces there

Resources