Почему мы не можем поднять событие с аксессорами? - PullRequest
0 голосов
/ 29 октября 2018

Почему мы не можем вызвать событие с пользовательской реализацией, а это возможно без них? Смотрите этот код:

public class Program
{
    private EventHandler myEvent;
    public event EventHandler MyEvent
    {
        add { myEvent += value; }
        remove { myEvent -= value; }
    }

    public event EventHandler AnotherEvent;

    public static void Main()
    {
        var target = new Program();
        target.MyEvent(null, null);       // ERROR CS0079
        target.AnotherEvent(null, null);  // compiles
    }
}

Вы видите, что оба события объявлены в моем классе. В то время как target.AnotherEvent(...) компилируется просто отлично, target.MyEvent(...) не делает:

Событие MyEvent может появляться только слева от + = или - =.

Я знаю, что событие - это просто делегат с методом добавления и удаления. Таким образом, AnotherEvent переводится компилятором в метод add и remove:

private EventHandler _AnotherEvent;
public event EventHandler AnotherEvent
{ 
    add { _AnotherEvent += value; }
    remove { _AnotherEvent -= value; }
}

Итак, я предполагаю, что вызов AnotherEvent заменяется компилятором на вызов частному делегату, который был _AnotherEvent(...).

Я правильно понял? Есть ли какие-либо документы о том, почему второй вызов работает, а первый - нет? Или хотя бы какое-нибудь описание того, что здесь делает компилятор?

Ответы [ 4 ]

0 голосов
/ 29 октября 2018

Рекомендуется заблокировать событие перед добавлением или удалением нового метода обработчика событий.

говоря это, взгляните на этот кусок кода:

public event EventHandler MyEvent
{
    add
    {
        lock (objectLock)
        {
            myEvent += value;
        }
    }
    remove
    {
        lock (objectLock)
        {
            myEvent -= value;
        }
    }
}

Причина, по которой public event EventHandler AnotherEvent; работает, заключается в том, что Когда в вашем коде не предусмотрены специальные средства доступа к событиям, компилятор добавит их автоматически.

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

Относительно реализации:

 private EventHandler myEvent;
 public event EventHandler MyEvent
    {
        add
        {
            lock (objectLock)
            {
                myEvent += value;
            }
        }
        remove
        {
            lock (objectLock)
            {
                myEvent -= value;
            }
        }
    }

    public event EventHandler AnotherEvent;

    public static void Main()
    {
        var target = new Program();
        var myEvent =  target.MyEvent;
        myEvent?.Invoke(EventArgs.Empty, EventArgs.Empty);      
        target.AnotherEvent(null, null); 
    }

Изменить, чтобы объяснить реализацию:

var myEvent =  target.MyEvent;

При явном событии вы должны предоставить свое собственное хранилище резервных копий - либо поле делегата, либо что-то вроде EventHandlerList, поэтому мы просто перейдем к var здесь.

0 голосов
/ 29 октября 2018

Это синтаксический сахар. То, что вы можете назвать AnotherEvent, как поле поддержки, - это удобство, предоставляемое компилятором (AnotherEvent - это так называемое полеподобное событие ). Когда вы добавляете свои собственные методы доступа, объявление события перестает быть похожим на поле событием и должно вызываться через его вспомогательное поле.

См. Соответствующую часть Спецификации языка C # :

Полевые события

В тексте программы класса или структуры, которая содержит объявление события, некоторые события могут использоваться как поля. Быть при таком использовании событие не должно быть абстрактным или внешним, и должно явно не включать event_accessor_declarations. Такое событие может использоваться в любом контексте, который разрешает поле. Поле содержит делегат (Delegates), который ссылается на список обработчиков событий, которые были добавлены к событию. Если обработчики событий не были добавлены, поле содержит ноль.

(акцент мой)

0 голосов
/ 29 октября 2018

Когда используется автоматическое событие public event EventHandler AnotherEvent;. Компилятор создаст поле (и некоторые методы) для него, и вызов будет выполнен для этого поля. Так что public event больше не существует. Это синтаксический сахар.

Таким образом, вызов неавтоматического события невозможен. Потому что он не найден в скомпилированном коде. Его заменяют методы add_, remove_. Вы можете вызвать только личное поле (которое генерируется)

Это объясняет, почему вы не можете вызвать событие вне экземпляра класса.

0 голосов
/ 29 октября 2018

Это не работает, потому что теперь есть просто способ получить фактический вызываемый обработчик события. Как вы заметили, есть только add и remove, а не get.

Сгенерированный код для обработчика событий:

.event [mscorlib]System.EventHandler MyEvent
{
  .addon instance void ConsoleApp1.Program::add_MyEvent(class [mscorlib]System.EventHandler)
  .removeon instance void ConsoleApp1.Program::remove_MyEvent(class [mscorlib]System.EventHandler)
} // end of event Program::MyEvent

Добавляет две ссылки на метод, одну для add и одну для remove. Если вы посмотрите на это, как он узнает, какой метод вызывать? Что если add и remove намного сложнее, чем сейчас? Просто невозможно точно узнать, какой обработчик события вызывать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...