Если у меня есть 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
}
}
Ключевое слово события создает аксессуар для частного объекта делегирования. Точно так же, как и свойство, оно ограничивает доступ к приватному полю. Ваш фрагмент кода не работает с подобной ошибкой, когда вы используете свойство вместо события:
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, действительно имеют это ключевое слово.
Вне класса вы только имеете доступ к add
и remove
accessors - то есть point of a event вы не можете ни видеть see other subscribers, ни менять их (например, установка события на ноль). Лучше обрабатывать событие нормально и вызывать необходимые последствия.
Представьте, что вы можете делать то, что предлагаете. Например, предположим, что вы подписываетесь на клик по кнопке, а некоторый код другой использует эту информацию, чтобы подключить вас к "тиковому" событию - ваш код не будет работать так, как он ожидал = ошибка.
Чтобы сделать это explict; событие не является событиемГендлера EventHandler
, точно так же, как свойство не является значением int
- событие/свойство определяет методы доступа аксессуаров .
В вашем сценарии либо сделайте OnEvent
публичным и используйте a. SomeEvent += c.OnEvent;
, либо используйте какой-нибудь аналогичный метод и используйте анон-метод:
a.SomeEvent += delegate { c.DoSomethingCool(); };
Как я могу обойти его, оставаясь достаточно близко к своей структуре?
Вместо этого используйте a.SomeEvent += обработчик
.
Почему это ограничение существует?
Смотрите ответ Марка Грэвелла.