Как ссылка на событие в C#

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

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;
}

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

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

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

    класс Foo { public void Observer (System.Регистр действий, Система.Отменить действие ) { реестр (этот.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 magic может каким-то образом сделать трюк, но это не будет принудительно проверять тип времени компиляции: я не хочу пользователей Observer передать ссылки на события, которые не удовлетворяют System.EventHandler подпись событие.

2 ответов


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

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

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


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

Если вы измените свое событие для этого

public System.EventHandler LengthChanged; 

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

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