为什么TList.Remove()会产生EAccessViolation错误?

9
当执行以下代码时,为什么会引发EAccessViolation错误?
uses
  Generics.Collections;
  ...

var
  list: TList<TNotifyEvent>;
  ...

begin
  list := TList<TNotifyEvent>.Create();
  try
    list.Add(myNotifyEvent);
    list.Remove(myNotifyEvent);  // EAccessViolation at address...
  finally
    FreeAndNil(list);
  end;
end;

procedure myNotifyEvent(Sender: TObject);
begin
  OutputDebugString('event');  // nebo cokoliv jineho
end;
4个回答

5

看起来像是一个bug。

如果你使用调试dcu编译(通常不要这样做,除非你想失去理智!),你会发现比较器的调用出了问题。比较函数的第三个值(可能是可选的)未设置,导致访问冲突。

所以可能不能在通用列表中放置方法指针。

好的,以下操作有效:

uses
  Generics.Defaults;

type
  TForm4 = class(TForm)
    ...
  private
    procedure myNotifyEvent(Sender: TObject);
  end;

TComparer<T> = class (TInterfacedObject, IComparer<T>)
public
  function Compare(const Left, Right: T): Integer;
end;

implementation

uses
  Generics.Collections;

var
  list: TList<TNotifyEvent>;
begin
  list := TList<TNotifyEvent>.Create(TComparer<TNotifyEvent>.Create);
  try
    list.Add(myNotifyEvent);
    list.Remove(myNotifyEvent);
  finally
    FreeAndNil(list);
  end;
end;

procedure TForm4.myNotifyEvent(Sender: TObject);
begin
  ShowMessage('event');
end;

{ TComparer<T> }

function TComparer<T>.Compare(const Left, Right: T): Integer;
begin
  Result := 0;
end;

您需要定义自己的比较器,并可能增加更多智能 ;-).


4

访问冲突是由于缺少比较器引起的。我怀疑这个问题在一个补丁中得到了修复,但如果您使用TObjectList,则该问题仍然存在(至少在Delphi 2009中)。因此,我将提供最简单的解决方案:

TList<TNotifyEvent>.Create(TComparer<TNotifyEvent>.Default);

或者在我的情况下
TObjectList<TNotifyEvent>.Create(TComparer<TNotifyEvent>.Default);

1
这个 bug 在 xe4 中仍然存在:/ - Mehmet Fide

1

是否可以将自定义比较器传递给 TList<T>?我手头没有 D2009,所以无法尝试。


是的,你可以在重载构造函数中传递一个。 - Toon Krijthe

0

上述代码用于TForm1...

uses 
  Generics.Collections;

procedure TForm1.Button1Click(Sender: TObject);
var
  list: TList<TNotifyEvent>;
begin
  list := TList<TNotifyEvent>.Create();
  try
    list.Add(myNotifyEvent);
    list.Remove(myNotifyEvent);  // EAccessViolation at address...
  finally
    FreeAndNil(list);
  end;
end;
procedure TForm1.myNotifyEvent(Sender: TObject);
begin
  OutputDebugString('event');  // nebo cokoliv jineho
end;

嗯,这并不是你问题的答案。我认为你应该将其与你问题中的代码合并(因为现在它无效,因为问题中显示的“myNotifyEvent”与“TNotifyEvent”签名不匹配(它不是一个方法)。 - Oliver Giesen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接