If you subscribe the .net event with the same subscribe more then once, then your subscribed method will be called the same times as subscribed. And if you unsubscribe just once, then it will be just one minus to the call. Meaning you have to unsubscribe same no of times as subscribed, otherwise u will be keep informed. Somtimes you don't want to do it.
In order to to prevent an event Handler to be hooked twice, we can implement event as following.
private EventHandler foo;
public event EventHandler Foo
{
add
{
if( foo == null || !foo.GetInvocationList().Contains(value) )
{
foo += value;
}
}
remove
{
foo -= value;
}
}
Now I would like to implement Postsharp EventInterceptionAspect to make this solution generic, so that I can apply PreventEventHookedTwiceAttribute
on every event to save lot of code. But I can't figure out how to check the second part of the following condition in add. I mean foo.GetInvocationList().Contains(value). My PreventEventHookedTwiceAttribute looks like following.
[Serializable]
public class PreventEventHookedTwiceAttribute: EventInterceptionAspect
{
public override void OnAddHandler(EventInterceptionArgs args)
{
if(args.Event == null || secondConditionRequired) // secondConditionRequired means it is required.
{
args.ProceedAddHandler();
}
}
}
I don't need to override OnRemoveHandler, as default functionality is enough here.
This class does the trick.
[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();
}
}
}
}
Example Usage to display the difference is given as below.
class Program
{
private static readonly object _lockObject = new object();
private static int _counter = 1;
[PreventEventHookedTwice]
public static event Action<string> GoodEvent;
public static event Action<string> BadEvent;
public static void Handler (string message)
{
lock(_lockObject)
{
Console.WriteLine(_counter +": "+ message);
_counter++;
}
}
static void Main(string[] args)
{
GoodEvent += Handler;
GoodEvent += Handler;
GoodEvent += Handler;
GoodEvent += Handler;
GoodEvent += Handler;
Console.WriteLine("Firing Good Event. Good Event is subscribed 5 times from the same Handler.");
GoodEvent("Good Event is Invoked.");
_counter = 1;
BadEvent += Handler;
BadEvent += Handler;
BadEvent += Handler;
BadEvent += Handler;
BadEvent += Handler;
Console.WriteLine("Firing Bad Event. Bad Event is subscribed 5 times from the same Handler.");
BadEvent("Bad Event is Invoked.");
_counter = 1;
GoodEvent -= Handler;
Console.WriteLine("GoodEvent is unsubscribed just once. Now fire the Event");
if(GoodEvent!= null)
{
GoodEvent("Good Event Fired");
}
Console.WriteLine("Event is not received to Handler.");
BadEvent -= Handler;
Console.WriteLine("BadEvent is unsubscribed just once. Now fire the Event");
BadEvent("Good Event Fired");
Console.WriteLine("Event is fired 4 times. If u subscribe good event 5 times then u have to unscribe it for 5 times, otherwise u will be keep informed.");
Console.ReadLine();
}
}
Postsharp Rocks.