创建自定义事件 - 对象发送者还是类型发送者?
我搜索了档案,发现了很多关于发件人是什么以及为什么应该使用该模式的问题,但我没有看到任何有关自定义事件和发件人类型的信息。
假设我正在创建一个名为 Subscription 的自定义类,它实现了 ISubscription,并且我有一些名为 SubscriptionEventArgs 的事件参数。如果订阅有一个名为 Changed 的事件,那么事件签名 Changed(ISubscription sender, SubscriptionEventArgs e) 有什么问题吗?
一些代码可以帮助解决这个问题:
public class SubscriptionEventArgs : EventArgs
{
// guts of event args go here
}
public interface ISubscription
{
event Action<ISubscription, SubscriptionEventArgs> Changed;
}
public class Subscription : ISubscription
{
public event Action<ISubscription, SubscriptionEventArgs> Changed;
private void OnChanged(SubscriptionEventArgs e)
{
if (Changed!= null)
{
Changed(this, e);
}
}
}
如果您只是鄙视使用操作代替“EventHandler”,那么您可以做同样的事情,但使用自定义通用“EventHandler”。
public delegate void EventHandler<TSender, TEventArgs>(TSender sender, TEventArgs e);
public class SubscriptionEventArgs : EventArgs
{
// guts of event args go here
}
public interface ISubscription
{
event EventHandler<ISubscription, SubscriptionEventArgs> Changed;
}
public class Subscription : ISubscription
{
public event EventHandler<ISubscription, SubscriptionEventArgs> Changed;
private void OnChanged(SubscriptionEventArgs e)
{
if (Changed!= null)
{
Changed(this, e);
}
}
}
响应 Hans 对示例事件处理程序的请求:
public class SubscriptionCollection
{
// what is actually holding the subscriptions is not really relevant to the question
private List<ISubscription> _subscriptions;
public SubscriptionCollection()
{
_subscriptions = new List<ISubscription>();
}
public void Add(ISubscription subscription)
{
subscription.Changed += new EventHandler<ISubscription, SubscriptionEventArgs>(Subscription_Changed);
_subscriptions.Add(subscription);
}
private void Subscription_Changed(ISubscription sender, SubscriptionEventArgs e)
{
// Now when the subscription changed event is being handled by the collection
// I don't have to look up the subscription in the list by some key and I don't
// have to cast sender to the correct type because the event handler was typed
// correctly from the beginning.
}
}
在列表中查找订阅可能看起来微不足道,但如果我正在处理非常大的数据集,并且新的数据量通过实时传入应用程序,该怎么办?溪流。必须停下来从列表中获取参考或完成铸造步骤的成本是没有意义的。他们在 2.0 中为我们提供了泛型来解决这个问题,所以我不明白为什么我们没有得到通用事件处理程序,这让我质疑通用事件处理程序有什么问题?
I searched through the archives and I found lots of questions about what sender is and why you should use the pattern but I didn't see anything about a custom event and the type if sender.
Say I am creating a custom class called Subscription and it implements ISubscription and I have some event args called SubscriptionEventArgs. If Subscription had an event called Changed what is wrong about the event signature Changed(ISubscription sender, SubscriptionEventArgs e)?
A little code to help drive the question:
public class SubscriptionEventArgs : EventArgs
{
// guts of event args go here
}
public interface ISubscription
{
event Action<ISubscription, SubscriptionEventArgs> Changed;
}
public class Subscription : ISubscription
{
public event Action<ISubscription, SubscriptionEventArgs> Changed;
private void OnChanged(SubscriptionEventArgs e)
{
if (Changed!= null)
{
Changed(this, e);
}
}
}
If you just despise the use of action in place of "EventHandler" then you could do the same thing but with a custom generic "EventHandler".
public delegate void EventHandler<TSender, TEventArgs>(TSender sender, TEventArgs e);
public class SubscriptionEventArgs : EventArgs
{
// guts of event args go here
}
public interface ISubscription
{
event EventHandler<ISubscription, SubscriptionEventArgs> Changed;
}
public class Subscription : ISubscription
{
public event EventHandler<ISubscription, SubscriptionEventArgs> Changed;
private void OnChanged(SubscriptionEventArgs e)
{
if (Changed!= null)
{
Changed(this, e);
}
}
}
In response to Hans' request for a sample event handler:
public class SubscriptionCollection
{
// what is actually holding the subscriptions is not really relevant to the question
private List<ISubscription> _subscriptions;
public SubscriptionCollection()
{
_subscriptions = new List<ISubscription>();
}
public void Add(ISubscription subscription)
{
subscription.Changed += new EventHandler<ISubscription, SubscriptionEventArgs>(Subscription_Changed);
_subscriptions.Add(subscription);
}
private void Subscription_Changed(ISubscription sender, SubscriptionEventArgs e)
{
// Now when the subscription changed event is being handled by the collection
// I don't have to look up the subscription in the list by some key and I don't
// have to cast sender to the correct type because the event handler was typed
// correctly from the beginning.
}
}
The lookup of the subscription in the list might seem trivial but what if I am working with very large sets of data and new volumes of data are coming at the application through a real-time stream. The cost of having to stop and get a reference out of a list or go through the steps of casting don't make sense. They gave us generics in 2.0 to solve that issue so I don't understand why we didn't get a generic event handler too and this led me to question what is wrong with a generic event handler?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
实际上,我很困惑为什么在设计 .Net Framework v2 时,MS 没有按照您描述的方式提供
EventHandler
- 使用TSender
和TEventArgs
作为两个通用参数。 (在 v1 和 v1.1 中,由于它们没有泛型,我完全理解为什么他们没有创建数千个额外的委托类型来处理所有可能的事件。)如果我没记错的话,您仍然可以使用通用处理程序监听更具体的事件:由于您没有将观察者暴露给可观察的对象,所以我不认为这会成为问题;您只是在到达事件处理程序后不需要进行类型检查“以防万一”。我认为这将是一个很棒的实践,尽管有点不标准,因为微软决定不包括它。
正如我在上面的评论中所指出的,我更愿意看到以下 EventHandler 的定义,以便您确实可以始终使用非常通用的处理程序方法,作为我的代码示例:
I actually am quite confused why, when designing the .Net Framework v2, MS didn't provide an
EventHandler
just the way you described - with theTSender
andTEventArgs
as both generic arguments. (In v1 and v1.1, since they didn't have generics, I completely understand why they didn't make thousands of extra delegate types to handle all the possible events.) If I remember properly, you can still use a generalized handler to listen to a more specific event:Since you're not exposing the observer to the observable, I don't see how this could be a problem; you're just preventing the need to do type-checking 'just in case' once you get to the event handler. I think it'd be a fantastic practice, although a bit non-standard since MS decided not to include it.
As noted in my comment above, I would prefer to see the following definition of EventHandler, so that you really could always use a very generalized handler method, as my code sample: