如何确保一次活动只订阅一次
我想确保我只在一个实例的某个特定类中订阅一次。如何确保一次活动只订阅一次
比如我想能够做到以下几点:
if (*not already subscribed*)
{
member.Event += new MemeberClass.Delegate(handler);
}
我如何去实现这样的后卫?
如果您正在讨论可以访问源的类上的事件,那么您可以将警卫置于事件定义中。
private bool _eventHasSubscribers = false;
private EventHandler<MyDelegateType> _myEvent;
public event EventHandler<MyDelegateType> MyEvent
{
add
{
if (_myEvent == null)
{
_myEvent += value;
}
}
remove
{
_myEvent -= value;
}
}
这将确保只有一个用户可以订阅的事件上提供该事件的类的实例。
编辑请查看关于为什么上面的代码是一个坏主意,而不是线程安全的评论。
如果您的问题是客户端的单个实例多次订阅(并且您需要多个订阅者),那么客户端代码将需要处理该问题。所以更换
尚未订阅
与当您订阅该事件的第一次时设置客户端类的布尔成员。
编辑(后接受):基于从@Glen T(问题的提交者)为接受的解决方案,他跟去了代码的注释是在客户端类:
if (alreadySubscribedFlag)
{
member.Event += new MemeberClass.Delegate(handler);
}
在哪里alreadySubscribedFlag是跟踪特定事件的第一次订阅的客户端类中的成员变量。 查看第一个代码片段的人请注意@ Rune的评论 - 以非明显的方式改变订阅活动的行为并不是一个好主意。
编辑31/7/2009:请参阅@Sam Saffron的评论。正如我已经说过的,Sam同意这里介绍的第一种方法不是修改事件订阅行为的明智方法。该班级的消费者需要了解其内部实施情况以了解其行为。不大好。
@Sam Saffron也评论了线程安全。我假设他指的是两个用户(接近)同时尝试订阅的可能的竞争状态,他们可能最终订阅。锁可以用来改善这一点。如果你打算改变事件订阅的方式,那么我建议你read about how to make the subscription add/remove properties thread safe。
你要么需要存储一个独立的标志,指示你是否愿意认购或者,如果你有过成员类的控制,提供了附加的实现,并删除该事件的方法:
class MemberClass
{
private EventHandler _event;
public event EventHandler Event
{
add
{
if(/* handler not already added */)
{
_event+= value;
}
}
remove
{
_event-= value;
}
}
}
要决定是否添加了处理程序,您需要比较从_event和value中的GetInvocationList()返回的委托。
正如其他人所示,您可以覆盖事件的添加/删除属性。或者,您可能想要放弃该事件,并且只需让该类在其构造函数(或其他方法)中将一个委托作为参数,而不是触发该事件,则调用所提供的委托。
事件意味着任何人都可以订阅他们,而代表是一个您可以传递给该类的方法。那么,如果你只在事实上包含了它通常提供的一对多语义时才使用事件,那么对于你的图书馆用户来说,可能会不那么令人惊讶。
我在所有重复问题中添加了这个,只是为了记录。这种模式为我工作:
myClass.MyEvent -= MyHandler;
myClass.MyEvent += MyHandler;
注意,这样做,每次您注册的处理程序将确保您的处理程序注册一次。
U可以使用Postsharper只写一次属性并在正常事件中使用它。重用代码。代码示例如下。
[Serializable]
public class PreventEventHookedTwiceAttribute: EventInterceptionAspect
{
private readonly object _lockObject = new object();
readonly List<Delegate> _delegates = new List<Delegate>();
public override void OnAddHandler(EventInterceptionArgs args)
{
lock(_lockObject)
{
if(!_delegates.Contains(args.Handler))
{
_delegates.Add(args.Handler);
args.ProceedAddHandler();
}
}
}
public override void OnRemoveHandler(EventInterceptionArgs args)
{
lock(_lockObject)
{
if(_delegates.Contains(args.Handler))
{
_delegates.Remove(args.Handler);
args.ProceedRemoveHandler();
}
}
}
}
就这样使用它。
[PreventEventHookedTwice]
public static event Action<string> GoodEvent;
详情看Implement Postsharp EventInterceptionAspect to prevent an event Handler hooked twice
在我看来,像一个简单的方法来做到这一点是退订处理程序(如果没有订阅,这将失败默默)和然后订阅。
member.Event -= eventHandler;
member.Event += eventHandler;
它确实发生在开发商,这是错误的方式做到这一点负担,但是对于快速和肮脏,这是快速和肮脏。
我想我会继续使用布尔成员变量的方法。 但是我有点惊讶,没有其他方法来检查客户是否已经订阅。我会认为给定的客户只想订阅一次是比较常见的? – 2008-12-15 07:26:25
根据您的设置,如果事件已经有用户,您可能想要抛出异常。如果您在运行时添加订户,则会通知他们错误,而不是无所事事。在没有通知用户的情况下更改默认行为并不是最佳做法。 – 2008-12-15 07:58:32