2016-10-26 2 views
17

J'essaye de faire la liste des gestionnaires d'événement où le gestionnaire est la référence de méthode. Pour supprimer un gestionnaire spécifique, je dois le trouver dans la liste. Mais comment puis-je comparer l'adresse de code de deux références de méthode?Comment vérifier si deux références de méthode référencent la même méthode?

type 
    TEventHandler = reference to procedure; 

procedure TestProc; 
begin 
end; 

procedure TForm26.FormCreate(Sender: TObject); 
var 
    Handlers: TList<TEventHandler>; 
begin 
    Handlers := TList<TEventHandler>.create; 
    try 
    Handlers.Add(TestProc); 
    Handlers.Remove(TestProc); { doesn't work } 
    Assert(Handlers.Count=0); { fails } 
    Assert(Handlers.IndexOf(TestProc)>=0); { fails } 
    finally 
    FreeAndNil(Handlers); 
    end; 
end; 

Fascicule de TList <> ne compare pas les références de méthode correctement. Comment puis-je les comparer? Existe-t-il une structure similaire à TMethod mais pour les références de méthode?

+0

TEqualityComparer .Default.Equals (A, B) –

+0

Et vous pouvez utiliser le TProc à la place de votre propre déclaration ... il suffit d'ajouter System.SysUtils. –

+0

@ZENsas Je connais TProc, j'ai juste essayé de rendre l'exemple aussi clair que possible. TEqualityComparer .Default.Equals (A, B) ne fonctionne pas, je viens de tester cela (sinon TList <>. Le retrait fonctionnera aussi, il est basé sur le comparateur par défaut). –

Répondre

15

Ce n'est pas aussi simple que cela puisse paraître. Pour comprendre pourquoi cela se produit, vous devez comprendre comment l'affectation à une référence de méthode est effectuée par le compilateur.

Le code que vous avez écrit est essentiellement traduit en cela par le compilateur:

Handlers.Add(procedure begin TestProc; end); 
Handlers.Remove(procedure begin TestProc; end); 

Maintenant, nous devons savoir que si vous avez plusieurs méthodes anonymes au sein de la même routine, ils sont en fait différentes méthodes anonymes même si leur le code est identique. (Voir How are anonymous methods implemented under the hood?)

Cela signifie que les valeurs transmises à Add et Remove sont différentes, même si le code dans leur corps est le même - même avec le piratage autour d'elle nécessiterait une analyse de code binaire pour déterminer si le code à l'intérieur du corps est le même.

si vous modifiez le code comme suit cela fonctionnerait parce que vous avez une seule méthode anonyme - pour cette ciselée cela fonctionne, mais en général vous n'ajouter et supprimer dans le exactement la même routine:

var 
    Handlers: TList<TEventHandler>; 
    Handler: TEventHandler; 
begin 
    Handlers := TList<TEventHandler>.create; 
    try 
    Handler := TestProc; 
    Handlers.Add(Handler); 
    Handlers.Remove(Handler); 
    Assert(Handlers.Count=0); 
    finally 
    FreeAndNil(Handlers); 
    end; 
end; 

Si vous voulez une liste dans laquelle vous ajoutez et supprimez des gestionnaires d'événements ma recommandation personnelle est d'éviter un type de méthode anonyme et la procédure d'utilisation ou méthodes:

type 
    TEventHandlerA = procedure; 
    TEventHandlerB = procedure of object; 

La décision que l'on est mieux est à vous parce que vous connaissez votre code meilleur.

+0

Je ne m'attendais pas à ce que la méthode anonyme soit générée quand j'appelle quelque chose avec la référence de la méthode comme paramètre. Je suppose que je peux utiliser des références de méthodes sans méthodes anonymes en réalité. Je vous remercie! –

+1

@AndreiGalatyn: vous pouvez utiliser les références 'procedure of object' sans méthodes anonymes, mais vous ne pouvez pas utiliser les références' reference to procedure' sans anonmeths, puisque ces dernières ** sont des références ** anonmeth. –