2016-06-04 4 views
2

У меня возникли проблемы с использованием методов, как Contains, Remove или IndexOf из TObjectList<T> класса, , когда T является пользовательский тип, как TSocket в следующем примере кода.Как использовать такие методы, как Contains, Удалить или IndexOf из TObjectList <T>

Я начал реализацию пользовательского типа TSocket и пытался использовать его в списке типа TObjectList<TSocket>, как это:

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(); 

я ожидал, что Contains, IndexOf и Remove делают использование Equals процедуры TMyObject и перезаписать выполнение этой процедуры. Поэтому я добавил следующее Equals реализации внешних к моему TSocket класса:

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; 

Используя этот код Contains возвращает ложное, но должно быть правдой, IndexOf возвращается -1, но должно быть 0 и Remove не удаляет объект, но ее следует удалить , Я ожидал, что эти методы будут использовать метод EqualsTSocket, которого они не сделали. Прочитав документацию, я узнал, что конструктор TObjectList можно вызвать с использованием IComparer. Поэтому я использовал TEqualityComparer<TSocket>, чтобы использовать мой метод Equals. К сожалению, конструктор TObjectList не поддерживает интерфейс IEqualityComparer, но вместо этого использует интерфейс IComparer.

Вопрос: Как использовать такие методы, как Contains, Remove или IndexOf из TObjectList<T> при использовании пользовательских типов в Delphi? В других языках программирования (например, Java или C#) Equals используется для сравнения объектов в типах списков. Какой механизм использует Delphi для сравнения объектов?

Обновление Благодарим вас за всестороннюю обратную связь. Я обновил свой вопрос и код соответствующим образом. Я подробно остановился на своих ожиданиях при запуске кода и добавлении дополнительного кода, чтобы сделать мои намерения более ясными. @DavidHeffernan: Реализация действительно была неправильной. Я добавил наследование в TInterfacedObject, чтобы узнать больше о подсчете ссылок. Я удалил TInterfacedObject из кода.

+0

Большая проблема заключается в том, что вы смешиваете эталонные подсчитывали интерфейсы и объекты. Вы должны это прекратить. Всякая часть кода здесь плохо ошибочна. 'Contains',' Remove', 'IndexOf' отлично работают с вашим типом, но вы используете неправильный тип, поэтому вам нужно будет начать заново. –

+0

Извините, если мое качество кода не хорошо, что я все еще пытаюсь выучить язык – MUG4N

+0

Дело в том, что мы можем ответить на этот вопрос, но это не поможет. Проблема глубже. Вы решили использовать ссылки с подсчитанными интерфейсами и должны придерживаться этого. –

ответ

7

В Вашей ошибке было сделано предположение, что TObjectList<T> использует функцию Equals для проверки равенства.

По умолчанию TObjectList<T>, или, точнее, TList<T>, используется компаратор, возвращаемый TComparer<T>.Default. В случае TObjectList<TSocket> сопоставитель по умолчанию сравнивает сам указатель. Поскольку вы создали 2 разных объекта, указатели отличаются. Результат, который вы получите, - ожидаемый результат.

Если вы хотите переопределить это поведение по умолчанию, вам необходимо предоставить свой собственный компаратор.Способ сделать так, чтобы передать его через конструктор, как это:

TObjectList<TSocket>.Create(TComparer<TSocket>.Construct(
    function (const L, R : TSocket) : Integer 
    begin 
     //Compare here. 
    end) 
    ); 

Ваша функция должна:

  • Вернуть значение ниже 0, если L меньше, чем R. (обычно -1)
  • Возвращает значение больше 0, если L больше R. (Обычно 1)
  • Возврат 0 равен равным.

Если по каким-либо причинам вы хотите только проверить на равенство, вы можете технически сделать это и вернуть -1 или 1, если он не равен без дальнейшего сравнения. То есть, пока вы не планируете сортировать список или BinarySearch через него.

+0

Спасибо за ваш приветственный ответ! Ваш последний абзац звучит для меня так, как если бы Equals не был правильным способом сравнить объекты внутри списков, иначе TEqualityComparer будет разрешен как параметр конструктора. Неправильно ли использовать метод Equals? Что бы вы проверили в реализации сравнения? – MUG4N

+1

Задача компаратора, как правило, сортировать список или находить в нем элемент. Вы не можете сортировать только с помощью TEqualityComparer. Поскольку вам, похоже, не нужны функции «сортировки», было бы «ОК», чтобы ваш результат сравнения возвращался равным (0)/не равно (ничего, кроме 0). Они, вероятно, не поставили параметр TEqualityComparer, потому что TComparer может это сделать, а затем и некоторые. –

+1

fwiw imho реализации 'Contains',' Remove' и 'IndexOf' нарушены дизайном. Они используют 'IComparer ', а не 'IEqualityComparer '. Если они будут использовать '' IEqualityComparer они на самом деле использовать виртуальный метод '' Equals' из TObject' для типов T, которые являются классами (см 'System.Generics.Defaults.Equals_Class'). –