我充分认识到,我提出的建议不符合。NET 指导原则,因此,仅仅因为这个原因,可能是一个糟糕的想法。不过,我想从两个可能的角度来考虑这个问题:
(1)我是否应该考虑将其用于我自己的开发工作,这是100% 的内部目的。
(2)这是框架设计人员可以考虑更改或更新的概念吗?
我正在考虑使用一个利用强类型“ sender”的事件签名,而不是将其输入为当前的“ object”。NET 设计模式。也就是说,不使用标准的事件签名,如下所示:
class Publisher
{
public event EventHandler<PublisherEventArgs> SomeEvent;
}
我正在考虑使用一个使用强类型“ sender”参数的事件签名,如下所示:
首先,定义一个“ StrongTypedEventHandler”:
[SerializableAttribute]
public delegate void StrongTypedEventHandler<TSender, TEventArgs>(
TSender sender,
TEventArgs e
)
where TEventArgs : EventArgs;
这与 Action < TSender,TEventArgs > 没有什么不同,但是通过使用 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 遵从性问题。这在 VisualBasic 中运行。NET2008100% 没问题(我已经测试过了) ,但是我相信老版本的 VisualBasic。NET 至2005年没有委派反变。可能还有其他。NET 语言也有这个问题,我不能肯定。
但是除了 C # 或 VisualBasic 之外,我没有看到自己在开发任何语言。NET,并且我不介意将它限制在 C # 和 VB.NET 中。NET Framework 3.0及以上版本。(老实说,我无法想象在这个时候回到2.0。)
还有人能想出什么问题吗?或者这仅仅是打破了惯例,以至于让人们反胃?
以下是我发现的一些相关链接:
(2) C # 简单事件引发-使用“ sender”与自定义 EventArgs [ StackOverflow 2009]
(3) Net 中的事件签名模式[ StackOverflow 2008]
我对每个人的意见都很感兴趣。
先说声谢谢,
麦克
编辑 # 1: 这是对 Tommy Carlier 的岗位的回应:
下面是一个完整的工作示例,它显示了强类型事件处理程序和使用“ object sender”参数的当前标准事件处理程序可以与此方法共存。你可以复制粘贴代码并运行它:
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 的证词关于反变及其应用的回应。C # 语言中的代表已经有了很长一段时间的反变,以至于让人感觉它是“内在的”,但事实并非如此。它甚至可能是在 CLR 中启用的,我不知道,但是 VisualBasic。NET 的委托反变直到。NET Framework 3.0(VB.NET 2008).因此,VisualBasic.NET 用于。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
NET 2008可以100% 正常运行。但我现在已经在 VB.NET 2005上进行了测试,以确保它不会编译,并声明:
方法 SomEventHandler (sender As Object,e 就像 VbGenericEventProcessing. GenericEventProcessing. PublisherEventArgs)’ 不具有与 代表代表小组 StrongTypeEventHandler (属于 TSender, 作为系统 作为出版商 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 事件处理程序返回 使他们可以发送每个事件 多个目标方法。任何值 一个目标返回将会丢失 在第一通电话之后”
当然,我们知道这一切,并打破了规则反正。(如果在任何情况下首选,所有事件处理程序都可以在其签名中使用标准的‘ object Sender’——这是一个非破坏性更改。)
因此,使用 SuppressMessageAttribute
就可以解决这个问题:
[SuppressMessage("Microsoft.Design", "CA1009:DeclareEventHandlersCorrectly",
Justification = "Using strong-typed GenericEventHandler<TSender, TEventArgs> event handler pattern.")]
我希望这种方法在将来的某个时候成为标准。
谢谢你们的意见,伙计们,我真的很感激..。
麦克