Solved

C# Event Delegates

Posted on 2011-02-11
16
398 Views
Last Modified: 2012-05-11
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.
0
Comment
Question by:Silas2
  • 9
  • 6
16 Comments
 
LVL 11

Expert Comment

by:SAMIR BHOGAYTA
Comment Utility
0
 

Author Comment

by:Silas2
Comment Utility
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

0
 
LVL 33

Expert Comment

by:Todd Gerbert
Comment Utility
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

0
 
LVL 33

Expert Comment

by:Todd Gerbert
Comment Utility
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;
      }
0
 
LVL 33

Expert Comment

by:Todd Gerbert
Comment Utility
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

0
 

Author Comment

by:Silas2
Comment Utility
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...
0
 
LVL 33

Expert Comment

by:Todd Gerbert
Comment Utility
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.
0
 

Author Comment

by:Silas2
Comment Utility
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)
0
What Should I Do With This Threat Intelligence?

Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

 
LVL 33

Expert Comment

by:Todd Gerbert
Comment Utility
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.
0
 
LVL 33

Expert Comment

by:Todd Gerbert
Comment Utility
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. ;)
0
 

Author Comment

by:Silas2
Comment Utility
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?
0
 
LVL 33

Accepted Solution

by:
Todd Gerbert earned 250 total points
Comment Utility
The way the compiler implements events with the short-hand syntax, e.g. without the add/remove blocks, all the delegates are combined into one multicast delegate. 99.999% of the time this is the behavior that you'll want - which is precisely why that syntactical shortcut exists.

There are circumstances where you might not want that to happen, however.  That multicast delegate will execute each method in the list synchronously, one after the other.  Suppose you want them all to execute in parallel. You could use the add/remove blocks to store the individual delegates.  Then upon event invocation you could loop through the list and launch each delegate in it's own thread.  MSDN cites another - probably more realistic example - something about implementing an event from two different interfaces, but of the same name (http://msdn.microsoft.com/en-us/library/bb882534.aspx and http://msdn.microsoft.com/en-us/library/ak9w5846.aspx).

I have never personally had the need to use manual add/remove accessors for an event, and very rarely do I even see any mention of it in various documentation, books, forums, questions, etc.
0
 
LVL 33

Expert Comment

by:Todd Gerbert
Comment Utility
"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.
0
 

Author Comment

by:Silas2
Comment Utility
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
0
 
LVL 33

Expert Comment

by:Todd Gerbert
Comment Utility
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.
0
 

Author Comment

by:Silas2
Comment Utility
Right, thanks for all you help.
0

Featured Post

Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

This article is for Object-Oriented Programming (OOP) beginners. An Interface contains declarations of events, indexers, methods and/or properties. Any class which implements the Interface should provide the concrete implementation for each Inter…
It was really hard time for me to get the understanding of Delegates in C#. I went through many websites and articles but I found them very clumsy. After going through those sites, I noted down the points in a easy way so here I am sharing that unde…
Sending a Secure fax is easy with eFax Corporate (http://www.enterprise.efax.com). First, Just open a new email message.  In the To field, type your recipient's fax number @efaxsend.com. You can even send a secure international fax — just include t…
This tutorial demonstrates a quick way of adding group price to multiple Magento products.

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

8 Experts available now in Live!

Get 1:1 Help Now