В C # вы можете установить ограничения на события? - PullRequest
7 голосов
/ 25 октября 2011

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

т.е. Я хочу, чтобы объект «Сотрудник», реализующий IPerson, подписывался на обработчик событий, но другой объект «Грузовик», реализующий совершенно другой интерфейс, должен быть ограничен. Из коробки любой объект, реализующий правильный шаблон метода, может подписаться.

Как мне ограничить это?

Причина, по которой я спрашиваю, заключается в том, что я внедряю шаблон Observer, но пытаюсь сделать это с C # Events. У меня есть пример, работающий аналогично , что MSDN имеет здесь .

Хотя я по-прежнему обеспокоен тем, что ЛЮБОЙ объект с ЛЮБОЙ структурой (при условии, что он содержит надлежащий метод делегата) может выполняться при возникновении события. Таким образом, в моем примере выше Employee мог бы реализовать метод, который имеет смысл для объектов в моем примере, но затем любой мог бы создать другой класс - "грузовик" в моем примере выше, с любой структурой (при условии, что он снова реализует метод ) и увязать в предметном событии ... Это определенно больше беспокоит использование объекта и хороший дизайн, и, может быть, я здесь придирчив, но меня это беспокоит.

Ответы [ 3 ]

8 голосов
/ 25 октября 2011

Это действительно плохая идея. Пожалуйста, не делайте этого.

От чего ты действительно пытаешься защищаться? Интерфейс может реализовать любой «враждебный» тип (и если они не могут видеть интерфейс, потому что он является внутренним для вашего проекта, то нет никаких оснований не делать событие тоже внутренним), поэтому ваш черный список не так уж и надежно реализован. Что еще хуже, это усложняет работу типов в белом списке - они не смогут легко делегировать слушателям экземпляры «близких», связанных типов. Даже использование замыкания (с помощью лямбда / анонимного метода) может внезапно нарушить подписки - «представляющий» объект подписки может стать экземпляром (занесенного в черный список) класса, сгенерированного компилятором.

Это плохой механизм безопасности, и он не обеспечивает безопасности во время компиляции. Это не помогает удерживать плохих парней и делает жизнь хороших парней трудной.

В любом случае, это возможно - вам нужно использовать пользовательскую реализацию для события и проверять каждый запрос на подписку.

//Please don't do this.

private EventHandler myEventField = delegate { };

// Add synchronization if required.
public event EventHandler MyEvent
{
   add
   {
      if(value.Target is IPerson)
        myEventField += value;

      else throw new ArgumentException("Subscriber must implement IPerson", "value");
   }

   remove { myEventField -= value; }
}


private void RaiseMyEvent() { myEventField(this, EventArgs.Empty); }
7 голосов
/ 25 октября 2011

В C # вы можете установить ограничения на события?

Нет.

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

Слушатели событий - это экземпляры делегатов типа, совместимого с типом события . «Типы» не являются слушателями событий.

т.е. Я хочу, чтобы объект «Сотрудник», реализующий IPerson, подписывался на обработчик событий, но другой объект «Грузовик», реализующий совершенно другой интерфейс, должен быть ограничен.

Объект "сотрудник" не слушает событие в первую очередь. Единственное, что слушает событие, это объект делегата .

Вы хотите сказать, что хотите принимать только делегаты, являющиеся делегатами, для методов, которые оказываются методами экземпляра определенного класса? Это очень странная вещь, и Я рекомендую вам не пытаться делать это .

Невозможно предотвратить это во время компиляции , но если вы действительно одержимы выполнением этой странной вещи, вы можете сделать это во время выполнения.

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

Но опять же, это звучит как необычайно плохая идея. Слушатель должен решить, заинтересован ли он в событии или нет, а не источник события, чтобы проверить его слушателей. У пользователей есть разумное ожидание, что любое доступное событие может прослушать любой делегат совместимого типа.

Возможно, вы можете объяснить, почему вы хотите сделать эту странную вещь; возможно, есть лучший способ достичь своей цели.

1 голос
/ 25 октября 2011

Я не могу себе представить, зачем вам это нужно, на первый взгляд мне кажется немного грязным, но в любом случае вы можете инкапсулировать эту логику ограничения в самом держателе событий и выставить Subscribe(delegate handler) для подписки на событие, а не для показа самого публичного события

class EventHolder
{
  private event EventHandler<NewUpdateEventArgs> NewUpdate;

  public void SubscribeForNewUpdates(object subscriptionOwner, 
                                     Action<NewUpdateEventArgs> callback) 
  {
     if (subscriptionOwner.GetType() == ... or subscriptionOwner is ...)
     {
        this.NewUpdate += .. subscribe callback
     }
  }
 }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...