Link to home
Start Free TrialLog in
Avatar of Silas2
Silas2

asked on

C# Event Delegates

I still haven't made the switch to C#'s take on events from VB, but, can anyone explain why this compiles (.Net 4.0):
public RunRaiseEvent()
        {
           GetTeamMemberList ();
        }
        public event VoidEventHandler GetTeamMemberList;

Open in new window

whereas this doesn't:
public RunRaiseEvent()
        {
               GetTeamMemberList ();
       }
        private EventHandler _myEvent;
        public event VoidEventHandler GetTeamMemberList
        {
            add { _myEvent = (EventHandler)Delegate.Combine(_myEvent, value); }
            remove { _myEvent = (EventHandler)Delegate.Remove(_myEvent, value); }
        }
        

Open in new window


The invocation GetTeamMemberList (); can't see the procedure.
Avatar of SAMIR BHOGAYTA
SAMIR BHOGAYTA
Flag of India image

Avatar of Silas2
Silas2

ASKER

I've read the link, I still can't see why this works (copy paste this straight into a console app):
using System;
namespace ConsoleApplication1
{
   public  delegate void  MyEv(object s, EventArgs e);
   public  class Program 
    {
         static void Main(string[] args)
        {
            Test x = new Test();
            x.Main();
            Console.Read();
        }
   }
   public class Test: WithEvent
   {
       public  void Main()
       {
           GetMemberList += MyDel;
           if (GetMemberList != null) GetMemberList(this, new EventArgs ());
       }

       void MyDel(object s, EventArgs e)
       {
           Console.Write("Called");
       }
       private EventHandler _myEvent;
       public event MyEv GetMemberList;
       //{
       //    add { _myEvent = (EventHandler)Delegate.Combine(_myEvent, value); }
       //    remove { _myEvent = (EventHandler)Delegate.Remove(_myEvent, value); }
       //}
   }
   public  interface WithEvent
   {
        event  MyEv GetMemberList;
   }
}              

Open in new window

and this doesn't:
using System;
namespace ConsoleApplication1
{
   public  delegate void  MyEv(object s, EventArgs e);
   public  class Program 
    {
         static void Main(string[] args)
        {
            Test x = new Test();
            x.Main();
            Console.Read();
        }
   }
   public class Test: WithEvent
   {
       public  void Main()
       {
           GetMemberList += MyDel;
           if (GetMemberList != null) GetMemberList(this, new EventArgs ());
       }

       void MyDel(object s, EventArgs e)
       {
           Console.Write("Called");
       }
       private EventHandler _myEvent;
       public event MyEv GetMemberList
       {
           add { _myEvent = (EventHandler)Delegate.Combine(_myEvent, value); }
           remove { _myEvent = (EventHandler)Delegate.Remove(_myEvent, value); }
       }
   }
   public  interface WithEvent
   {
        event  MyEv GetMemberList;
   }
}

Open in new window

GetMemberList is an event, and only supports addition & removal (the add and remove accessors), so it only makes sense on the left-hand side of a -= or += operation.  In your second snippet above, you would change line 19 to if (_myEvent != null) _myEvent(this, new EventArgs()); - GetMemberList is affects the value of _myEvent, but _myEvent is the actual delegate you want to check for null & invoke.

Also note that you don't need to manually write the add & remove accessors for events in C# - if you omit them the compiler will automatically add simple ones like you have above, it's only necessary to override the default behavior; you also aren't required to declare events in an Interface.

I realize you may intentionally want the event in an interface, but just for the sake of example I would write your second snippet above as:
using System;
namespace ConsoleApplication1
{
	public class Program
	{
		static void Main(string[] args)
		{
			Test x = new Test();
			x.MembersRetrieved += new EventHandler(x_MembersRetrieved);
			x.RetrieveMembers();
			Console.Read();
		}

		static void x_MembersRetrieved(object sender, EventArgs e)
		{
			Console.WriteLine("MembersRetrieved event raised.");
		}
	}
	
	public class Test
	{
		public event EventHandler MembersRetrieved;

		public void RetrieveMembers()
		{
			// raise the event
			OnMembersRetrieved(new EventArgs());
		}

		protected virtual void OnMembersRetrieved(EventArgs e)
		{
			// Check if the delegate belonging to the MembersRetrieved
			// event is null, and if not call it
			if (MembersRetrieved != null)
				MembersRetrieved(this, e);
		}

	}
}

Open in new window

Or, like this with an Interface:
      public class Test: IEventTest
      {
            public event EventHandler MembersRetrieved;

            public void RetrieveMembers()
            {
                  // raise the event
                  OnMembersRetrieved(new EventArgs());
            }

            protected virtual void OnMembersRetrieved(EventArgs e)
            {
                  // Check if the delegate belonging to the MembersRetrieved
                  // event is null, and if not call it
                  if (MembersRetrieved != null)
                        MembersRetrieved(this, e);
            }
      }

      public interface IEventTest
      {
            event EventHandler MembersRetrieved;
      }
Or this, with manual add/remove accessors and an interface
	public class Test: IEventTest
	{
		private EventHandler _event;

		public event EventHandler MembersRetrieved
		{
			add
			{
				_event += value;
			}
			remove
			{
				_event -= value;
			}
		}

		public void RetrieveMembers()
		{
			// raise the event
			OnMembersRetrieved(new EventArgs());
		}

		protected virtual void OnMembersRetrieved(EventArgs e)
		{
			// Check if the delegate belonging to the MembersRetrieved
			// event is null, and if not call it
			if (_event != null)
				_event(this, e);
		}
	}

	public interface IEventTest
	{
		event EventHandler MembersRetrieved;
	}

Open in new window

Avatar of Silas2

ASKER

I think I'm getting warmer. I'm still not sure when you say: "GetMemberList is an event, and only supports addition & removal ", surely in both examples it's an event, but in the working example the add/remove is implied/under the hood but they are the same, aren't they?
PS The interface was just because I was trying to get a MVVM to work...
Yes, under the hood they are the same.

In the case of your first example, using implicit accessors, the compiler knows for certain how add and remove are going to behave, because it's going to write them itself.  Therefore it also knows it can allow you to refer to the event name as if it were the actual delegate.

Using explicitly defined add/remove accessors the compiler doesn't necessarily know how they're going to behave - since there's no "get" like you would find in a property - the compiler really has no way to know which EventHandler delegate you want, consequently it is unable to allow you to refer to the event's name as if it were a variable containing a delegate.  Consider this:

public class TestClass
{
	private EventHandler _evt1;
	private EventHandler _evt2;
	private EventHandler _evt3;
	private int subscriberCount = 0;

	public event EventHandler TestEvent
	{
		add
		{
			if (subscriberCount >= 3)
				throw new Exception("Too many event subscribers.");
			else
			{
				subscriberCount++;

				switch (subscriberCount)
				{
					case 1:
						_evt1 = value;
						break;
					case 2:
						_evt2 = value;
						break;
					case 3:
						_evt3 = value;
						break;
				}
			}
		}
		remove
		{
			switch (subscriberCount)
			{
				case 3:
					_evt3 = null;
					break;
				case 2:
					_evt2 = null;
					break;
				case 1:
					_evt1 = null;
					break;
			}
			subscriberCount--;
		}
	}
}

Open in new window


While the above seems fairly idiotic - it is entirely valid.  And if you tried to refer to TestEvent, to check if it's null or to invoke for example, the compiler would have no way to know whether it should give you _evt1, _evt2 or _evt3.
Avatar of Silas2

ASKER

That makes sense, but I sought of thought that a composite of delegates like (potentially) GetMemberList has a default invocation behviour wouldn't it?
(I know its a pointless question as if that is how the compiler thinks, that's that)
Well, consider my silly example above - if TestClass used this method to raise TestEvent:
protected virtual void OnTesetEvent(EventArgs e)
{
	// Get random num between 1 & 3
	int eventToRaise = random.Next(1, 4);

	// Invoke the EventHandler delegate corresponding
	// to the random number, ignore the other two
	switch (eventToRaise)
	{
		case 1:
			if (_evt1 != null)
				_evt1(this, e);
			break;
		case 2:
			if (_evt2 != null)
				_evt2(this, e);
			break;
		case 3:
			if (_evt3 != null)
				_evt3(this, e);
			break;
	}
}

Open in new window



Here only one of the three possible EventHandlers is invoked, it is randomly selected by OnTestEvent().  You couldn't write if (TestEvent != null) TestEvent(this, e) because TestEvent has no way to know which one of the three EventHandler's you're trying to invoke - really it doesn't even know how, where or if references to the EventHandlers have been stored; it's not possible for the compiler or the runtime to implement any kind of default behavior.

The auto-implemented accessor syntax, e.g. public event EventHandler TestEvent; without add/remove blocks, is a special shortcut syntax whereby the compiler behind-the-scenes adds private EventHandler _testEvent; to your class and, because of the special syntax, knows to translate if (TestEvent != null) TestEvent(this, e) into if (_testEvent != null) _testEvent(this,e) at compile-time.
I would also suggest that even if it were possible to implement some kind of default behavior with manually added event accessors, it would be silly to do so as the whole point of explicitly defined event accessors is to avoid default behavior. ;)
Avatar of Silas2

ASKER

That would seem to beg the question to me: what is the point in aggregating events together if not to execute them as a (anonymous) lump? What other purpose could there be in 'combining' them?
ASKER CERTIFIED SOLUTION
Avatar of Todd Gerbert
Todd Gerbert
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
"You could use the add/remove blocks to store the individual delegates" should read:

You could use the add/remove blocks to store the individual delegates in a List<EventHandler>, for example, instead of combining them.
Avatar of Silas2

ASKER

If its so rare, its strange that when you use the right click-Implement Interface, you  get the add/remove, that's what threw me and broke my code, I would never have thought if otherwise
Choosing Implement Interface will get you the "normal" short form of the event; Implement Interface Explicitly gets you the long form with add/remove blocks.

From http://msdn.microsoft.com/en-us/library/ak9w5846.aspx:
The following example shows how to handle the less-common situation in which your class inherits from two or more interfaces and each interface has an event with the same name. In this situation, you must provide an explicit interface implementation for at least one of the events. When you write an explicit interface implementation for an event, you must also write the add and remove event accessors. Normally these are provided by the compiler, but in this case the compiler cannot provide them.
Avatar of Silas2

ASKER

Right, thanks for all you help.