views:

456

answers:

4
+5  Q: 

Generics problem

Why EAccessViolation is raised?

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;
A: 

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;
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).
Oliver Giesen
+3  A: 

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

Gamecat
Looks like a bug to me too.
Fabio Gomes
+1  A: 

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.

Steve
Yes you can pass one at the overloaded constructor.
Gamecat
+1  A: 

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