2

I need to pass a parameter (in C#) to an event handler and then be able to detach the event handler.

I attach the event handler and pass the parameter:

_map.MouseLeftButtonUp += (sender, e) => _map_MouseLeftButtonUp2(sender, e, showResultsWindow);

The event is called as expected. I try to detach the event handler:

_map.MouseLeftButtonUp -= (sender, e) => _map_MouseLeftButtonUp2(sender, e, showResultsWindow);

The code executes without error, but does not seem to detach.

If I attach the event handler the more conventional way (without passing a parameter):

_map.MouseLeftButtonUp+=_map_MouseLeftButtonUp;

and detach

_map.MouseLeftButtonUp -= _map_MouseLeftButtonUp;

everything works as expected

Detaching the event handler (that takes a parameter) via the more conventional way

_map.MouseLeftButtonUp -= _map_MouseLeftButtonUp2;

gives me an error saying the delegates don't match (which makes sense)

So my question is: Why is the event handler not really being detached when I pass a parameter, and is there a way to circumvent this problem.

TruthOf42
  • 2,017
  • 4
  • 23
  • 38
  • I'm not too sure I understood your final question regarding the `_map_MouseLeftButtonUp2` method, so I'm not sure if my answer is enough. Did you subscribe to `_map_MouseLeftButtonUp2`, and if you really think it's just the method declaration making a difference, post the method declaration. – Will Eddins Aug 01 '13 at 13:34
  • The final question and the last line of code are not really related. I just mentioned that last line of code as I thought it *might* unsubscribe properly. But since _map_MouseLeftButtonUp2 takes the extra parameter it makes sense that it wouldn't work. – TruthOf42 Aug 01 '13 at 13:48

2 Answers2

7

When you create a lambda (anonymous) function, you're actually creating a new function each time.

The reason your first two lines don't work is because they are two completely different functions that just happen to do the same thing. The correct way to detach would be to have subscribe and unsubscribe from a function, as you've already figured out.

An alternative, that's probably not worth it, is to save your lambda to a variable.

Action<object, MouseButtonEventArgs> myEventMethod = (sender, e) => _map_MouseLeftButtonUp2(sender, e, showResultsWindow);
_map.MouseLeftButtonUp += myEventMethod;
// ...
_map.MouseLeftButtonUp -= myEventMethod;
Will Eddins
  • 13,628
  • 5
  • 51
  • 85
  • @TruthOf42 The reason your final unsubscribe doesn't work is because your code has no idea what's going on inside the lambda expression, so it doesn't know `_map_MouseLeftButton2` exists. Using a lambda expression is really no different than using a function, you're just using it to remember another parameter. Depending on your design, you could just move that 'parameter' to a variable local to your class. – Will Eddins Aug 01 '13 at 13:47
  • I think making the parameter a global variable will be what I have to do. I liked the saving the lambda expression to a variable, but apparent var doesn't work for global variables and you can't seem to save a lambda expression to var: http://stackoverflow.com/questions/6897611/lambda-expression-and-var-keyword-in-c-sharp – TruthOf42 Aug 01 '13 at 13:56
  • @TruthOf42 Right, I forgot that. Just change var to `Func` or whatever is appropriate. It just doesn't know what type the parameters are since it doesn't say anywhere. My parameters may not be exactly right off the top of my head, but it should be close enough for you to figure out. – Will Eddins Aug 01 '13 at 13:58
  • So close now... "Func myMethod = (sender, e) => _map_MouseLeftButtonUp2(sender, e, showResultsWindow);" gives me an error on "_map_MouseLeftButtonUp2(sender, e, showResultsWindow)" saying that it "cannot implicitly convert type 'void' to bool?" 'bool?' is the parameter type. Any ideas as to the problem? – TruthOf42 Aug 01 '13 at 14:10
  • @TruthOf42 The last parameter in the `Func` is your return type. Your lambda takes in two variables and returns nothing (void). – Will Eddins Aug 01 '13 at 14:13
  • @TruthOf42 I changed the answer to be the correct type, using Action<>. See http://stackoverflow.com/questions/4244359/using-void-return-types-with-new-funct-tresult for the reason why. I tested this in VS and it works. – Will Eddins Aug 01 '13 at 14:18
  • so apparently you can't assign an Action AS an event handler (http://stackoverflow.com/questions/1551953/actionobject-eventargs-could-not-be-casted-to-eventhandler) the proper way I guess it to do this "MouseButtonEventHandler myEventMethod = (sender, e) => _map_MouseLeftButtonUp2(sender, e, showResultsWindow);" Unfortuantly, it still seems like that event is not getting unsubscribed to – TruthOf42 Aug 01 '13 at 14:55
1

The reason is that two delegates are not equal:

  // You add one delegate instance 
  _map.MouseLeftButtonUp += (sender, e) => _map_MouseLeftButtonUp2(sender, e, showResultsWindow);

  // ..And try to remove another one (not previous!) That's why the first delegate remains unremoved
  _map.MouseLeftButtonUp += (sender, e) => _map_MouseLeftButtonUp2(sender, e, showResultsWindow);

You can convince youself by

  var x = (sender, e) => _map_MouseLeftButtonUp2(sender, e, showResultsWindow);
  var y = (sender, e) => _map_MouseLeftButtonUp2(sender, e, showResultsWindow);

  if (Object.Equals(x, y)) { // <- You expected this behaviour
    ...
  }
  else { // <- Alas, this is a real situation: x != y
    ...
  }

The reason of such a behaviour is that when Object.Equals is not overridden (and in case of delegates it's not) Object.Equals works as Object.RefrenceEquals does, which checks instances referenses (addresses). Sure, that addresses of x and y are different as well as two your delegates

Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215