I am having problems using methods like Contains
, Remove
or IndexOf
of TObjectList<T>
class,
when T is a custom type like TSocket
in the following example code.
I started by implementing a custom TSocket
type and tried to use it in a list of type TObjectList<TSocket>
like this:
list := nil;
socket := nil;
try
list := TObjectList<TSocket>.Create();
socket := TSocket.Create(TIpAddress.Parse('127.0.0.1'),6857);
// add new socket object with equal values to list
list.Add(TSocket.Create(TIpAddress.Parse('127.0.0.1'),6857));
// should return true but returns false
if list.Contains(socket) then
WriteLn('socket contained in list')
else
WriteLn('socket not contained in list');
// should return number 0 but returns -1
if list.IndexOf(socket) = 0 then
WriteLn('socket contained in list')
else
WriteLn('socket not contained in list');
// should remove item from list but items doesn't get removed
list.Remove(socket);
finally
list.Free();
socket.Free();
I expected that Contains
, IndexOf
and Remove
make use of the Equals
procedure of TMyObject
and overwriten implementation of this procedure. Therefore I added the following implemenation of Equals
to my TSocket class:
type
TSocket = class
strict private
_ipAddress: TIpAddress;
_port: integer;
public
constructor Create(ipAddress: TIpAddress; port: integer);
function GetIpAddress: TIpAddress;
function GetPort: integer;
property IpAddress: TIpAddress read GetIpAddress;
property Port: integer read GetPort;
function Equals(other: TObject): boolean; overload; override;
destructor Destroy; override;
end;
implementation
constructor TSocket.Create(ipAddress: TIpAddress; port: integer);
begin
inherited Create();
_ipAddress := ipAddress;
_port := port;
end;
function TSocket.Equals(other: TObject): boolean;
var
otherSocket: TSocket;
begin
if not (other is TSocket) then exit(false);
otherSocket := other as TSocket;
result:= (_ipAddress.Equals(otherSocket.IpAddress)) and (_port = otherSocket.Port)
end;
function TSocket.GetIpAddress: TIpAddress;
begin
result := _ipAddress;
end;
function TSocket.GetPort: integer;
begin
result := _port;
end;
destructor TSocket.Destroy;
begin
_ipAddress.Free();
inherited Destroy();
end;
Using this code Contains
returns false but should be true, IndexOf
returns -1 but should be 0 and Remove
does not remove the object but should remove it. I expected these methods would use the Equals
method of TSocket
which they didn't. After reading the documentation I found out that the constructor of TObjectList
can be called with an implemenantion of IComparer. Therefore I implementated an TEqualityComparer<TSocket>
in order to use my Equals
method.
Unfortunately the constructor of TObjectList
does not support the IEqualityComparer
interface but instead use the IComparer
interface.
Question:
How do I use methods like Contains
, Remove
or IndexOf
of TObjectList<T>
when using custom types in Delphi?
In other programming languages (like Java or C#) Equals
is used to compare objects in list types. What mechanism does Delphi use to compare objects?
Update Thank you for the comprehensive feedback. I have updated my question and the code appropriately. I elaborated on what my expectations were when running the code and added further code to make my intentions clearer. @DavidHeffernan: The implementation was indeed wrong. I added inheritance to TInterfacedObject in order to learn more about reference counting. I removed TInterfacedObject from the code.
Your error was your assumption that TObjectList<T>
uses the Equals
function to test for equality.
By default, TObjectList<T>
, or more accurately TList<T>
, uses the comparer returned by TComparer<T>.Default
. In the case of TObjectList<TSocket>
, the default comparer compare the pointer itself. Since you created 2 differents objects, the pointers differ. The result you get is the expected result.
If you want to override that default behavior, you need to supply your own comparer. The way to do so is to pass it through the constructor like this :
TObjectList<TSocket>.Create(TComparer<TSocket>.Construct(
function (const L, R : TSocket) : Integer
begin
//Compare here.
end)
);
Your function should:
If, for whatever reasons, you want to only check for equality, you can technically do so and return either -1 or 1 when it's not equal without further comparison. That is, as long as you don't plan to sort the list or BinarySearch through it.