I am trying to create a class that implements an interface but I get these errors:
[dcc32 Error] dl_tPA_MailJournal.pas(10): E2291 Missing implementation of interface method IInterface.QueryInterface
[dcc32 Error] dl_tPA_MailJournal.pas(10): E2291 Missing implementation of interface method IInterface._AddRef
[dcc32 Error] dl_tPA_MailJournal.pas(10): E2291 Missing implementation of interface method IInterface._Release
[dcc32 Fatal Error] MainUnit.pas(8): F2063 Could not compile used unit 'dl_tPA_MailJournal.pas'
The code is:
unit dl_tPA_MailJournal;
interface
uses
Windows,
Generics.Collections,
SysUtils,
uInterfaces;
type
TtPA_MailJournal = class(TObject, ITable)
public
function GetanQId: integer;
procedure SetanQId(const Value: integer);
function GetadDate: TDateTime;
procedure SetadDate(const Value: TDateTime);
function toList: TList<string>;
constructor Create(aId : Integer; aDate : TDateTime);
private
property anQId : integer read GetanQId write SetanQId;
property adDate : TDateTime read GetadDate write SetadDate;
end;
implementation
{ TtPA_MailJournal }
constructor TtPA_MailJournal.Create(aId : Integer; aDate : TDateTime);
begin
SetanQId(aId);
SetadDate(aDate);
end;
function TtPA_MailJournal.GetadDate: TDateTime;
begin
Result := adDate;
end;
function TtPA_MailJournal.GetanQId: integer;
begin
Result := anQId ;
end;
procedure TtPA_MailJournal.SetadDate(const Value: TDateTime);
begin
adDate := Value;
end;
procedure TtPA_MailJournal.SetanQId(const Value: integer);
begin
anQId := Value;
end;
function TtPA_MailJournal.toList: TList<string>;
var
aListTable: TList<TtPA_MailJournal>;
aTable: TtPA_MailJournal;
aListString: TList<String>;
begin
aTable.Create(1,now);
aListTable.Add(aTable);
aTable.Create(2,now);
aListTable.Add(aTable);
aListString.Add(aListTable.ToString);
Result := aListString;
end;
end.
And the interface is:
unit uInterfaces;
interface
uses
Generics.Collections;
type
ITable = Interface
['{6CED8DCE-9CC7-491F-8D93-996BE8E4D388}']
function toList: TList<string>;
end;
implementation
end.
The problem is that you use TObject as the parent for your class. You should use TInterfacedObject instead.
In Delphi, every interface inherits from IInterface at therefore has, at least, the following 3 methods:
_AddRef
_Release
QueryInterface
You must implement these 3 methods, either by implementing them yourself or by inheriting from a base object that includes these methods.
Because you inherit from TObject, but you are not implementing these 3 methods, you get a compilation error. If you read the compiler error, you will see that it actually spells out this omission for you.
TInterfacedObject has already implemented these methods for you.
Other base objects that implement IInterface (aka IUnknown) are: TAggregatedObject and TContainedObject. However these are special purpose vehicles, only to be used if you really know what you're doing.
Change the definition of your class to
TTPA_MailJournal = class(TInterfacedObject, ITable)
And your code will compile.
See Delphi basics for more info.
Related
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;
I'm struggling with Spring4D (1.2.2) TMultiMap generic class. I want to call an overloaded constructor and the compiler complain:
"E2250 There is no overloaded version of 'Create' that can be called with these arguments"
The argument is the correct type according to the Spring4D source code.
I devised a small do-nothing program reproducing the error:
program Spring4DMultiMapTest;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
Generics.Collections,
Spring.Tests.Interception.Types,
Spring.Collections.MultiMaps,
Spring.Collections;
type
TMyKey = TPair<Double, Integer>;
TMyKeyEqualityComparer = class(TInterfacedObject, IEqualityComparer<TMyKey>)
function Equals(const Left, Right: TMyKey): Boolean; reintroduce;
function GetHashCode(const Value: TMyKey): Integer; reintroduce;
end;
function TMyKeyEqualityComparer.Equals(const Left, Right: TMyKey): Boolean;
begin
if Left.Key < (Right.Key - 1E-9) then
Result := TRUE
else if Left.Key > (Right.Key + 1E-9) then
Result := FALSE
else if Left.Value < Right.Value then
Result := TRUE
else
Result := FALSE;
end;
function TMyKeyEqualityComparer.GetHashCode(const Value: TMyKey): Integer;
begin
Result := Value.Value + Round(Value.Key * 1E6);
end;
var
Events : IMultiMap<TMyKey, Integer>;
KeyComparer : IEqualityComparer<TMyKey>;
begin
KeyComparer := TMyKeyEqualityComparer.Create;
// Next line triggers error: "E2250 There is no overloaded version of 'Create' that can be called with these arguments"
Events := TMultiMap<TMyKey, Integer>.Create(KeyComparer);
end.
In Spring4D source code, I find the following declaration:
TMultiMap<TKey, TValue> = class(TMultiMapBase<TKey, TValue>)
and also the TMultiMap acestor is declared:
TMultiMapBase<TKey, TValue> = class abstract(TMapBase<TKey, TValue>,
IMultiMap<TKey, TValue>, IReadOnlyMultiMap<TKey, TValue>)
and has this constructor:
constructor Create(const keyComparer: IEqualityComparer<TKey>); overload;
This is the constructor I want to call. As far as I understand, my argument KeyComparer has the correct type. But obviously the compiler doesn't agree :-(
How to fix this code?
You use different IEqualityComparer<T> types.
The IEqualityComparer<T> in your example comes from Spring.Tests.Interception.Types.pas. The one used in TMultiMap<TKey, TValue>.Create constructor is defined in System.Generics.Defaults.pas.
If you change your uses part to
uses
System.SysUtils,
Generics.Collections,
// Spring.Tests.Interception.Types,
Generics.Defaults,
Spring.Collections.MultiMaps,
Spring.Collections;
your example will compile.
Tested with Delphi 10.3 U3 and Spring4D 1.2.2
Consider this class:
unit u_myclass;
interface
type
TMyClass = class
public
class function Foo : Integer;
function Foo : Integer;
end;
implementation
{ TMyClass }
class function TMyClass.Foo: Integer;
begin
Result := 10;
end;
function TMyClass.Foo: Integer;
begin
Result := 1;
end;
end.
I want to use a class function and an instance function with the same name.
Sadly Delphi doesn't like this and the compiler barfs these errors:
[DCC Error] u_myclass.pas(9): E2252 Method 'Foo' with identical parameters already exists
[DCC Error] u_myclass.pas(20): E2037 Declaration of 'Foo' differs from previous declaration
[DCC Error] u_myclass.pas(9): E2065 Unsatisfied forward or external declaration: 'TMyClass.Foo'
My Question: is this possible or is this simply a language limitation (And I need to rename one of the 2 methods)?
It's not possible to give use the same name for an instance method and a class method. That this is not allowed is that the compiler cannot distinguish between them in some scenarios.
For instance, if you write:
procedure TMyClass.Bar;
begin
Foo;
end;
then the compiler cannot determine whether or not you wish to call the class method or the instance method.
The only solution I found is to use overload and different parameters:
unit u_myclass;
interface
type
TMyClass = class
public
class function Foo(A : Integer) : Integer; overload;
function Foo : Integer; overload;
end;
implementation
{ TMyClass }
class function TMyClass.Foo(A: Integer): Integer;
begin
Result := A;
end;
function TMyClass.Foo: Integer;
begin
Result := 1;
end;
end.
I'm getting a IEnumVariant from a .NET class library and I am trying to use a generic class to convert this to a IEnumerator
There is a compiler error, "Operator not applicable to this operand type" when attempting to cast an IInterface to the generic type T
I've seen workarounds when attempting to type cast to a class, but these don't work for an interface.
Using Supports as suggested by Rob seems to have problems as well as TypeInfo returns nil for the parameterized type.
uses WinApi.ActiveX, Generics.Collections;
type
TDotNetEnum<T: IInterface> = class(TInterfacedObject, IEnumerator<T>)
strict private
FDotNetEnum: IEnumVariant;
FCurrent: T;
function MoveNext: Boolean;
procedure Reset;
function GetCurrent: TObject;
function IEnumerator<T>.GetCurrent = GenericGetCurrent;
function GenericGetCurrent: T;
public
constructor Create(const ADotNetObject: OleVariant);
//// I can get it to work using this constructor
// constructor Create(const ADotNetObject: OleVariant; const AGUID: TGUID);
end;
implementation
uses System.Rtti, SysUtils, mscorlib_TLB, ComObj;
constructor TDotNetEnum<T>.Create(const ADotNetObject: OleVariant);
var
netEnum: IEnumerable;
begin
netEnum := IUnknown(ADotNetObject) as mscorlib_TLB.IEnumerable;
FDotNetEnum := netEnum.GetEnumerator();
end;
function TDotNetEnum<T>.GenericGetCurrent: T;
begin
result := FCurrent;
end;
function TDotNetEnum<T>.GetCurrent: TObject;
begin
result := nil;
end;
function TDotNetEnum<T>.MoveNext: Boolean;
var
rgvar: OleVariant;
fetched: Cardinal;
ti: TypeInfo;
guid: TGUID;
begin
OleCheck(FDotNetEnum.Next(1, rgvar, fetched));
result := fetched = 1;
if not result then
FCurrent := nil
else
begin
FCurrent := IUnknown(rgvar) as T; // <-- Compiler error here
//// Doesn't work using Supports either
// ti := TypeInfo(T); // <-- returns nil
// guid := GetTypeData(#ti)^.Guid;
// Supports(IUnknown(rgvar), guid, FCurrent);
end;
end;
procedure TDotNetEnum<T>.Reset;
begin
OleCheck(FDotNetEnum.Reset);
end;
Am I missing something in order to get that case to the generic interface type to work ?
I do have the alternative constructor which I CAN get the guid from so that
TDotNetEnum<IContact>.Create(vContactList, IContact);
works but the ideal
TDotNetEnum<IContact>.Create(vContactList);
doesn't
Using as to cast interfaces is only valid for interfaces that have GUIDs. The compiler cannot assume that T has a GUID when it's compiling your generic class, so it cannot accept an expression of the form val as T.
This has been covered before, but in reference to the Supports function, which has the same limitation as the as operator.
The solution is to use RTTI to fetch the interface's GUID, and then use that to type-cast the interface value. You could use Supports:
guid := GetTypeData(TypeInfo(T))^.Guid;
success := Supports(IUnknown(rgvar), guid, FCurrent);
Assert(success);
You could also call QueryInterface directly:
guid := GetTypeData(TypeInfo(T))^.Guid;
OleCheck(IUnknown(rgvar).QueryInterface(guid, FCurrent));
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.