2011-01-21 5 views
7

У меня есть следующий класс, который имеет один публичное мероприятие под названием LengthChanged:Как ссылаться на события в C#

class Dimension 
{ 
    public int Length 
    { 
     get 
     { 
      return this.length; 
     } 
     set 
     { 
      if (this.length != value) 
      { 
       this.length = value; 
       this.OnLengthChanged(); 
      } 
    } 

    protected virtual void OnLengthChanged() 
    { 
     var handler = this.LengthChanged; 
     if (handler != null) 
     { 
      handler (this, System.EventArgs.Empty); 
     } 
    } 

    public event System.EventHandler LengthChanged; 

    private int length; 
} 

Я хотел бы иметь возможность регистрировать/UNREGISTER обработчики для этого события в методе, называемом Observer , который ничего не знает о классе Dimension. Я пришел с двумя сценариями, ни один из которых на самом деле удовлетворение:

  1. Определить интерфейс ILengthChanged с LengthChanged случае, убедитесь, что Dimension реализует ILengthChanged. Затем я должен предоставить одну реализацию метода Observer для каждого определяемого интерфейса. Это никоим образом не является достаточно общим. Я бы действительно хотел просто передать ссылку на событие System.EventHandler.

  2. Используйте System.Action<System.EventHandler> обратные вызовы для регистрации и отмены регистрации обработчика событий в методе Observer, просто так:

    класс Foo { общественных недействительного Observer (System.Action регистр, System.Action незарегистрированного) { регистр (this.MyEventHandler);

    // keep track of the unregister callback, so that we can unregister 
        // our event handler later on, if needed... 
    } 
    
    private void MyEventHandler(object sender, System.EventArgs e) 
    { 
        ... 
    } 
    

    }

, который затем будет вызван следующим образом:

Foo foo = ...; 
Dimension dim = ...; 

foo.Observer (x => dim.LengthChanged += x, x => dim.LengthChanged -= x); 

и которые, когда выполняются, действительно в конечном итоге электромонтаж LengthChanged событие с обработчиком внутреннего события MyEventHandler , Но это не очень элегантно. Мне бы хотелось написать это вместо этого:

Foo foo = ...; 
Dimension dim = ...; 

foo.Observer (dim.LengthChanged); 

но я не знаю, как это можно было бы достичь. Может быть, мне не хватает чего-то действительно очевидного здесь? Я предполагаю, что некоторая магия dynamic может каким-то образом сделать трюк, но это не повлияет на проверку типа компиляции: я не хочу, чтобы пользователи Observer передавали ссылки на события, которые не удовлетворяют сигнатуре события System.EventHandler.

ответ

10

К сожалению, на самом деле нет способа сделать это. События не являются гражданами первого класса в .NET в целом - хотя F # пытается их продвигать.

Либо передайте делегат подписки/отмены подписки, либо введите строку с указанием имени события. (Последний часто бывает короче, но, очевидно, менее безопасен во время компиляции.)

Это те подходы, которые принимает Reactive Extensions - если бы был более чистый способ сделать это, я уверен, что они будут использовать это :(

+0

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

2

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

Если вы измените свое событие этой

public System.EventHandler LengthChanged; 

Вы можете просто передать LengthChanged к наблюдателю, как этот

Foo foo = ...; 
Dimension dim = ...; 
foo.Observer (dim.LengthChanged); 
+0

Благодарим вас за предложение. Нет, я не могу передать делегата вместо события; событие может реализовать установки 'add' и' remove' и действительно должно быть определено как полноценное C# 'event'. –

+0

@Pierre Я вижу .. в этом случае я бы хотел объявить класс оболочки делегата. Конструктор этого класса принимает делегат. Класс имеет виртуальный AddHandler() и виртуальный RemoveHandler(). Он также имеет метод Invoke() для вызова делегата. Чтобы сделать его более элегантным, вы можете определить operator + =, - =, + и -, называя ваш AddHandler и RemoveHandler. Замените событие LengthChanged вашего измерения этим классом-оболочкой. Для случая вам нужно «добавить» и «удалить» сеттеры, просто переопределите методы AddHandler класса AddHandler и RemoveHandler. Только мои 2 цента. Я сделал подобное. –

+0

приятное решение ... Мне придется подумать об этом. +1 для этого приятного взлома. –

Смежные вопросы