Почему я не могу вызвать событие PropertyChanged из метода расширения?

Я попытался закодировать класс, чтобы избежать метода типа "RaisePropertyChanged". Я знаю, что я могу наследовать класс, который имеет эту реализацию, но в некоторых случаях я не могу. Я попытался с помощью метода расширения, но Visual Studio жалуется.

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

Он сказал:

"Событие 'System.ComponentModel.INotifyPropertyChanged.PropertyChanged' может появляются только в левой части + = или - ="

Ответ 1

Рид прав. Тем не менее, я вижу, что вы пытаетесь сделать (сделайте свой код повторно используемым полезным для вас); и я просто укажу, что это часто легко исправить, приняв сам делегат PropertyChangedEventHandler и передав его из реализации INotifyPropertyChanged:

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

Затем изнутри вашего класса, который реализует INotifyPropertyChanged, вы можете вызвать этот метод расширения следующим образом:

PropertyChanged.Raise(this, "MyProperty");

Это работает, потому что как сказал Марк, в классе, объявляющем событие, вы можете получить к нему доступ, как поле (это означает, что вы можете передать его как аргумент делегата метод, включая методы расширения).

Ответ 2

Вы можете только поднять событие из класса, в котором он определен.

Эта строка:

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

Ошибка, так как predicate не является классом, который определяет этот метод расширения.

Отчасти это объясняется тем, что это обычно обрабатывается через базовый класс вместо использования методов расширения.

Ответ 3

События - это просто API с добавлением/удалением (технически в CLI есть необязательный "вызов", но компилятор С# не предоставляет его).

У вас есть полевое событие; полевые события действуют как API добавления/удаления извне объявляющего типа и действуют как поле только внутри типа объявления, и только тогда, когда это необходимо рассматривать как делегат - чаще всего: вызывая подписчиков (это незначительное изменение здесь в С# 4, а до С# 4 весь доступ изнутри действует против поля, включая + =/- =).

Метод расширения по определению не может быть внутри класса объявления - только статические классы верхнего уровня (не вложенные) могут предоставлять методы расширения; поэтому метод расширения никогда не может иметь прямую возможность вызывать полевое событие.

Ответ 4

Когда вы определяете событие с ключевым словом event, компилятор С# фактически генерирует для вас несколько вещей за кулисами.

event EventHandler MyEvent;

Переводит на что-то вроде (хотя не совсем так, как на самом деле добавляет некоторые блокировки и другие проверки)...

private EventHandler _myEvent;

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

Как вы можете видеть, фактический EventHandler _myEvent является закрытым и не может быть вызван напрямую.