tags:

views:

1619

answers:

6

If I am assigning an event handler at runtime and it is in a spot that can be called multiple times, what is the recommended practice to prevent multiple assignments of the same handler to the same event.

object.Event += MyFunction

Adding this in a spot that will be called more than once will execute the handler 'n' times (of course).

I have resorted to removing any previous handler before trying to add via

object.Event -= MyFunction;

object.Event += MyFunction;

This works but seems off somehow. Any suggestions on proper handling ;) of this scenario.

+1  A: 

I tend to add an event handler in a path that's executed once, for example in a constructor.

ChrisW
+6  A: 

Baget is right about using an explicitly implemented event (although there's a mixture there of explicit interface implementation and the full event syntax). You can probably get away with this:

private EventHandler foo;

public event EventHandler Foo
{
    add
    {
        // First try to remove the handler, then re-add it
        foo -= value;
        foo += value;
    }
    remove
    {
        foo -= value;
    }
}

That may have some odd edge cases if you ever add or remove multicast delegates, but that's unlikely. It also needs careful documentation as it's not the way that events normally work.

Jon Skeet
A: 

I am asking can we use readonly??

Ahmed Said
+1  A: 

You can implement your own storage of the delgates, and check for uniqueness when adding them to the event. See EventOwner2 class below for an example. I don't know how this is doing performance wise, but than again, that is not always an issue.

using System;
using System.Collections.Generic;

namespace EventExperiment
{
    class Program
    {
        static void Main(string[] args)
        {
            IEventOwner e=new EventOwner2();
            Subscriber s=new Subscriber(e);
            e.RaiseSome();
            Console.ReadKey();
        }
    }

    /// <summary>
    /// A consumer class, subscribing twice to the event in it's constructor.
    /// </summary>
    public class Subscriber
    {
        public Subscriber(IEventOwner eventOwner)
        {
            eventOwner.SomeEvent += eventOwner_SomeEvent;
            eventOwner.SomeEvent += eventOwner_SomeEvent;
        }

        void eventOwner_SomeEvent(object sender, EventArgs e)
        {
            Console.WriteLine(DateTimeOffset.Now);
        }

    }

    /// <summary>
    /// This interface is not essensial to this point. it is just added for conveniance.
    /// </summary>
    public interface IEventOwner
    {
        event EventHandler<EventArgs> SomeEvent;
        void RaiseSome();
    }

    /// <summary>
    /// A traditional event. This is raised for each subscription.
    /// </summary>
    public class EventOwner1 : IEventOwner
    {
        public event EventHandler<EventArgs> SomeEvent = delegate { };
        public void RaiseSome()
        {
            SomeEvent(this,new EventArgs());
        }
    }
    /// <summary>
    /// A custom event. This is raised only once for each subscriber.
    /// </summary>
    public class EventOwner2 : IEventOwner
    {
        private readonly List<EventHandler<EventArgs>> handlers=new List<EventHandler<EventArgs>>();
        public event EventHandler<EventArgs> SomeEvent
        {
            add
            {
                lock (handlers)
                    if (handlers!=null&&!handlers.Contains(value))
                    {
                        handlers.Add(value);
                    }
            }
            remove
            {
                handlers.Remove(value);
            }
        }
        public void RaiseSome()
        {
            EventArgs args=new EventArgs();
            lock(handlers)
            foreach (EventHandler<EventArgs> handler in handlers)
            {
                handler(this,args);
            }
        }
    }
}
Øyvind Skaar
A: 

What is the access modifier of 'object'?

If it's private, you only need to worry about the containing object setting the event handler. If it's internal, you only need to worry about the containing assembly setting the event handler. If it's public, then it's wide-open.

If 'object' can be made private on the containing class, you can make your checks much more efficient by controlling event handler assignment in the local class.

If 'internal' or 'public' and uniqueness is a requirement, go with a wrapper class that hides 'object' and instead exposes a method for assigning an event handler with your checks behind it to ensure uniqueness.

jro
A: 

I think the answer I accepted is the closest to what I was asking.

It seems that my original approach would still be the best way for events in classes that I don't have access to. (e.g. Form controls).

Opinions?