我想確保我只在一個實例的某個特定類中訂閱一次。如何確保一次活動只訂閱一次
比如我想能夠做到以下幾點:
if (*not already subscribed*)
{
member.Event += new MemeberClass.Delegate(handler);
}
我如何去實現這樣的後衛?
我想確保我只在一個實例的某個特定類中訂閱一次。如何確保一次活動只訂閱一次
比如我想能夠做到以下幾點:
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