开发者

Capture Variable Into An EventHandler

I might be overthinking this one a little but I could use some help in identifying a way/the best way to do the following.

I have an event handler that is attached to an object that is a property of another class. In my event handler I need additional meta-data about the object that caused the event (i.e. the ID of the object it is contained in). From the sender and the event information there is no way to obtain the information I need. My inclination was that this would be a good place to use a captured variable but I'm unsure of my implementation ideas.

So to illustrate in code I have an event handler:

void MyEventHandler(object sender, EventArgs e){
    //Do Stuff here
}

(As a note I'm using base EventArgs here but in my actual implementation the are a specialized subclass and the event is declared using the generic EventHandler)

I am currently attaching it like this:

topObject.SubObject.EventToHandle += MyEventHandler;

I later detatch it like so:

topObject.SubObject.EventToHandle -= MyEventHandler;

I want topObject's ID when I'm handling the event so I was going to change the MyEventHandler to have the following signature:

void MyEventHandler(int id, object sender, EventArgs e)

and attach the event handler like this:

topObject.SubObject.EventToHandle += (s,e) => MyEventHandler(topObject.ID, s,e);

My concern with that is two fold.

  1. Is there a problem with the scope where the handler will actually disapear without removal once I'm outside the function where this is attached. I've seen some wierd errors in the past where event handler disapeared on me when I used lambda exp开发者_StackOverflowression. Its not all the time, just in some cases. Can anyone enlighten me on what those cases might be so I know when its safe to use the syntax I had.
  2. I can't remember exactly but I don't think I can ever have the event handler removed if I use this syntax because the implicit object that is created is not the same.

Because of those two concerns my thought was to create an Action and save the action and use it until I needed to remove the event handler. I did the following:

Action<object, EventArgs> handler = (s,e) => MyEventHandler(topObject.ID, s,e);
topObject.SubObject.EventToHandle += handler;

I get that the action cannot be casted to an event handler. Is there some easy way that I can make this transformation that will still ensure that I can detach the event handler? Am i just over thinking this/ is there a way I'm not seeing right now to do this?


You can create all nice wrapper functions that wrap existing event-handlers and supply them with object ids, but you will still have to store the resulting delegate explicitly to unsubscribe the event.

The only nice way I see to do it without ugly wrappers is using reactive extensions. It essentially allows you to transform your event to an IObservable, and then you can apply any operator to the resulting IObservable (for example Select will do the job in your case). But it's still no that elegant.


An event handler signature in the class raising the event should be:

protected void OnMyEvent(object sender, EventArgs  e)
{
    ....
}

or

protected void OnMyEvent(object sender, MyEventArgs  e)
{
    ....
}

In this case, the caller would do this kind of code:

topObject.SubObject.MyEvent -= OnSubObjectMyEvent;

and implement OnSubObjectMyEvent like this (example):

private void OnSubObjectMyEvent(object sender, MyEventArgs e)
{
   int topObjectId = ((SubObjectType)sender).TopObject.Id;
   ...
}

Here I suppose SubObject has a TopObject property that allows me to get the top object's id.

That's the way most .NET Framework classes work. Is there anything wrong with this approach?


If the event handler needs the top object's Id, I would guess it would not break some abstraction layer in your design to make them aware of eachother.

In other words, your event handler can look like this:

void Handler(object sender, EventArgs e)
{
     var s = (SubObject) sender;
     int id = s.TopObject.ID;

     // do something with id...
}

I would keep event signatures in the convention of sender and args.


Don't change the signature of the event. While the CLR allows, technically, for any signature for an event, there are reasons why the entire Framework was designed with events having the signature (object sender, EventArgs args). In fact, there is an FxCop rule for events that violate this signature, CA1009: Declare event handlers correctly:

Event handler methods take two parameters. The first is of type System.Object and is named 'sender'. This is the object that raised the event. The second parameter is of type System.EventArgs and is named 'e'.

There are several solutions (alternatives):

  • pass the topObject.ID as a member of your custom EventArgs.
  • create a wrapper object that encapsulates the topObject.ID and hook the event handler to a method of this object.
  • use a closure scope that can retain the reference to topObject.ID in scope (which is identical with the method above, but the heavylifting is done by the compiler)
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜