我完全意识到我提出的建议并不遵循.NET准则,因此,仅凭这个原因可能是一个糟糕的想法.但是,我想从两个可能的角度考虑这个问题:
(1)我是否应考虑将此用于我自己的开发工作,100%用于内部目的.
(2)这是框架设计者可以考虑改变或更新的概念吗?
我正在考虑使用一个使用强类型"发送者"的事件签名,而不是将其键入"对象",这是当前的.NET设计模式.也就是说,而不是使用如下所示的标准事件签名:
class Publisher
{
public event EventHandler<PublisherEventArgs> SomeEvent;
}
Run Code Online (Sandbox Code Playgroud)
我正在考虑使用一个使用强类型'sender'参数的事件签名,如下所示:
首先,定义一个"StrongTypedEventHandler":
[SerializableAttribute]
public delegate void StrongTypedEventHandler<TSender, TEventArgs>(
TSender sender,
TEventArgs e
)
where TEventArgs : EventArgs;
Run Code Online (Sandbox Code Playgroud)
这与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(...));
}
}
}
Run Code Online (Sandbox Code Playgroud)
上述安排将使订阅者能够使用不需要强制转换的强类型事件处理程序:
class Subscriber
{
void SomeEventHandler(Publisher sender, PublisherEventArgs e)
{
if (sender.Name == "John Smith")
{
// ...
}
}
} …Run Code Online (Sandbox Code Playgroud) .NET中的事件有一个标准模式 - 它们使用一个delegate类型,它接受一个名为sender的普通对象,然后是第二个参数中的实际"payload",它应该从中派生出来EventArgs.
派生的第二个参数的基本原理EventArgs似乎非常清楚(请参阅.NET Framework标准库带注释的参考).它旨在确保随着软件的发展,事件接收器和源之间的二进制兼容性.对于每个事件,即使它只有一个参数,我们派生一个自定义事件参数类,它具有包含该参数的单个属性,这样我们就可以保留在未来版本中向有效负载添加更多属性而不破坏现有客户端代码的能力. .在独立开发组件的生态系统中非常重要.
但我发现零参数也是如此.这意味着如果我的第一个版本中有一个没有参数的事件,我会写:
public event EventHandler Click;
Run Code Online (Sandbox Code Playgroud)
......然后我做错了.如果我将来的委托类型更改为新的类作为其有效负载:
public class ClickEventArgs : EventArgs { ...
Run Code Online (Sandbox Code Playgroud)
...我将破坏与客户的二进制兼容性.客户端上界到内部方法的具体超载add_Click是需要EventHandler的,如果我改变了委托类型,然后他们无法找到超载,所以有一个MissingMethodException.
好的,那么如果我使用方便的通用版本怎么办?
public EventHandler<EventArgs> Click;
Run Code Online (Sandbox Code Playgroud)
不,仍然是错的,因为一个EventHandler<ClickEventArgs>不是EventHandler<EventArgs>.
因此,为了获得好处EventArgs,您必须从中获取,而不是直接使用它.如果你不这样做,你也可以不使用它(在我看来).
然后是第一个论点,sender.在我看来,这似乎是一个邪恶的耦合配方.事件触发本质上是一个函数调用.一般来说,这个函数是否应该能够通过堆栈挖掘并找出调用者是谁,并相应地调整其行为?我们应该强制要求接口看起来像这样吗?
public interface IFoo
{
void Bar(object caller, int actualArg1, ...);
}
Run Code Online (Sandbox Code Playgroud)
毕竟,实现者Bar可能想知道是谁caller,所以他们可以查询更多信息!我希望你现在正在呕吐.为什么事件会有所不同?
因此,即使我已经准备好为EventArgs我声明的每个事件都要为一个独立的派生类而烦恼,只是为了让它值得我在使用EventArgs时,我绝对宁愿删除对象发送者参数.
Visual Studio的自动完成功能似乎并不关心您用于事件的委托 - 您可以键入+= [命中空间,返回]并为您编写一个匹配任何委托的处理程序方法.
那么偏离标准模式我会失去什么价值? …
可能重复:
在C#事件处理程序中,为什么"sender"参数必须是对象?
.NET中的事件签名 - 使用强类型"发件人"?
为什么.NET中的所有事件都有类型的第一个参数object,而不是泛型类型T?每次我必须得到我的发件人,我必须将它转换为更多派生类型.例如:
(Button)sender
Run Code Online (Sandbox Code Playgroud)