Why is TList.Remove() producing an EAccessViolation error?
Asked Answered
E

4

9

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;
Eatton answered 14/11, 2008 at 11:22 Comment(0)
B
5

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

Bagnio answered 14/11, 2008 at 11:59 Comment(0)
C
4

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);
Cristobalcristobalite answered 14/1, 2010 at 19:13 Comment(1)
this bug still exists with xe4 :/Initial
O
1

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.

Outdoors answered 14/11, 2008 at 12:19 Comment(1)
Yes you can pass one at the overloaded constructor.Bagnio
E
0

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;
Eatton answered 14/11, 2008 at 11:50 Comment(1)
Hmm, this isn't really an answer to your question. I think you should merge this with the code in your question (which, as it is now, is invalid because the myNotifyEvent as shown in the question does not match the TNotifyEvent signature (it's not a method there).Hypostatize

© 2022 - 2024 — McMap. All rights reserved.