Implement Postsharp EventInterceptionAspect to prevent an event Handler hooked twice
Asked Answered
H

1

1

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.

Hadley answered 17/4, 2012 at 9:2 Comment(3)
You don't want the same subscriber subscribing twice twice or you only want a single subscriber period?Belomancy
@DustinDavis: Thanks for the question. I want to prevent the same subscriber subscribing twice. like this exmple: void EventHandler(){.....} Main() { someClass.SomeEvent += EventHandler; someClass.SomeEvent += EventHandler; } If I use the normal events, then EventHandler method will be invoked twice. But If I use the pattern like implemented in Foo then it will be called just once.Hadley
@DustinDavis: I solved this problem. Please see my answer.Hadley
H
2

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.

Hadley answered 20/4, 2012 at 16:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.