Why can't I invoke PropertyChanged event from an Extension Method?
Asked Answered
J

4

18

I've tried to code a class to avoid a method like "RaisePropertyChanged". I know that I can inherit from a class that has that implementation but in some cases I can't. I've tried with a Extension Method but Visual Studio complain.

public static class Extension
{
    public static void RaisePropertyChanged(this INotifyPropertyChanged predicate, string propertyName)
    {
        if (predicate.PropertyChanged != null)
        {
            predicate.PropertyChanged(propertyName, new PropertyChangedEventArgs(propertyName));
        }
    }
}

It said:

"The event 'System.ComponentModel.INotifyPropertyChanged.PropertyChanged' can only appear on the left hand side of += or -="

Judaism answered 7/2, 2011 at 18:43 Comment(1)
Thank you for asking this question. I was trying to do the same thing (mixin for INotifyPropertyChanged) and ran into the same issue.Disbelief
W
20

Reed is right. However, I see what you're trying to do (make your code reusable—good for you); and I'll just point out that this is often easily rectified by accepting the PropertyChangedEventHandler delegate itself and passing it from within the INotifyPropertyChanged implementation:

public static void Raise(this PropertyChangedEventHandler handler, object sender, string propertyName)
{
    if (handler != null)
    {
        handler(sender, new PropertyChangedEventArgs(propertyName));
    }
}

Then from within your class which implements INotifyPropertyChanged, you can call this extension method like so:

PropertyChanged.Raise(this, "MyProperty");

This works because, as Marc said, within the class declaring the event you can access it like a field (which means you can pass it as a delegate argument to a method, including extension methods).

Willpower answered 7/2, 2011 at 18:48 Comment(2)
+1. You can go even more generic and just take a EventHandler<T>. See the Raise<T> method in this class for an example: thehelpertrinity.codeplex.com/SourceControl/changeset/view/…Toscano
@Kent: I completely agree; the only annoying thing is that with a lot of BCL types you actually don't have generic EventHandler<T>-based events but rather these weird strongly typed delegates (e.g., PropertyChangedEventHandler), so you have to either accept some conversion parameter to do the trivial lambda conversion from PropertyChangedEventHandler to EventHandler<PropertyChangedEventArgs> or, more realistically, you just implement what you actually need ;)Willpower
C
11

You can only raise an event from within the class in which it's defined.

This line:

predicate.PropertyChanged(propertyName, new PropertyChangedEventArgs(propertyName));

Will fail, since predicate is not the class which defines this extension method.

This is partly why this is typically handled via a base class instead of via using Extension methods.

Cowpuncher answered 7/2, 2011 at 18:45 Comment(0)
D
4

Events are really just an API with add/remove (technically there is an optional "invoke" available in the CLI, but the c# compiler doesn't provide it).

What you have there is a field-like event; field-like events act as the add/remove API from outside the declaring type, and act like the field only inside the declaring type, and only when it is necessary to treat as a delegate - most commonly: invoking the subscribers (this is a subtle change here in c# 4; before c# 4 all access from inside the type acts against the field, including +=/-=).

An extension method by definition cannot be inside the declaring type - only top-level (non-nested) static classes can provide extension methods; so no extension method can ever have the direct ability to invoke a field-like event.

Deeprooted answered 7/2, 2011 at 18:49 Comment(0)
D
2

When you define an event with the event keyword, the C# compiler actually generates a number of things for you behind the scenes.

event EventHandler MyEvent;

Translates to something like (though not exactly as it actually adds some locking and other checks)...

private EventHandler _myEvent;

public EventHandler MyEvent
{
  add( EventHandler handler )
  { 
    _myEvent += handler;
  }
  remove( EventHandler handler )
  {
    _myEvent -= handler;
  }
}

As you can see the actual EventHandler _myEvent is private and cannot be invoked directly.

Divider answered 7/2, 2011 at 18:49 Comment(1)
Pedant point: field-like events (the above) are only generated if no add/remove implementation is provided.Deeprooted

© 2022 - 2024 — McMap. All rights reserved.