Tôi hoàn toàn nhận ra rằng những gì tôi đang đề xuất không tuân theo các nguyên tắc .NET, và do đó, có lẽ là một ý tưởng tồi chỉ vì lý do này. Tuy nhiên, tôi muốn xem xét điều này từ hai khía cạnh có thể có:
(1) Tôi có nên cân nhắc sử dụng cái này cho công việc phát triển của bản thân không, 100% cho mục đích nội bộ.
(2) Đây có phải là một khái niệm mà các nhà thiết kế khung có thể xem xét thay đổi hoặc cập nhật không?
Tôi đang suy nghĩ về việc sử dụng chữ ký sự kiện sử dụng 'người gửi' được nhập mạnh, thay vì nhập nó là 'đối tượng', đây là mẫu thiết kế .NET hiện tại. Đó là, thay vì sử dụng chữ ký sự kiện tiêu chuẩn trông như thế này:
class Publisher
{
public event EventHandler<PublisherEventArgs> SomeEvent;
}
Tôi đang xem xét sử dụng chữ ký sự kiện sử dụng tham số 'người gửi' được đánh mạnh, như sau:
Đầu tiên, xác định một "StrongTypedEventHandler":
[SerializableAttribute]
public delegate void StrongTypedEventHandler<TSender, TEventArgs>(
TSender sender,
TEventArgs e
)
where TEventArgs : EventArgs;
Đây không phải là tất cả những gì khác biệt với một Hành động <TSender, TEventArgs>, nhưng bằng cách sử dụng StrongTypedEventHandler
, chúng tôi thực thi mà TEventArgs có được từ đó System.EventArgs
.
Tiếp theo, làm ví dụ, chúng ta có thể sử dụng StrongTypedEventHandler trong một lớp xuất bản như sau:
class Publisher
{
public event StrongTypedEventHandler<Publisher, PublisherEventArgs> SomeEvent;
protected void OnSomeEvent()
{
if (SomeEvent != null)
{
SomeEvent(this, new PublisherEventArgs(...));
}
}
}
Sự sắp xếp trên sẽ cho phép người đăng ký sử dụng trình xử lý sự kiện kiểu mạnh mà không yêu cầu truyền:
class Subscriber
{
void SomeEventHandler(Publisher sender, PublisherEventArgs e)
{
if (sender.Name == "John Smith")
{
// ...
}
}
}
Tôi hoàn toàn nhận ra rằng điều này không đúng với mẫu xử lý sự kiện .NET tiêu chuẩn; tuy nhiên, hãy nhớ rằng sự trái ngược sẽ cho phép người đăng ký sử dụng chữ ký xử lý sự kiện truyền thống nếu muốn:
class Subscriber
{
void SomeEventHandler(object sender, PublisherEventArgs e)
{
if (((Publisher)sender).Name == "John Smith")
{
// ...
}
}
}
Nghĩa là, nếu một trình xử lý sự kiện cần đăng ký các sự kiện từ các loại đối tượng khác nhau (hoặc có thể không xác định), trình xử lý có thể nhập tham số 'sender' là 'object' để xử lý toàn bộ các đối tượng người gửi tiềm năng.
Ngoài việc phá vỡ quy ước (đó là điều mà tôi không coi thường, tin tôi đi), tôi không thể nghĩ ra bất kỳ mặt trái nào của điều này.
Có thể có một số vấn đề tuân thủ CLS ở đây. Điều này chạy trong Visual Basic .NET 2008 100% tốt (tôi đã thử nghiệm), nhưng tôi tin rằng các phiên bản cũ hơn của Visual Basic .NET đến 2005 không có hiệp phương sai đại biểu và phương sai. [Chỉnh sửa: Tôi đã thử nghiệm điều này kể từ đó và được xác nhận: VB.NET 2005 trở xuống không thể xử lý điều này, nhưng VB.NET 2008 là tốt 100%. Xem "Chỉnh sửa # 2" bên dưới.] Có thể có các ngôn ngữ .NET khác cũng gặp sự cố với vấn đề này, tôi không thể chắc chắn.
Nhưng tôi không thấy mình đang phát triển cho bất kỳ ngôn ngữ nào ngoài C # hoặc Visual Basic .NET và tôi không ngại giới hạn ngôn ngữ đó đối với C # và VB.NET cho .NET Framework 3.0 trở lên. (Thành thật mà nói, tôi không thể tưởng tượng sẽ quay trở lại 2.0 vào thời điểm này.)
Bất cứ ai khác có thể nghĩ ra một vấn đề với điều này? Hay điều này chỉ đơn giản là phá vỡ quy ước đến mức khiến bao tử của mọi người quay cuồng?
Dưới đây là một số liên kết liên quan mà tôi đã tìm thấy:
(1) Hướng dẫn thiết kế sự kiện [MSDN 3.5]
(3) Mẫu chữ ký sự kiện trong .net [StackOverflow 2008]
Tôi quan tâm đến ý kiến của bất kỳ ai và của mọi người về điều này ...
Cảm ơn trước,
Mike
Chỉnh sửa # 1: Đây là phản hồi cho bài đăng của Tommy Carlier :
Dưới đây là một ví dụ hoạt động đầy đủ cho thấy rằng cả trình xử lý sự kiện kiểu mạnh và trình xử lý sự kiện tiêu chuẩn hiện tại sử dụng tham số 'object sender' đều có thể cùng tồn tại với phương pháp này. Bạn có thể sao chép-dán mã và chạy mã:
namespace csScrap.GenericEventHandling
{
class PublisherEventArgs : EventArgs
{
// ...
}
[SerializableAttribute]
public delegate void StrongTypedEventHandler<TSender, TEventArgs>(
TSender sender,
TEventArgs e
)
where TEventArgs : EventArgs;
class Publisher
{
public event StrongTypedEventHandler<Publisher, PublisherEventArgs> SomeEvent;
public void OnSomeEvent()
{
if (SomeEvent != null)
{
SomeEvent(this, new PublisherEventArgs());
}
}
}
class StrongTypedSubscriber
{
public void SomeEventHandler(Publisher sender, PublisherEventArgs e)
{
MessageBox.Show("StrongTypedSubscriber.SomeEventHandler called.");
}
}
class TraditionalSubscriber
{
public void SomeEventHandler(object sender, PublisherEventArgs e)
{
MessageBox.Show("TraditionalSubscriber.SomeEventHandler called.");
}
}
class Tester
{
public static void Main()
{
Publisher publisher = new Publisher();
StrongTypedSubscriber strongTypedSubscriber = new StrongTypedSubscriber();
TraditionalSubscriber traditionalSubscriber = new TraditionalSubscriber();
publisher.SomeEvent += strongTypedSubscriber.SomeEventHandler;
publisher.SomeEvent += traditionalSubscriber.SomeEventHandler;
publisher.OnSomeEvent();
}
}
}
Chỉnh sửa # 2: Đây là câu trả lời cho tuyên bố của Andrew Hare về hiệp phương sai và phương sai và cách nó áp dụng ở đây. Các đại biểu trong ngôn ngữ C # đã có hiệp phương sai và phương sai trong thời gian dài đến mức nó chỉ cảm thấy "nội tại", nhưng không phải vậy. Nó thậm chí có thể là một cái gì đó được kích hoạt trong CLR, tôi không biết, nhưng Visual Basic .NET đã không nhận được khả năng hiệp phương sai và tương phản cho các đại biểu của nó cho đến .NET Framework 3.0 (VB.NET 2008). Và kết quả là Visual Basic.NET cho .NET 2.0 trở xuống sẽ không thể sử dụng phương pháp này.
Ví dụ, ví dụ trên có thể được dịch sang VB.NET như sau:
Namespace GenericEventHandling
Class PublisherEventArgs
Inherits EventArgs
' ...
' ...
End Class
<SerializableAttribute()> _
Public Delegate Sub StrongTypedEventHandler(Of TSender, TEventArgs As EventArgs) _
(ByVal sender As TSender, ByVal e As TEventArgs)
Class Publisher
Public Event SomeEvent As StrongTypedEventHandler(Of Publisher, PublisherEventArgs)
Public Sub OnSomeEvent()
RaiseEvent SomeEvent(Me, New PublisherEventArgs)
End Sub
End Class
Class StrongTypedSubscriber
Public Sub SomeEventHandler(ByVal sender As Publisher, ByVal e As PublisherEventArgs)
MessageBox.Show("StrongTypedSubscriber.SomeEventHandler called.")
End Sub
End Class
Class TraditionalSubscriber
Public Sub SomeEventHandler(ByVal sender As Object, ByVal e As PublisherEventArgs)
MessageBox.Show("TraditionalSubscriber.SomeEventHandler called.")
End Sub
End Class
Class Tester
Public Shared Sub Main()
Dim publisher As Publisher = New Publisher
Dim strongTypedSubscriber As StrongTypedSubscriber = New StrongTypedSubscriber
Dim traditionalSubscriber As TraditionalSubscriber = New TraditionalSubscriber
AddHandler publisher.SomeEvent, AddressOf strongTypedSubscriber.SomeEventHandler
AddHandler publisher.SomeEvent, AddressOf traditionalSubscriber.SomeEventHandler
publisher.OnSomeEvent()
End Sub
End Class
End Namespace
VB.NET 2008 có thể chạy tốt 100%. Nhưng bây giờ tôi đã thử nghiệm nó trên VB.NET 2005, chỉ để chắc chắn, và nó không biên dịch, nói rõ:
Phương thức 'Public Sub SomeEventHandler (sender As Object, e As vbGenericEventHandling.GenericEventHandling.PublisherEventArgs)' không có chữ ký giống như đại diện 'Delegate Sub StrongTypedEventHandler (Of TSender, TEventArgs As System.EventAventArgs) (Người gửi As Publisher, eventArgs) '
Về cơ bản, các đại biểu là bất biến trong VB.NET phiên bản 2005 trở xuống. Tôi thực sự đã nghĩ ra ý tưởng này cách đây vài năm, nhưng việc VB.NET không có khả năng giải quyết vấn đề này đã làm phiền tôi ... Nhưng bây giờ tôi đã chuyển hẳn sang C # và VB.NET hiện có thể xử lý nó, vì vậy, do đó, bài này.
Chỉnh sửa: Cập nhật # 3
Ok, tôi đã sử dụng cái này khá thành công trong một thời gian. Nó thực sự là một hệ thống tốt đẹp. Tôi quyết định đặt tên "StrongTypedEventHandler" của mình là "GenericEventHandler", được định nghĩa như sau:
[SerializableAttribute]
public delegate void GenericEventHandler<TSender, TEventArgs>(
TSender sender,
TEventArgs e
)
where TEventArgs : EventArgs;
Ngoài việc đổi tên này, tôi đã thực hiện nó chính xác như đã thảo luận ở trên.
Nó vượt qua quy tắc FxCop CA1009, trong đó nêu rõ:
"Theo quy ước, các sự kiện .NET có hai tham số chỉ định người gửi sự kiện và dữ liệu sự kiện. Chữ ký của trình xử lý sự kiện phải theo mẫu sau: void MyEventHandler (object sender, EventArgs e). Tham số 'sender' luôn thuộc loại System.Object, ngay cả khi có thể sử dụng một loại cụ thể hơn. Tham số 'e' luôn thuộc loại System.EventArgs. Các sự kiện không cung cấp dữ liệu sự kiện nên sử dụng loại ủy quyền System.EventHandler. Các trình xử lý sự kiện trả về void để chúng có thể gửi mỗi sự kiện cho nhiều phương thức đích. Bất kỳ giá trị nào được trả về bởi một mục tiêu sẽ bị mất sau lần gọi đầu tiên. "
Tất nhiên, chúng tôi biết tất cả những điều này, và dù sao cũng đang vi phạm các quy tắc. (Tất cả các trình xử lý sự kiện có thể sử dụng 'Người gửi đối tượng' tiêu chuẩn trong chữ ký của họ nếu được ưu tiên trong mọi trường hợp - đây là một thay đổi không vi phạm.)
Vì vậy, việc sử dụng một SuppressMessageAttribute
thực hiện mẹo:
[SuppressMessage("Microsoft.Design", "CA1009:DeclareEventHandlersCorrectly",
Justification = "Using strong-typed GenericEventHandler<TSender, TEventArgs> event handler pattern.")]
Tôi hy vọng rằng cách tiếp cận này sẽ trở thành tiêu chuẩn vào một thời điểm nào đó trong tương lai. Nó thực sự hoạt động rất độc đáo.
Cảm ơn tất cả các ý kiến của các bạn, tôi thực sự đánh giá cao nó ...
Mike
oh hi this my hom work solve it plz :code dump:
câu hỏi cỡ tweet này , mà là một câu hỏi mà chúng tôi rút kinh nghiệm .
EventHandler<,>
hơn GenericEventHandler<,>
. Đã có chung chung EventHandler<>
trong BCL được đặt tên chỉ là EventHandler. Vì vậy, EventHandler là một cái tên phổ biến hơn và các đại biểu ủng hộ kiểu quá tải