В C#, почему я не могу передать ссылку EventHandler другого класса и как я могу обойти его?

Если у меня есть ClassA, который имеет общественное мероприятие, SomeEvent и ClassC, который имеет метод, addListener, который принимает ссылку EventHandler, почему ClassB не может иметь строки, которая говорит что c.addListener (касательно a. SomeEvent)? Если я пробую, я получаю ошибку компилятора, которая говорит: "Событие 'ClassA.SomeEvent' может только казаться на левой стороне + = или - = (кроме тех случаев, когда используемым из типа 'ClassA').

Почему это ограничение существует? И как я могу обойти его при пребывании обоснованно близко к моей структуре?

Я - новичок C#; любая справка ценилась бы. Спасибо!

class ClassA {
    public event EventHandler SomeEvent;
}

ClassB{
    public ClassB() {

        ClassA a = new ClassA();
        ClassC c = new ClassC();
        c.addListener(ref a.SomeEvent);  //Compile error
    }
}

class ClassC {
    public void addListener(ref EventHandler handler) {
        handler += onEvent;
    }

    private void onEvent(object sender, EventArgs e) {
        //do stuff
    }

}
10
задан Newtang 3 January 2010 в 09:02
поделиться

3 ответа

Ключевое слово события создает аксессуар для частного объекта делегирования. Точно так же, как и свойство, оно ограничивает доступ к приватному полю. Ваш фрагмент кода не работает с подобной ошибкой, когда вы используете свойство вместо события:

  class ClassA {
    public int Property { get; set; }
  }
  class ClassB {
    public ClassB() {
      ClassA a = new ClassA();
      ClassC c = new ClassC();
      c.setValue(ref a.Property);   // CS0206
    }
  }
  class ClassC {
    public void setValue(ref int value) {
      value = 42;
    }
  }

Теперь легче увидеть, что компилятор не может гарантировать, что метод setValue() использует установщик свойств. Также он не может знать, что аргумент "value" является свойством с сеттером или обычным полем.

Это менее понятно для события, потому что в нем так много синтаксического сахара. Это объявление

public event EventHandler SomeEvent;

на самом деле генерирует такой код:

private EventHandler _SomeEvent;
public event SomeEvent {
  add    { _SomeEvent += new EventHandler(value); }
  remove { _SomeEvent -= new EventHandler(value); }
}

Аксессуары для добавления и удаления эквивалентны аксессуарам для получения и установки свойства, они предотвращают ошибку в коде с приватным полем _SomeEvent. По условию, аксессуар добавления вызывается при использовании +=, remove вызывается при помощи -=. Сравните это с предыдущим примером для свойства. Та же проблема, вы не можете использовать ключевое слово ref, и ClassC.addListener() не будет иметь возможности узнать, что обработчик на самом деле является событием, а не объектом делегирования. Если бы компилятор передал _SomeEvent вместо него, то потерялась бы точка использования доступа.

Для решения этой проблемы можно перестроить код:

  class ClassC {
    public EventHandler getListener() {
      return new EventHandler(onEvent);
    }
    private void onEvent(object sender, EventArgs e) { }
  }
...
      a.SomeEvent += c.getListener();

Последнее замечание: симметрия между событием и свойством немного потеряна, компилятор C# автоматически генерирует аксессуары add/remove accessors, если вы не напишете их явно. Он не делает этого для свойства. Это сделало бы автоматические свойства намного проще:

property int Property;

Но это потребовало бы добавления нового ключевого слова в язык, что-то, что действительно не нравится команде C#. Другие языки, такие как VB.NET и C++/CLI, действительно имеют это ключевое слово.

6
ответ дан 3 December 2019 в 22:37
поделиться

Вне класса вы только имеете доступ к add и remove accessors - то есть point of a event вы не можете ни видеть see other subscribers, ни менять их (например, установка события на ноль). Лучше обрабатывать событие нормально и вызывать необходимые последствия.

Представьте, что вы можете делать то, что предлагаете. Например, предположим, что вы подписываетесь на клик по кнопке, а некоторый код другой использует эту информацию, чтобы подключить вас к "тиковому" событию - ваш код не будет работать так, как он ожидал = ошибка.

Чтобы сделать это explict; событие не является событиемГендлера EventHandler, точно так же, как свойство не является значением int - событие/свойство определяет методы доступа аксессуаров .

В вашем сценарии либо сделайте OnEvent публичным и используйте a. SomeEvent += c.OnEvent;, либо используйте какой-нибудь аналогичный метод и используйте анон-метод:

a.SomeEvent += delegate { c.DoSomethingCool(); };
9
ответ дан 3 December 2019 в 22:37
поделиться

Как я могу обойти его, оставаясь достаточно близко к своей структуре?

Вместо этого используйте a.SomeEvent += обработчик.

Почему это ограничение существует?

Смотрите ответ Марка Грэвелла.

0
ответ дан 3 December 2019 в 22:37
поделиться
Другие вопросы по тегам:

Похожие вопросы: