none
Why can not use eventX -= delegateY? RRS feed

  • Question

  • Hi,

    I want to remove a RoutedEventHandler from MediaElement.MediaEnded event, the code like the following:
    mediaElement.MediaEnded -= (RoutedEventHandler)mediaElement.MediaEnded; 
    But, I got "'MediaEnded' can only appear on the left hand side of += or -=".

    I used above code to remove customized Event Handler is OK, but why I can not use this tech on MediaEnded event?

    M.Joe
    Wednesday, June 11, 2008 5:49 AM

Answers

  • I've always thought in terms of events NOT representing the delegate (list) for just these kind of reasons. It's an implementation detail that shouldn't be exposed, in my view.

    You don't want code outside the class that owns the event to be able to affect ALL the handlers in the invocation list -- only the ones they know about. And imagine the chaos of allowing any old code to do this:

    obj.Event = null

    or

    obj.Event1 = obj.Event2; 

    or even

    obj.Event 

    to actually raise an event on another class...

    Which would otherwise be legal, but incredibly dangerous and very hard to debug. It's much safer to treat the event as simply a means to attach and detach individual handlers.

    It would be very dangerous to allow user code to clear the invocation list on the MediaEvent, for instance, because there might be other internal PresentationFramework classes listening for that event, and you'd inadvertently break your UI. In short, you might not be the only one listening on a public event.

    I would argue that the same should hold true for all events, internal or external, but the compiler guys would obviously disagree. There's no need to use the +=/-= syntactic sugar to listen for your own events -- since you are the one that's raising them. Best practice would be something like:

    public event EventHandler MyEvent;  
     
    public void DoSomethingThatRaisesEvent() {  
        // ...  
        OnMyEvent();  
        // ...  
    }  
     
    protected virtual void OnMyEvent() {  
        DoMyInternalHandling();  
          
        if (MyEvent != null) {  
            MyEvent(this, new EventArgs());  
        }  

    Your class knows immediately when the event is being raised and has full control of when to take action (either before or after all external listeners get notified). Unrelated classes can listen, and derived classes can more optimally override the virtual method rather than attach a delegate.
    • Edited by wbradney Friday, June 13, 2008 12:37 PM fixed
    • Marked as answer by M.Joe Saturday, June 14, 2008 4:45 AM
    Friday, June 13, 2008 12:33 PM

All replies

  • On the other hander I got the following error message from VS2008:

    Error    1    The event 'System.Windows.Controls.MediaElement.MediaEnded' can only appear on the left hand side of += or -=    D:\wrk\dev\office\Rainbow\trunk\src\MediaPlayer\UIHelper.cs    220


    M.Joe
    Wednesday, June 11, 2008 9:12 AM
  • The right hand side should be a delegate, but what you have is an event.

    You may want to do this:

                mediaElement.MediaEnded += my_MediaEnded;
                mediaElement.MediaEnded -= my_MediaEnded;

    -Jer
    Wednesday, June 11, 2008 10:23 AM
  • Jeremiah Morrill said:

    The right hand side should be a delegate, but what you have is an event.

    You may want to do this:

                mediaElement.MediaEnded += my_MediaEnded;
                mediaElement.MediaEnded -= my_MediaEnded;

    -Jer

    Thank you for your reply.

    I want to remove the invocation list from mediaElement.MediaEnded.

    "eventX -= delegateY" should be compiled and that is legal c# syntax. I can compile the such code in other WPF C# project, so I think this is bug!


    M.Joe
    Thursday, June 12, 2008 1:16 AM
  • Event -= Delegate would indeed be legal, but what you're trying to do is not that, you're trying to do Event -= Event, since MediaEnded is an event.

    You can't clear all delegates in an event's delegate list unless the owning class provides a means to do so, and you'll rarely find that to be the case. The best you can do is remove delegates that you have references to.

    The +=/-= syntax on the event is actually just compiler sugar for invoking add/remove methods on the MediaElement class:

    public event RoutedEventHandler MediaEnded  
    {  
        add  
        {  
            base.AddHandler(MediaEndedEvent, value);  
        }  
        remove  
        {  
            base.RemoveHandler(MediaEndedEvent, value);  
        }  
    }  
     

    These in turn being compiler sugar for:

    public void add_MediaEnded(RoutedEventHandler value)  
    {  
        base.AddHandler(MediaEndedEvent, value);  
    }  
     
    public void remove_MediaEnded(RoutedEventHandler value)  
    {  
        base.RemoveHandler(MediaEndedEvent, value);  
    }  
     
     
    Thursday, June 12, 2008 2:22 AM
  • wbradney said:

    Event -= Delegate would indeed be legal, but what you're trying to do is not that, you're trying to do Event -= Event, since MediaEnded is an event.

    You can't clear all delegates in an event's delegate list unless the owning class provides a means to do so, and you'll rarely find that to be the case. The best you can do is remove delegates that you have references to.

    The +=/-= syntax on the event is actually just compiler sugar for invoking add/remove methods on the MediaElement class:

    public event RoutedEventHandler MediaEnded  
    {  
        add  
        {  
            base.AddHandler(MediaEndedEvent, value);  
        }  
        remove  
        {  
            base.RemoveHandler(MediaEndedEvent, value);  
        }  
    }  
     

    These in turn being compiler sugar for:

    public void add_MediaEnded(RoutedEventHandler value)  
    {  
        base.AddHandler(MediaEndedEvent, value);  
    }  
     
    public void remove_MediaEnded(RoutedEventHandler value)  
    {  
        base.RemoveHandler(MediaEndedEvent, value);  
    }  
     
     

    Thank you for your reply.

    I don't try to do "eventX -= eventY". In deed, I do "eventX -= (delegateTypeX)eventX", and you can see in my first post:
     
    mediaElement.MediaEnded -= (RoutedEventHandler)mediaElement.MediaEnded; 
     


     And I think: the event is just the delegate.

    On the other hand, "eventX -= (delegateX)eventX" can be compiled and run on other VS2008 C# project, except the one WPF project I'm working on.


    M.Joe
    Thursday, June 12, 2008 6:16 AM
  • My guess would be that the compiler isn't even getting as far as the (RoutedEventHandler) cast, but even if it were it would probably complain that the EVENT cannot be converted to a DELEGATE, mainly because the EVENT in this context doesn't actually exist as a typed delegate entity as far as the CLR is concerned.

    As far as I know, it's illegal to refer to an event on the right-hand side of an assignment -- it has no 'value' that can be used. I'd surely like to see the code you have that compiles and runs...
    • Edited by wbradney Thursday, June 12, 2008 10:57 AM clarify
    Thursday, June 12, 2008 10:44 AM
  • wbradney said:

    My guess would be that the compiler isn't even getting as far as the (RoutedEventHandler) cast, but even if it were it would probably complain that the EVENT cannot be converted to a DELEGATE, mainly because the EVENT in this context doesn't actually exist as a typed delegate entity as far as the CLR is concerned.

    As far as I know, it's illegal to refer to an event on the right-hand side of an assignment -- it has no 'value' that can be used. I'd surely like to see the code you have that compiles and runs...


    Hi, wbradney,

    Thank you for your reply first.

    And I think you can compile the following code:
    namespace ConsoleApplication1 
        delegate void DelegateX(); 
     
        class Program 
        { 
            [STAThread] 
            static void Main(string[] args) 
            { 
                Program p = new Program(); 
                p.EventX += p.EventHandler; 
                p.raiseEvent(); 
     
                p.EventX -= (DelegateX)p.EventX; 
                p.raiseEvent(); 
            } 
     
            void raiseEvent() 
            { 
                if (EventX != null
                { 
                    EventX(); 
                } 
            } 
     
            public void EventHandler() 
            { 
                Console.WriteLine("EventX has been handled!\n"); 
            } 
     
            public event DelegateX EventX; 
       } 

    There is a problem that I cannot explain! But it's true that MSFTs are sleeping :D

    M.Joe
    Friday, June 13, 2008 1:03 AM
  • Well, that seems to be subtly different. In this case EventX is YOUR event, and the IL that's emitted will be different. In fact, it (optimally) causes Delegate.Remove to get called directly, (because it's your type and you have access to the delegate list, presumably). This will only work when you're dealing with events owned by the class (which won't be the case 99% of the time). Try the following:

    namespace ConsoleApplication6 {  
        delegate void DelegateX();  
     
        class Foreign {  
            public event DelegateX EventX;  
     
            public void DoSomethingToCauseEvent() {  
                if (EventX != null) {  
                    EventX();  
                }  
            }  
        }  
     
        class Program {  
            [STAThread]  
            static void Main(string[] args) {  
                Program p = new Program();  
                p.EventX += p.EventHandler;  
                p.raiseEvent();  
     
                p.EventX -= (DelegateX) p.EventX;  
                p.raiseEvent();  
     
                Foreign f = new Foreign();  
                f.EventX += p.EventHandler;  
                f.DoSomethingToCauseEvent();  
     
                f.EventX -= (DelegateX) f.EventX;  
                f.DoSomethingToCauseEvent();  
            }  
     
            void raiseEvent() {  
                if (EventX != null) {  
                    EventX();  
                }  
            }  
     
            public void EventHandler() {  
                Console.WriteLine("EventX has been handled!\n");  
            }  
     
            public event DelegateX EventX;  
        }    
     

    and you'll get a slightly different error than your first one with a more revealing error message:

    Error   1   
    The event 'ConsoleApplication6.Foreign.EventX' can only appear on the left hand side of += or 
    -
    (except when used from within the type 'ConsoleApplication6.Foreign')  
    C:\Users\wayne\Documents\Visual Studio 2008\Projects\ConsoleApplication6\ConsoleApplication6\Program.cs 33  39  ConsoleApplication6  
     
    • Edited by wbradney Friday, June 13, 2008 2:09 AM clarify
    Friday, June 13, 2008 2:06 AM
  • wbradney said:

    Well, that seems to be subtly different. In this case EventX is YOUR event, and the IL that's emitted will be different. In fact, it (optimally) causes Delegate.Remove to get called directly, (because it's your type and you have access to the delegate list, presumably). This will only work when you're dealing with events owned by the class (which won't be the case 99% of the time). Try the following:

    namespace ConsoleApplication6 {  
        delegate void DelegateX();  
     
        class Foreign {  
            public event DelegateX EventX;  
     
            public void DoSomethingToCauseEvent() {  
                if (EventX != null) {  
                    EventX();  
                }  
            }  
        }  
     
        class Program {  
            [STAThread]  
            static void Main(string[] args) {  
                Program p = new Program();  
                p.EventX += p.EventHandler;  
                p.raiseEvent();  
     
                p.EventX -= (DelegateX) p.EventX;  
                p.raiseEvent();  
     
                Foreign f = new Foreign();  
                f.EventX += p.EventHandler;  
                f.DoSomethingToCauseEvent();  
     
                f.EventX -= (DelegateX) f.EventX;  
                f.DoSomethingToCauseEvent();  
            }  
     
            void raiseEvent() {  
                if (EventX != null) {  
                    EventX();  
                }  
            }  
     
            public void EventHandler() {  
                Console.WriteLine("EventX has been handled!\n");  
            }  
     
            public event DelegateX EventX;  
        }    
     

    and you'll get a slightly different error than your first one with a more revealing error message:

    Error   1   
    The event 'ConsoleApplication6.Foreign.EventX' can only appear on the left hand side of += or 
    -
    (except when used from within the type 'ConsoleApplication6.Foreign')  
    C:\Users\wayne\Documents\Visual Studio 2008\Projects\ConsoleApplication6\ConsoleApplication6\Program.cs 33  39  ConsoleApplication6  
     

    You are right. I think this problem is the difference between the Event and Delegate, and C# just want to encapsulate the Event implementation details.
    On the other hand, the Event just is Delegate, I can use Delegate to do anything that Event can do, but C# compiler treat Event and Delegate is different.

    Thank you for your help. And I hope you remain digging out the truth, then share it with me :D


    M.Joe
    Friday, June 13, 2008 3:36 AM
  • I've always thought in terms of events NOT representing the delegate (list) for just these kind of reasons. It's an implementation detail that shouldn't be exposed, in my view.

    You don't want code outside the class that owns the event to be able to affect ALL the handlers in the invocation list -- only the ones they know about. And imagine the chaos of allowing any old code to do this:

    obj.Event = null

    or

    obj.Event1 = obj.Event2; 

    or even

    obj.Event 

    to actually raise an event on another class...

    Which would otherwise be legal, but incredibly dangerous and very hard to debug. It's much safer to treat the event as simply a means to attach and detach individual handlers.

    It would be very dangerous to allow user code to clear the invocation list on the MediaEvent, for instance, because there might be other internal PresentationFramework classes listening for that event, and you'd inadvertently break your UI. In short, you might not be the only one listening on a public event.

    I would argue that the same should hold true for all events, internal or external, but the compiler guys would obviously disagree. There's no need to use the +=/-= syntactic sugar to listen for your own events -- since you are the one that's raising them. Best practice would be something like:

    public event EventHandler MyEvent;  
     
    public void DoSomethingThatRaisesEvent() {  
        // ...  
        OnMyEvent();  
        // ...  
    }  
     
    protected virtual void OnMyEvent() {  
        DoMyInternalHandling();  
          
        if (MyEvent != null) {  
            MyEvent(this, new EventArgs());  
        }  

    Your class knows immediately when the event is being raised and has full control of when to take action (either before or after all external listeners get notified). Unrelated classes can listen, and derived classes can more optimally override the virtual method rather than attach a delegate.
    • Edited by wbradney Friday, June 13, 2008 12:37 PM fixed
    • Marked as answer by M.Joe Saturday, June 14, 2008 4:45 AM
    Friday, June 13, 2008 12:33 PM
  • wbradney said:

    I've always thought in terms of events NOT representing the delegate (list) for just these kind of reasons. It's an implementation detail that shouldn't be exposed, in my view.

    You don't want code outside the class that owns the event to be able to affect ALL the handlers in the invocation list -- only the ones they know about. And imagine the chaos of allowing any old code to do this:

    obj.Event = null

    or

    obj.Event1 = obj.Event2; 

    or even

    obj.Event 

    to actually raise an event on another class...

    Which would otherwise be legal, but incredibly dangerous and very hard to debug. It's much safer to treat the event as simply a means to attach and detach individual handlers.

    It would be very dangerous to allow user code to clear the invocation list on the MediaEvent, for instance, because there might be other internal PresentationFramework classes listening for that event, and you'd inadvertently break your UI. In short, you might not be the only one listening on a public event.

    I would argue that the same should hold true for all events, internal or external, but the compiler guys would obviously disagree. There's no need to use the +=/-= syntactic sugar to listen for your own events -- since you are the one that's raising them. Best practice would be something like:

    public event EventHandler MyEvent;  
     
    public void DoSomethingThatRaisesEvent() {  
        // ...  
        OnMyEvent();  
        // ...  
    }  
     
    protected virtual void OnMyEvent() {  
        DoMyInternalHandling();  
          
        if (MyEvent != null) {  
            MyEvent(this, new EventArgs());  
        }  

    Your class knows immediately when the event is being raised and has full control of when to take action (either before or after all external listeners get notified). Unrelated classes can listen, and derived classes can more optimally override the virtual method rather than attach a delegate.


    Thank you for your help.

    It's pretty good.


    M.Joe
    Saturday, June 14, 2008 4:45 AM