Why is TList.Remove() producing an EAccessViolatio

2019-04-06 19:33发布

Why EAccessViolation is raised when executing the code below?

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条回答
放我归山
2楼-- · 2019-04-06 19:40

the above code is used in 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;
查看更多
ら.Afraid
3楼-- · 2019-04-06 19:42

It looks like a bug.

If you compile with debug dcu's (normally don't do that unless you want to loose your sanity!) you see that a call to the comparer went wrong. A (possibly optional) third value of a compare function is not set and causes the access violation.

So possibly you can't put method pointers in a generic list.

Ok the following works:

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;

You have to define your own comparer, with possiby some more intelligence ;-).

查看更多
Bombasti
4楼-- · 2019-04-06 19:46

Access Violation is caused by missing comparer. I suspect this was fixed in a patch but the problem still persists (at least in Delphi 2009) if you use a TObjectList so I'm just updating with the simplest solution:

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

or in my case

TObjectList<TNotifyEvent>.Create(TComparer<TNotifyEvent>.Default);
查看更多
疯言疯语
5楼-- · 2019-04-06 20:02

Is it possible to pass a custom comparer to TList<T>? I don't have D2009 in front of me, so can't try it.

查看更多
登录 后发表回答