How to reference an event in C#

2020-02-11 03:51发布

I have the following class, which has one public event called 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;
}

I would like to be able to register/unregister handlers for this event in a method called Observer, which does not know anything about the Dimension class. I have come up with two scenarios, none of which are really satisfying:

  1. Define an interface ILengthChanged with the LengthChanged event, then make sure Dimension implements ILengthChanged. Then I have to provide one implementation of the Observer method for every interface I define. This by no way generic enough. I'd really want to be able to simply pass in a reference to a System.EventHandler event.

  2. Use System.Action<System.EventHandler> callbacks for registering and unregistering the event handler in the Observer method, just like that:

    class Foo { public void Observer(System.Action register, System.Action unregister) { register (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)
    {
        ...
    }
    

    }

which would then be invoked like this:

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

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

and which, when executed, will indeed end up wiring the LengthChanged event with the internal event handler MyEventHandler. But this is not very elegant. I would have loved to be able to write this instead:

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

foo.Observer (dim.LengthChanged);

but I've no idea how this could be achieved. Maybe I am missing something really obvious here? I guess that some dynamic magic could do the trick, somehow, but this would not enforce compile-time type checking: I don't want the users of Observer to pass in references to events which do not satisfy the System.EventHandler event signature.

2条回答
时光不老,我们不散
2楼-- · 2020-02-11 04:25

Unfortunately there isn't really a way of doing this. Events aren't first class citizens in .NET in general - although F# tries to promote them there.

Either pass in the subscribe/unsubscribe delegate or using a string indicating the name of the event. (The latter is often shorter, but obviously less safe at compile-time.)

Those are the approaches which Reactive Extensions takes - if there were a cleaner way of doing it, I'm sure they would be using that :(

查看更多
啃猪蹄的小仙女
3楼-- · 2020-02-11 04:36

Event is not supposed to be passed into another method. However, you can pass delegate into another method. Perhaps, what you are looking for are just a simple public delegate instead of event.

If you change your event to this

public System.EventHandler LengthChanged; 

You can simply pass the LengthChanged to Observer like this

Foo foo = ...;
Dimension dim = ...;
foo.Observer (dim.LengthChanged); 
查看更多
登录 后发表回答