2016-10-26 26 views
17

Tôi đang cố gắng tạo danh sách các trình xử lý sự kiện trong đó trình xử lý là tham chiếu phương thức. Để xóa trình xử lý cụ thể, tôi cần tìm nó trong danh sách. Nhưng làm cách nào tôi có thể so sánh địa chỉ mã của hai tham chiếu phương thức?Làm cách nào để kiểm tra xem hai tham chiếu phương thức có đang tham chiếu cùng một phương thức không?

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; 

So sánh mặc định của TList <> không so sánh tham chiếu phương pháp đúng cách. Tôi có thể so sánh chúng bằng cách nào? Có cấu trúc tương tự như TMethod nhưng để tham khảo phương pháp?

+0

TEqualityComparer .Default.Equals (A, B) –

+0

Và bạn có thể sử dụng TProc thay vì khai báo của riêng bạn ... chỉ cần thêm System.SysUtils. –

+0

@ZENsas Tôi biết về TProc, tôi chỉ cố gắng làm ví dụ rõ ràng nhất có thể. TEqualityComparer .Default.Equals (A, B) không hoạt động, tôi chỉ kiểm tra (nếu không TList <>. Remove cũng sẽ hoạt động, nó dựa trên so sánh mặc định). –

Trả lời

15

Đây không phải là dễ dàng như nó có vẻ.

Để hiểu tại sao điều này xảy ra, bạn cần hiểu cách gán cho một tham chiếu phương thức được trình biên dịch thực hiện.

Mã bạn đã viết được về cơ bản dịch ra tiếng này bởi trình biên dịch:

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

Bây giờ chúng ta phải biết rằng nếu bạn có nhiều phương pháp vô danh trong thói quen cùng họ đang có trong thực tế phương pháp vô danh khác nhau ngay cả khi họ mã giống hệt nhau. (Xem How are anonymous methods implemented under the hood?)

Điều này có nghĩa rằng các giá trị truyền cho AddRemove là khác nhau ngay cả khi các mã trong cơ thể của họ là như nhau - thậm chí với hacking xung quanh nó sẽ đòi hỏi một phân tích mã nhị phân để xác định xem mã bên trong cơ thể là giống nhau.

Nếu bạn muốn thay đổi mã như sau nó sẽ làm việc bởi vì khi đó bạn chỉ có một phương pháp vô danh - cho điều này snipped nó hoạt động nhưng thường bạn sẽ không thêm và loại bỏ trong chính xác cùng một thói quen:

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; 

Nếu bạn muốn có một danh sách mà bạn thêm và loại bỏ các event handler khuyên của chúng tôi là để tránh một loại vô danh phương pháp và thủ tục sử dụng hoặc các phương pháp:

type 
    TEventHandlerA = procedure; 
    TEventHandlerB = procedure of object; 

quyết định cái nào là tốt hơn là tùy thuộc vào bạn vì bạn biết mã của bạn tốt hơn.

+0

Tôi đã không mong đợi rằng phương pháp nặc danh được tạo ra khi tôi gọi một cái gì đó với tham chiếu phương thức như tham số. Tôi cho rằng tôi có thể sử dụng các tham chiếu phương thức mà không có các phương thức ẩn danh thực sự. Cảm ơn bạn! –

+1

@AndreiGalatyn: bạn có thể sử dụng tham chiếu 'thủ tục đối tượng' mà không có các phương thức ẩn danh, nhưng bạn không thể sử dụng tham chiếu' tham chiếu đến thủ tục' mà không có anonmeth, vì sau ** là ** tham chiếu anonmeth. –

Các vấn đề liên quan