2

T が次のコード例のようにカスタム型の場合ContainsRemoveやクラスIndexOfのようなメソッドを使用すると問題が発生します。TObjectList<T>TSocket

カスタム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();

私はそれを期待しContainsIndexOfの手順をRemove利用し、この手順の実装を上書きしました。したがって、次の実装をTSocket クラスに追加しました。EqualsTMyObjectEquals

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と、false が返されますが true である必要があり、IndexOf-1 が返されますが 0 である必要がありRemove、オブジェクトは削除されませんが、削除する必要があります。Equalsこれらのメソッドは、使用していないメソッドを使用すると予想TSocketしていました。TObjectListドキュメントを読んだ後、のコンストラクターを IComparer の実装で呼び出すことができることがわかりました。したがって、メソッドTEqualityComparer<TSocket>を使用するために を実装しました。Equals残念ながら、 のコンストラクターはインターフェイスTObjectListをサポートしていませんIEqualityComparerが、代わりにインターフェイスを使用していIComparerます。

質問: Delphi でカスタム型を使用する場合ContainsRemoveIndexOfof などのメソッドはどのように使用すればよいですか? TObjectList<T>他のプログラミング言語 (Java や C# など) ではEquals、リスト型のオブジェクトを比較するために使用されます。オブジェクトを比較するために Delphi が使用するメカニズムは何ですか?

更新 包括的なフィードバックをお寄せいただきありがとうございます。質問とコードを適切に更新しました。コードを実行するときの私の期待を詳しく説明し、意図をより明確にするためにさらにコードを追加しました。@DavidHeffernan:実装は確かに間違っていました。参照カウントについて詳しく知るために、TInterfacedObject に継承を追加しました。コードから TInterfacedObject を削除しました。

4

1 に答える 1

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

関数は次のようにする必要があります。

  • L が R より小さい場合、0 より小さい値を返します (通常は -1)。
  • L が R より大きい場合は、0 より大きい値を返します (通常は 1)。
  • どちらも 0 を返します。

何らかの理由で、等しいかどうかのみをチェックしたい場合は、技術的にそうすることができ、それ以上の比較なしで等しくない場合は -1 または 1 を返すことができます。つまり、リストまたは BinarySearch を並べ替える予定がない限り。

于 2016-06-05T16:39:42.277 に答える