Capturing delegates by anonymous methods

Consider

Action _captureAction; private void TestSimpleCapturedAction() { Action action = new Action(delegate { }); Action printAction = () => Console.WriteLine("Printing..."); action += printAction; CaptureActionFromParam(action); action -= printAction; _captureAction(); //printAction will be called! } private void CaptureActionFromParam(Action action) { _captureAction = () => action(); } 

The reason printAction will be called by _captureAction is because the line

 action -= printAction; 

Actually translated into

 action = (Action) Delegate.Remove(action, printAction); 

therefore, the action taken by _captureAction in CaptureActionFromParam () does not change - only the local variable action in TestSimpleCapturedAction () is affected.

My desired behavior in such a scenario would not be to call printAction. The only solution I can think of is to define a new delegate container class:

  class ActionContainer { public Action Action = new Action(delegate { }); } private void TestCapturedActionContainer() { var actionContainer = new ActionContainer(); Action printAction = () => Console.WriteLine("Printing..."); actionContainer.Action += printAction; CaptureInvoker(actionContainer); actionContainer.Action -= printAction; _captureAction(); } private void CaptureInvoker(ActionContainer actionContainer) { _captureAction = () => actionContainer.Action(); } 

This works, but I wonder if the desired behavior can be achieved without introducing this new layer of abstraction. The introduction of a strategy template can easily lead to this situation, so the language could be considered and / or BCL would somehow support it.

Thanks!

+4
source share
1 answer

Delegates are like strings. They are implemented as reference types, but they behave more like immutable value types. When you add or subtract characters in a string, it does not change the string; it creates a new string, which is a new result. When you add or subtract numbers from an integer, it does not change the integer, it creates a new integer, which is a new result. And when you add or subtract a delegate from a delegate, it does not change any delegate; he creates a new delegate, which is the result.

If what you want to capture is a delegate that can change, then commit a variable containing a reference to the delegate. Variables change, so they are called "variables." If you want something that can change, get a variable.

  CaptureActionFromParam(()=>{action();}); 

Now the captured delegate himself captured the variable "action", and not the value that is in it.

Remember:

  • Parameters are passed by value .
  • Lambdas captures variables , not values .

Make sense?

+7
source

Source: https://habr.com/ru/post/1301919/


All Articles