简体   繁体   中英

Capturing delegates in 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 that the line

action -= printAction;

Actually translates into

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

so the action captured by _captureAction in CaptureActionFromParam() is not changed - only the local 'action' variable in TestSimpleCapturedAction() is affected.

My desired behavior in such a scenario would be printAction not being called. The only solution I can think of is defning a new "delegate container" class as such:

    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 my desired behavior can be achieved without introducing this new layer of abstraction. Implementing the strategy pattern can easily lead to such a situation, so one would reckon the language and/or the BCL would support it natively somehow.

Thanks !

Delegates are like strings. They're implemented as reference types, but they behave more like immutable value types. When you add or subtract characters on a string, it doesn't change the string, it produces a new string that is the new result. When you add or subtract numbers from an integer, it doesn't change the integer, it produces a new integer that is the new result. And when you add or substract a delegate from a delegate, it doesn't change either delegate; it produces a new delegate which is the result.

If what you want to capture is a delegate which can vary then capture a variable that contains a reference to a delegate . Variables vary , that's why they're called "variables". If you want something that can vary, get the variable.

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

Now the delegate that is captured has itself captured the variable "action", not the value that happens to be in it.

Remember:

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

Make sense?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM