Подтвердить что ты не робот

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

Я уверен, что я просто не понимаю что-то фундаментальное в отношении событий и/или делегатов на С#, но почему я не могу выполнять логические тесты в этом примере кода:

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) { }
}
4b9b3361

Ответ 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

Издатель события неявно перегружает только операции += и -=, а другие операции в издателе не реализованы из-за очевидных причин, как объяснялось выше, например, не хотят давать контроль над подписчиком для изменения события.

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

Если абонент может получить доступ к значку издателя, очень легко определить, является ли конкретное событие подписчиком или нет, проверив значение флага.