我完全意识到,我提出的建议不遵循.NET准则,因此,仅鉴于此原因,可能是一个糟糕的主意。但是,我想从两个可能的角度考虑这一点:
(1)我应该考虑将其用于我自己的开发工作,这是100%用于内部目的。
(2)这是框架设计者可以考虑更改或更新的概念吗?
我正在考虑使用利用强类型的“发送者”的事件签名,而不是将其键入为“对象”,这是当前的.NET设计模式。也就是说,不是使用如下所示的标准事件签名:
class Publisher
{
public event EventHandler<PublisherEventArgs> SomeEvent;
}
我正在考虑使用利用强类型的“发件人”参数的事件签名,如下所示:
首先,定义一个“ StrongTypedEventHandler”:
[SerializableAttribute]
public delegate void StrongTypedEventHandler<TSender, TEventArgs>(
TSender sender,
TEventArgs e
)
where TEventArgs : EventArgs;
这与Action 并没有什么不同,但是通过使用StrongTypedEventHandler
,我们强制TEventArgs源自System.EventArgs
。
接下来,作为示例,我们可以在发布类中使用StrongTypedEventHandler,如下所示:
class Publisher
{
public event StrongTypedEventHandler<Publisher, PublisherEventArgs> SomeEvent;
protected void OnSomeEvent()
{
if (SomeEvent != null)
{
SomeEvent(this, new PublisherEventArgs(...));
}
}
}
上述安排将使订户能够使用不需要强制转换的强类型事件处理程序:
class Subscriber
{
void SomeEventHandler(Publisher sender, PublisherEventArgs e)
{
if (sender.Name == "John Smith")
{
// ...
}
}
}
我完全意识到这与标准.NET事件处理模式不符;但是,请记住,如果需要,可以使订户使用传统的事件处理签名:
class Subscriber
{
void SomeEventHandler(object sender, PublisherEventArgs e)
{
if (((Publisher)sender).Name == "John Smith")
{
// ...
}
}
}
就是说,如果事件处理程序需要订阅来自不同(或未知)对象类型的事件,则该处理程序可以将“ sender”参数键入“ object”,以便处理潜在的发送方对象的全部范围。
除了打破常规(我不会掉以轻心,相信我),我无法想到任何不利之处。
这里可能存在一些CLS合规性问题。这确实可以在Visual Basic .NET 2008中100%正常运行(我已经测试过),但是我认为到2005年的Visual Basic .NET的较旧版本都没有委托协方差和相反方差。 [编辑:我对此进行了测试,并确认:VB.NET 2005及以下版本无法处理此问题,但VB.NET 2008可以100%罚款。请参阅下面的“编辑#2”。]可能还有其他.NET语言对此也有问题,我不确定。
但是我看不到自己为C#或Visual Basic .NET以外的任何其他语言开发,也不介意将其限制为.NET Framework 3.0及更高版本的C#和VB.NET。 (老实说,我无法想象现在回到2.0。)
有人能想到这个问题吗?还是这仅仅是与惯例大相径庭,以至于让人胃口大开?
这是我找到的一些相关链接:
(1)Event Design Guidelines [MSDN 3.5]
(2)C# simple Event Raising - using “sender” vs. custom EventArgs [StackOverflow 2009]
(3)Event signature pattern in .net [StackOverflow 2008]
我对任何人和每个人的看法都感兴趣...
提前致谢,
麦克风
编辑#1:这是对 Tommy Carlier's post 的响应:
这是一个完整的工作示例,该示例显示强类型事件处理程序和使用“对象发送者”参数的当前标准事件处理程序都可以与该方法共存。您可以复制粘贴代码并运行:
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();
}
}
}
编辑2:这是对Andrew Hare's statement关于协方差和协方差及其在此处的应用方式的回应。 C#语言的代表之间一直有协变和矛盾,以至于感觉只是“本能”,而事实并非如此。我不知道,它甚至可能是在CLR中启用的,但是直到.NET Framework 3.0(VB.NET 2008)为止,Visual Basic .NET才没有为其委托提供协方差和逆方差功能。结果,Visual Basic.NET for .NET 2.0及以下版本将无法使用此方法。
例如,可以将以上示例转换为VB.NET,如下所示:
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可以100%正常运行。但是为了确定起见,我现在已经在VB.NET 2005上对其进行了测试,并且没有编译,并指出:
方法'公共子
SomeEventHandler(sender as Object,e
如
vbGenericEventHandling.GenericEventHandling.PublisherEventArgs)'
与以下签名不同
委托'代表小组
StrongTypedEventHandler(Of TSender,
TEventArgs作为System.EventArgs)(发送方
作为发布者,作为
PublisherEventArgs)”
基本上,委托在VB.NET 2005版及更低版本中是不变的。我实际上是在几年前想到这个主意的,但是VB.NET无法处理这个问题困扰了我……但是我现在已经坚定地转向C#,并且VB.NET现在可以处理它了,所以,所以这个帖子。
编辑:更新#3
好的,我已经成功使用了一段时间了。这确实是一个不错的系统。我决定将“ StrongTypedEventHandler”命名为“ GenericEventHandler”,定义如下:
[SerializableAttribute]
public delegate void GenericEventHandler<TSender, TEventArgs>(
TSender sender,
TEventArgs e
)
where TEventArgs : EventArgs;
除了这种重命名,我完全按照上面的讨论实施了它。
它确实跳过了FxCop规则CA1009,该规则指出:
“按照惯例,.NET事件有两个
指定事件的参数
发送者和事件数据。事件处理程序
签名应遵循以下格式:
void MyEventHandler(对象发送者,
EventArgs e)。 'sender'参数
始终为System.Object类型,即使
如果有可能雇用更多
具体类型。 “ e”参数是
始终为System.EventArgs类型。
不提供事件数据的事件
应该使用System.EventHandler
委托类型。事件处理程序返回
无效,以便他们可以发送每个事件
多种目标方法。任何值
目标返回的将丢失
在第一个电话之后。”
当然,我们知道所有这些,并且无论如何都在违反规则。 (在任何情况下,如果需要的话,所有事件处理程序都可以在其签名中使用标准的“对象发件人”,这是不间断的更改。)
因此,使用SuppressMessageAttribute
可以达到目的:
[SuppressMessage("Microsoft.Design", "CA1009:DeclareEventHandlersCorrectly",
Justification = "Using strong-typed GenericEventHandler<TSender, TEventArgs> event handler pattern.")]
我希望这种方法将来会成为标准。它确实非常好用。
感谢您的所有意见,我真的很感激...
麦克风
参考方案
微软似乎已经选择了此方法,因为现在MSDN上也有类似的示例:
Generic Delegates
.NET C#Webbrowser填充输入,不带ID或类名 - javascript我需要在网络浏览器中填写一个输入,但这不起作用。我认为必须使用name属性来完成,但是怎么做呢?foreach (HtmlElement login in webBrowser1.Document.GetElementsByTagName("input")) { if (login.GetAttribute("name"…
ddl在服务器中未更新-asp.net - javascript我在ASP.NET c#上工作。我有一个DropDownList。 (runat =“ server”)在$ {document).ready上,我更新了它的值:$(document).ready(function () { document.getElementById("ddl").value = "abc"; ……
asp.net mvc中的对象数组数据始终为null - javascript我需要通过json将对象数组发送到asp.net mvc 2,但是我在mvc控制器中没有得到null对象是这样的entries[1].date = "12/22/2014" entries[1].Ref = "0002" entries[1].Credit = "100" entries[2].da…
jQuery发布不会将数据发布到ASP.NET API控制器 - javascript我有一次噩梦般的时间通过jquery post将数据发送到ASP.NET Controller。这是JSON.stringify之后的数据:[{"scheduleTaskID":"203","task":"Permit","baselineDate":…
ASP.NET MVC中应为DataTable返回哪种数据? - javascript我想为DataTable中的每个页面创建动态加载信息。我正在尝试遵循以下示例:https://www.datatables.net/manual/server-sidehttps://www.datatables.net/manual/data来自示例的代码:$('#example').DataTable( { serverSide: t…