В С#, почему я не могу проверить, является ли обработчик событий нулевым вне класса, который он определил?
Я уверен, что я просто не понимаю что-то фундаментальное в отношении событий и/или делегатов на С#, но почему я не могу выполнять логические тесты в этом примере кода:
public class UseSomeEventBase {
public delegate void SomeEventHandler(object sender, EventArgs e);
public event SomeEventHandler SomeEvent;
protected void OnSomeEvent(EventArgs e) {
// CANONICAL WAY TO TEST EVENT. OF COURSE, THIS WORKS.
if (SomeEvent != null) SomeEvent(this, e);
}
}
public class UseSomeEvent : UseSomeEventBase {
public bool IsSomeEventHandlerNull() {
// "LEFT HAND SIDE" COMPILER ERROR
return SomeEvent == null;
}
}
class Program {
static void Main(string[] args) {
var useSomeEvent = new UseSomeEvent();
useSomeEvent.SomeEvent +=new UseSomeEventBase.SomeEventHandler(FuncToHandle);
// "LEFT HAND SIDE" COMPILER ERROR
if (useSomeEvent.SomeEvent == null) {
}
var useSomeEventBase = new UseSomeEventBase();
useSomeEventBase.SomeEvent += new UseSomeEventBase.SomeEventHandler(FuncToHandle);
// "LEFT HAND SIDE" COMPILER ERROR
if (useSomeEventBase.SomeEvent == null) {
}
}
static void FuncToHandle(object sender, EventArgs e) { }
}
Ответы
Ответ 1
Событие - это просто операция "добавить" и операцию "удалить". Вы не можете получить значение, вы не можете установить значение, вы не можете его вызывать - вы можете просто подписаться на обработчик события (add
) или отказаться от подписки (remove
). Это прекрасно - это инкапсуляция, простая и простая. Издатель должен реализовать добавление/удаление соответствующим образом, но если издатель не захочет предоставить информацию, подписчики не могут изменять или получать доступ к частям, специфичным для реализации.
Полевые события в С# (где вы не указываете бит добавления/удаления) скрыть это - они создают переменную типа делегата и события. В реализациях добавления/удаления события просто используется переменная, чтобы отслеживать подписчиков.
Внутри класса вы ссылаетесь на переменную (так что вы можете получить подписчиков, подписанных в настоящий момент, выполнить их и т.д.), а вне класса вы ссылаетесь на само событие (так что у вас есть только возможности добавления/удаления).
Альтернативой полевым событиям является то, где вы явно реализуете добавление/удаление себя, например.
private EventHandler clickHandler; // Normal private field
public event EventHandler Click
{
add
{
Console.WriteLine("New subscriber");
clickHandler += value;
}
remove
{
Console.WriteLine("Lost a subscriber");
clickHandler -= value;
}
}
Для получения дополнительной информации см. мою статью о событиях.
Конечно, издатель событий также может сделать больше информации - вы можете написать свойство типа ClickHandlers
, чтобы вернуть текущий делегат с несколькими листами, или HasClickHandlers
, чтобы вернуть, есть ли они или нет. Это не часть основной модели событий.
Ответ 2
Вы можете легко использовать очень простой подход здесь, чтобы неоднократно подписываться на событие.
Можно использовать любой из двух подходов ниже:
-
Подход флагов: _getWarehouseForVendorCompletedSubscribed - это приватная переменная, инициализированная false.
if (!_getWarehouseForVendorCompletedSubscribed)
{
_serviceClient.GetWarehouseForVendorCompleted += new EventHandler<GetWarehouseForVendorCompletedEventArgs>(_serviceClient_GetWarehouseForVendorCompleted);
_getWarehouseForVendorCompletedSubscribed = true;
}
-
Подменю "Отменить подписку" . Включите отмену подписки каждый раз, когда вы хотите подписаться.
_serviceClient.GetWarehouseForVendorCompleted -= new
EventHandler<GetWarehouseForVendorCompletedEventArgs>
(_serviceClient_GetWarehouseForVendorCompleted);
_serviceClient.GetWarehouseForVendorCompleted += new
EventHandler<GetWarehouseForVendorCompletedEventArgs>
(_serviceClient_GetWarehouseForVendorCompleted);
Ответ 3
Вот ответ:
using System;
delegate void MyEventHandler();
class MyEvent
{
string s;
public event MyEventHandler SomeEvent;
// This is called to raise the event.
public void OnSomeEvent()
{
if (SomeEvent != null)
{
SomeEvent();
}
}
public string IsNull
{
get
{
if (SomeEvent != null)
return s = "The EventHandlerList is not NULL";
else return s = "The EventHandlerList is NULL"; ;
}
}
}
class EventDemo
{
// An event handler.
static void Handler()
{
Console.WriteLine("Event occurred");
}
static void Main()
{
MyEvent evt = new MyEvent();
// Add Handler() to the event list.
evt.SomeEvent += Handler;
// Raise the event.
//evt.OnSomeEvent();
evt.SomeEvent -= Handler;
Console.WriteLine(evt.IsNull);
Console.ReadKey();
}
}
Ответ 4
Здесь немного другой вопрос
Какое значение имеет значение при тестировании внешнего события для null?
В качестве внешнего потребителя события вы можете выполнять только 2 операции
- Добавить обработчик
- Удалить обработчик
Нулевая или ненулевая ошибка события не влияет на эти 2 действия. Почему вы хотите запустить тест, который не дает ощутимого значения?
Ответ 5
Это правило используется при использовании ключевого слова 'event'. Когда вы создаете событие, вы ограничиваете взаимодействие внешнего класса с делегатом с отношением "подписка/отмена подписки", это включает случаи наследования. Помните, что событие по сути является свойством, но для вызовов методов он не является действительно объектом, поэтому он выглядит более похоже:
public event SomeEventHandler SomeEvent
{
add
{
//Add method call to delegate
}
remove
{
//Remove method call to delegate
}
}
Ответ 6
Вам нужно будет сделать это из базового класса. Это точная причина, по которой вы это сделали:
protected void OnSomeEvent(EventArgs e) {
// CANONICAL WAY TO TEST EVENT. OF COURSE, THIS WORKS.
if (SomeEvent != null) SomeEvent(this, e);
}
Вы не можете получать доступ к событиям из производного класса. Кроме того, вы должны сделать этот метод виртуальным, чтобы его можно было переопределить в производном классе.
Ответ 7
Издатель события неявно перегружает только операции +=
и -=
, а другие операции в издателе не реализованы из-за очевидных причин, как объяснялось выше, например, не хотят давать контроль над подписчиком для изменения события.
Если мы хотим проверить, подписано ли какое-либо конкретное событие в классе подписчика, лучший издатель установит флаг в своем классе, когда событие является подписчиком, и очистите флаг, когда он отменит подписку.
Если абонент может получить доступ к значку издателя, очень легко определить, является ли конкретное событие подписчиком или нет, проверив значение флага.