none
Determining which Event was raised to invoke an EventHandler

    Question

  • I am trying to determine the Event that was raised to invoke an EventHandler, from within the EventHandler itself.

    I can get to the Invoke method, but not the event which was raised.


    Example of what I'm trying to do:

            public event EventHandler Thrown;  
     
            public void Throw()  
            {  
                OnThrown();  
            }  
     
            private void OnThrown()  
            {  
                EventHandler handler = Thrown;  
                if( handler != null )  
                    handler( thisnew EventArgs() );  
            }  
     
            public void AutoSubscribeEvents()  
            {  
                foreach( EventInfo eventInfo in this.GetType().GetEvents() )  
                {  
                    if( eventInfo.EventHandlerType == typeof( EventHandler ) )  
                    {  
                        eventInfo.AddEventHandler( thisnew EventHandler( this_EventHandler ) );  
                    }  
                }  
            }  
     
            void this_EventHandler( object sender, EventArgs e )  
            {  
                StackFrame sf = new StackFrame( 1 );  
     
                Console.WriteLine( sf.GetMethod().Name );  
     
                // I can get the Invoke() method itself, but I want to be able to get "Thrown"  
            } 


    Of course, I can go up a level via StackFrame and get the method name of "OnThrown", and parse that, but I can't make the guarantee that the OnEvent() practice will be followed, nor that the method name will at all match the event that's raised.

    So, is there a way to determine the event that was raised to invoke an EventHandler?  I want to have one handler to handle things like "Throwing" and "Thrown", but I want to be able to tell, in the handler, which event it was.


    Thanks,
    Jonathan
    Tuesday, July 08, 2008 12:30 AM

Answers

  • AlexBB,


    No offense, but this question is out of your level of expertise.

    First, StackFrame has had a constructor since .NET 2.0.  See: http://msdn.microsoft.com/en-us/library/ws7f30w6(VS.80).aspx

    Secondly, the code compiles fine... but no, I didn't include the using statements.  This is probably what you were missing:
    using System.Reflection;  
    using System.Diagnostics; 

    Thirdly, raising events by using a handler like that is the recommended best practice for raising events.  My understanding is that it avoids a race condition, as compared to
             if ( Thrown != null )     
                  Thrown ( thisnew EventArgs ( ) );  

    Refer to the "Raising Events" section of: http://msdn.microsoft.com/en-us/library/ms173168(VS.80).aspx

    When you subscribe to the event, Thrown (or 'handler') is no longer null.  Specifically, it is/has a MulticastDelegate that points to all of the actual methods to call, and invokes them one after another with the parameters you passed.

    Some actual code to demo that:

    using System;  
    using System.Collections.Generic;  
    using System.Text;  
     
    namespace EventingDemo  
    {  
        public class Class1  
        {  
            public event EventHandler SomeEvent;  
     
            public Class1()  
            {  
                this.SomeEvent += new EventHandler( Class1_SomeEvent );  
            }  
     
            public void RaiseSomeEvent()  
            {  
                EventHandler handler = SomeEvent;  
                if( handler != null )  
                    handler( thisnew EventArgs() );  
            }  
     
            void Class1_SomeEvent( object sender, EventArgs e )  
            {  
                Console.WriteLine( "Class1.SomeEvent was invoked and handled within Class1" );  
            }  
        }  
    using System;  
    using System.Collections.Generic;  
    using System.Text;  
     
    namespace EventingDemo  
    {  
        public class Class2  
        {  
            public Class1 _class1 = new Class1();  
     
            public Class2()  
            {  
                _class1.SomeEvent += new EventHandler( _class1_SomeEvent );  
            }  
     
            void _class1_SomeEvent( object sender, EventArgs e )  
            {  
                Console.WriteLine( "Class1.SomeEvent was invoked and handled within Class2" );  
            }  
        }  

    Now....

    using System;  
    using System.Collections.Generic;  
    using System.Text;  
     
    namespace EventingDemo  
    {  
        class Program  
        {  
            static void Main( string[] args )  
            {  
                Class2 class2 = new Class2();  
     
                class2.Class1.RaiseSomeEvent();  
     
                Console.ReadLine();  
            }  
        }  

    And when you run it, the output is this...

    Class1.SomeEvent was invoked and handled within Class1  
    Class1.SomeEvent was invoked and handled within Class2 

    So what's actually happening here?

    When you subscribe to the event, you're giving it a delegate to call when the event is raised.  It gets thrown into a MulticastDelegate.

    When the event itself is raised, it loops through the delegates/eventhandlers (in the order in which they were subscribed, although I don't believe the framework makes any guarantees) and invokes the delegate for each of them.

    In this case, there are two delegates handling the event, and they're both called.


    Anyway... thanks for attempting to help me.  Hope this explains some things to help you.


    Jonathan

    • Edited by JMitchem Tuesday, July 08, 2008 3:13 PM clarification
    • Marked as answer by jack 321 Friday, July 11, 2008 2:24 AM
    Tuesday, July 08, 2008 3:11 PM
  • I'm going to continue discussion on this over in the BCL forum, since I feel it's more of a .NET question than a C# question.

    http://forums.msdn.microsoft.com/en-US/netfxbcl/thread/9f0375b1-d136-457a-a39c-69292af7895c

    • Marked as answer by jack 321 Friday, July 11, 2008 2:25 AM
    Tuesday, July 08, 2008 5:22 PM

All replies

  • Jonathan, I tried to compile your code and it did not. Stackframe class does not have a constructor defined. that is only part of the story. Secondly you should read this:

    ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/fxref_microsoft.jscript/html/f0648acf-9663-34f0-e7e4-186004dc1fbe.htm

    This API supports the .NET Framework infrastructure and is not intended to be used directly from your code.


    AlexB
    Tuesday, July 08, 2008 1:36 AM
  • I also tried to change your code to the condition that it at least compiled but I still see a problem that most likely will preclude it from working.

    public event EventHandler Thrown;  
          public void Throw ( )  
          {  
              OnThrown ( );  
          }  
     
          private void OnThrown ( )  
          {  
              EventHandler handler = Thrown;  // <== the problem is here.
              if ( handler != null )  
                  handler ( this, new EventArgs ( ) );  
          }  
     
          public void AutoSubscribeEvents ( )  
          {  
              foreach ( EventInfo eventInfo in this.GetType ( ).GetEvents (  ) )  
              {  
                  if ( eventInfo.EventHandlerType == typeof ( EventHandler ) )  
                  {  
                      eventInfo.AddEventHandler ( this, new EventHandler ( this_EventHandler ) );  
                  }  
              }  
          }  
     
          void this_EventHandler ( object sender, EventArgs e )  
          {  
              Thrown.Invoke ( sender, e);  
          }   
    I cannot understand how the object handler will be initialized in the statement I marked <==? It appears it is going to be null under any circimstances.
    AlexB
    Tuesday, July 08, 2008 1:45 AM
  • Cross replying:

    Provide the information in an EventArgs derived class and pass an instance of that instead of the base EventArgs to the event handler.


    /Ruben RJJournal
    Tuesday, July 08, 2008 5:31 AM
  • AlexBB,


    No offense, but this question is out of your level of expertise.

    First, StackFrame has had a constructor since .NET 2.0.  See: http://msdn.microsoft.com/en-us/library/ws7f30w6(VS.80).aspx

    Secondly, the code compiles fine... but no, I didn't include the using statements.  This is probably what you were missing:
    using System.Reflection;  
    using System.Diagnostics; 

    Thirdly, raising events by using a handler like that is the recommended best practice for raising events.  My understanding is that it avoids a race condition, as compared to
             if ( Thrown != null )     
                  Thrown ( thisnew EventArgs ( ) );  

    Refer to the "Raising Events" section of: http://msdn.microsoft.com/en-us/library/ms173168(VS.80).aspx

    When you subscribe to the event, Thrown (or 'handler') is no longer null.  Specifically, it is/has a MulticastDelegate that points to all of the actual methods to call, and invokes them one after another with the parameters you passed.

    Some actual code to demo that:

    using System;  
    using System.Collections.Generic;  
    using System.Text;  
     
    namespace EventingDemo  
    {  
        public class Class1  
        {  
            public event EventHandler SomeEvent;  
     
            public Class1()  
            {  
                this.SomeEvent += new EventHandler( Class1_SomeEvent );  
            }  
     
            public void RaiseSomeEvent()  
            {  
                EventHandler handler = SomeEvent;  
                if( handler != null )  
                    handler( thisnew EventArgs() );  
            }  
     
            void Class1_SomeEvent( object sender, EventArgs e )  
            {  
                Console.WriteLine( "Class1.SomeEvent was invoked and handled within Class1" );  
            }  
        }  
    using System;  
    using System.Collections.Generic;  
    using System.Text;  
     
    namespace EventingDemo  
    {  
        public class Class2  
        {  
            public Class1 _class1 = new Class1();  
     
            public Class2()  
            {  
                _class1.SomeEvent += new EventHandler( _class1_SomeEvent );  
            }  
     
            void _class1_SomeEvent( object sender, EventArgs e )  
            {  
                Console.WriteLine( "Class1.SomeEvent was invoked and handled within Class2" );  
            }  
        }  

    Now....

    using System;  
    using System.Collections.Generic;  
    using System.Text;  
     
    namespace EventingDemo  
    {  
        class Program  
        {  
            static void Main( string[] args )  
            {  
                Class2 class2 = new Class2();  
     
                class2.Class1.RaiseSomeEvent();  
     
                Console.ReadLine();  
            }  
        }  

    And when you run it, the output is this...

    Class1.SomeEvent was invoked and handled within Class1  
    Class1.SomeEvent was invoked and handled within Class2 

    So what's actually happening here?

    When you subscribe to the event, you're giving it a delegate to call when the event is raised.  It gets thrown into a MulticastDelegate.

    When the event itself is raised, it loops through the delegates/eventhandlers (in the order in which they were subscribed, although I don't believe the framework makes any guarantees) and invokes the delegate for each of them.

    In this case, there are two delegates handling the event, and they're both called.


    Anyway... thanks for attempting to help me.  Hope this explains some things to help you.


    Jonathan

    • Edited by JMitchem Tuesday, July 08, 2008 3:13 PM clarification
    • Marked as answer by jack 321 Friday, July 11, 2008 2:24 AM
    Tuesday, July 08, 2008 3:11 PM
  • Thank you, Jonathan. You are right, my expertise does not go that far. Last night I looked up StackFrame and saw that it was a class in Microsoft.JScript. I included it. I don't do J# or JScript and that was new to me. It is a side effect of incomplete and misleading MSDN documentation on this part and I mean specifically the Help you get thru "index" option in VS2008. Today I checked MSDN at large and lo and behhold everything was there.. I hate ti blame MS for anything, they do their best but sometimes certain important classes are missing in documentation.

    Nonetheless, I will keep thinking about what I can do since now it compiled:) and the whole thing makes much more sense with your explanation. It is an interesting problem for me too.
    AlexB
    Tuesday, July 08, 2008 4:31 PM
  • I'm going to continue discussion on this over in the BCL forum, since I feel it's more of a .NET question than a C# question.

    http://forums.msdn.microsoft.com/en-US/netfxbcl/thread/9f0375b1-d136-457a-a39c-69292af7895c

    • Marked as answer by jack 321 Friday, July 11, 2008 2:25 AM
    Tuesday, July 08, 2008 5:22 PM
  • AlexBB,

    It sounds like you'll need to run the installer again if you haven't installed the appropriate MSDN documentation.

    Also, if I remember correctly, there's a selector somewhere in the help application itself to pick which MSDN dataset to look through (e.g., Visual C# Help, SQL Server 2005 Books Online, etc. )

    Sounds like one or the other needs to be done.

    (But to be perfectly honest, I use the online MSDN help most of the time, just because a web browser is far more useful to me than the MSDN help application.)


    Jonathan
    Tuesday, July 08, 2008 5:45 PM
  • The tables have turned. Now I have to thank you for "all the help." :)

    You will be much better off in the Common Language Runtime, though.
    AlexB
    Tuesday, July 08, 2008 8:56 PM