2009-07-13 15 views
20

Có một mẫu chuẩn cho các sự kiện trong .NET - chúng sử dụng loại delegate lấy một đối tượng đơn giản gọi là người gửi và sau đó là "trọng tải" thực tế trong tham số thứ hai, bắt nguồn từ EventArgs.Tôi sẽ mất gì bằng cách bỏ mẫu EventHandler chuẩn trong .NET?

Lý do cho thông số thứ hai có nguồn gốc từ EventArgs có vẻ khá rõ ràng (xem .NET Framework Standard Library Annotated Reference). Nó được thiết kế để đảm bảo khả năng tương thích nhị phân giữa các sự kiện chìm và các nguồn khi phần mềm phát triển. Đối với mọi sự kiện, ngay cả khi nó chỉ có một đối số, chúng tôi lấy được một lớp đối số sự kiện tùy chỉnh có một thuộc tính duy nhất chứa đối số đó, để chúng tôi giữ khả năng thêm nhiều thuộc tính hơn vào tải trọng trong các phiên bản sau mà không vi phạm mã máy khách hiện tại . Rất quan trọng trong một hệ sinh thái của các thành phần phát triển độc lập.

Nhưng tôi thấy rằng điều này cũng tương tự với 0 đối số. Điều này có nghĩa là nếu tôi có một sự kiện không có đối số trong phiên bản đầu tiên của tôi và tôi viết:

public event EventHandler Click; 

... thì tôi đang làm sai. Nếu tôi thay đổi loại đại biểu trong tương lai thành một lớp mới làm trọng tải của nó:

public class ClickEventArgs : EventArgs { ... 

... Tôi sẽ phá vỡ tính tương thích nhị phân với khách hàng của tôi. Các khách hàng kết thúc lên đến một quá tải cụ thể của một phương pháp nội bộ add_Click mà mất EventHandler, và nếu tôi thay đổi loại đại biểu sau đó họ không thể tìm thấy tình trạng quá tải, do đó, có một MissingMethodException.

Được rồi, vậy nếu tôi sử dụng phiên bản chung tiện dụng thì sao?

public EventHandler<EventArgs> Click; 

Không, vẫn sai, vì EventHandler<ClickEventArgs> không phải là EventHandler<EventArgs>.

Vì vậy, để có được lợi ích của EventArgs, bạn để lấy được từ nó, thay vì sử dụng trực tiếp như vậy. Nếu bạn không, bạn có thể cũng không được sử dụng nó (có vẻ như với tôi).

Sau đó, có đối số đầu tiên, sender. Dường như với tôi giống như một công thức cho khớp nối không độc. Một sự kiện bắn về cơ bản là một cuộc gọi chức năng. Nếu chức năng, nói chung, có khả năng đào trở lại thông qua ngăn xếp và tìm ra ai là người gọi, và điều chỉnh hành vi của nó cho phù hợp? Chúng ta có nên ủy thác giao diện đó trông như thế này không?

public interface IFoo 
{ 
    void Bar(object caller, int actualArg1, ...); 
} 

Sau khi tất cả, implementor của Bar có thể muốn biết caller là ai, vì vậy họ có thể truy vấn để biết thêm thông tin! Tôi hy vọng bạn đang puking bây giờ. Tại sao nó lại khác biệt cho các sự kiện?

Vì vậy, ngay cả khi tôi sẵn sàng chịu nỗi đau khi tạo một lớp độc lập cho mỗi sự kiện tôi tuyên bố, chỉ để làm cho nó đáng giá trong khi sử dụng EventArgs, tôi chắc chắn muốn bỏ đối số người gửi đối tượng .

trực quan tính năng tự động hoàn Studio dường như không quan tâm những gì đại biểu bạn sử dụng cho một sự kiện - bạn có thể gõ +=[nhấn Space, Return] và nó viết một phương pháp xử lý đối với các bạn rằng các trận đấu bất cứ đại biểu nó sẽ xảy ra là .

Vì vậy, tôi sẽ mất giá trị nào bằng cách lệch khỏi mẫu chuẩn?

Như một câu hỏi tiền thưởng, C#/CLR 4.0 có làm bất cứ điều gì để thay đổi điều này, có lẽ thông qua contravariance trong các đại biểu? Tôi đã cố gắng điều tra điều này nhưng nhấn another problem. Ban đầu tôi bao gồm khía cạnh này của câu hỏi trong câu hỏi khác, nhưng nó gây ra sự nhầm lẫn ở đó. Và nó có vẻ hơi nhiều để chia này thành tổng cộng ba câu hỏi ...

Cập nhật:

Hóa ra tôi đã đúng khi tự hỏi về những tác động của contravariance trên toàn bộ vấn đề này!

noted elsewhere, quy tắc trình biên dịch mới để lại một lỗ hổng trong hệ thống kiểu thổi lên khi chạy. Lỗ đã được cắm một cách hiệu quả bằng cách xác định EventHandler<T> khác với Action<T>.

Vì vậy, đối với các sự kiện, để tránh lỗ loại đó, bạn không nên sử dụng Action<T>. Điều đó không có nghĩa là bạn phải sử dụng EventHandler<TEventArgs>; nó chỉ có nghĩa rằng nếu bạn sử dụng một loại đại biểu chung, không chọn một trong đó được kích hoạt cho contravariance.

+0

Hãy quan tâm để nghe lý do từ người bỏ phiếu. –

+0

Tôi đã không bỏ phiếu theo một trong hai cách, nhưng tôi nghi ngờ downvote là bởi vì bạn đã có ít nhất hai câu hỏi trong một ở đây. Tôi không muốn thực sự cố gắng giải quyết toàn bộ bản thân mình, nhưng tôi rất vui khi nhìn vào khía cạnh khác biệt (như tôi đang viết về nó ngay bây giờ). Nếu bạn có thể tách các bit dẫn đến một lỗi thời gian thực hiện vào một câu hỏi riêng biệt với một trường hợp ngắn nhưng đầy đủ để tái tạo nó, tôi sẽ được quan tâm ... –

+0

Sẽ làm [PADDING] –

Trả lời

7

Không có gì, bạn sẽ không mất gì. Tôi đã sử dụng Action<> kể từ khi .NET 3.5 xuất hiện và nó tự nhiên hơn và dễ dàng hơn để lập trình chống lại.

Tôi thậm chí không đối phó với các loại EventHandler cho xử lý sự kiện được tạo ra nữa, chỉ cần viết chữ ký phương pháp mà bạn muốn và dây nó lên với một lambda:

btnCompleteOrder.OnClick += (o,e) => _presenter.CompleteOrder(); 
+2

Bạn có gặp sự cố khi hủy đăng ký sự kiện với chức năng ẩn danh này không? – Matt

+2

Tôi cho rằng bạn sẽ làm, nhưng bạn không nên hủy đăng ký sự kiện OnClick vì điều đó chỉ có nghĩa là thông báo cho người trình bày của bạn rằng có điều gì đó đã xảy ra.Nếu bạn đăng ký/hủy đăng ký, bạn có thể quan tâm nhiều hơn đến các sự kiện do các lớp không phải là lớp học tạo ra, trong trường hợp đó bạn có toàn quyền kiểm soát loại sự kiện và có thể biến nó thành Hành động <>. –

+0

@Matt - Tôi viết phần lớn sự kiện của tôi tranh thủ theo cách đó. Sự cần thiết phải bỏ đăng ký một cách rõ ràng là khá bất thường. Về cơ bản, nếu nguồn sự kiện là một đối tượng tồn tại lâu hơn (ví dụ: được lưu trữ trong một cửa sổ tĩnh hoặc cửa sổ chính của ứng dụng) nhưng nếu nguồn và sink có cùng tuổi thọ thì tại sao lại bận tâm? Đây là những gì GC cho. –

1

Tôi không thích những event- mô hình xử lý. Theo tôi, đối tượng Người gửi không thực sự hữu ích. Trong trường hợp một sự kiện đang nói điều gì đó đã xảy ra với một số đối tượng (ví dụ: thông báo thay đổi) sẽ hữu ích hơn khi có thông tin trong EventArgs. Việc sử dụng duy nhất tôi có thể kinda-sorta xem cho người gửi sẽ được để bỏ đăng ký từ một sự kiện, nhưng nó không phải luôn luôn rõ ràng những gì một trong những sự kiện nên bỏ đăng ký.

Ngẫu nhiên, nếu tôi có druthers của tôi, một sự kiện sẽ không phải là một phương pháp AddHandler và phương pháp RemoveHandler; nó sẽ chỉ là một phương pháp AddHandler mà sẽ trả về một MethodInvoker có thể được sử dụng để hủy đăng ký. Thay vì một đối số Sender, tôi sẽ có đối số đầu tiên là một bản sao của MethodInvoker được yêu cầu để hủy đăng ký (trong trường hợp một đối tượng tự nhận các sự kiện mà người hủy bỏ đăng ký đã bị mất). MulticastDelegate chuẩn sẽ không thích hợp cho việc gửi đi các sự kiện (vì mỗi người đăng ký sẽ nhận được một ủy nhiệm unsubscription khác) nhưng các sự kiện hủy đăng ký sẽ không yêu cầu tìm kiếm tuyến tính thông qua một danh sách yêu cầu.

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