在匿名方法中捕获委托

发布于 2024-08-22 11:32:48 字数 1598 浏览 6 评论 0原文

考虑

    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();
    }

printAction 将被 _captureAction 调用的原因是,该行

action -= printAction;

实际转换为

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

,因此 CaptureActionFromParam() 中 _captureAction 捕获的操作不会更改 - 只有 TestSimpleCapturedAction() 中的本地“action”变量受到影响。

在这种情况下我期望的行为是不调用 printAction 。我能想到的唯一解决方案是定义一个新的“委托容器”类:

    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();
    }

这可行,但我想知道是否可以在不引入这个新的抽象层的情况下实现我想要的行为。实现策略模式很容易导致这种情况,因此人们会认为该语言和/或 BCL 会以某种方式原生支持它。

谢谢 !

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 !

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

情定在深秋 2024-08-29 11:32:48

代表就像绳子。它们被实现为引用类型,但它们的行为更像不可变值类型。当您在字符串上添加或减去字符时,它不会更改字符串,而是会生成一个新字符串,即新结果。当您对整数进行加法或减法时,它不会改变该整数,而是会产生一个新的整数,即新的结果。当您添加委托或从委托中减去委托时,不会更改任何一个委托;它产生一个新的委托,这就是结果。

如果您要捕获的是一个可以变化的委托,那么捕获一个包含对委托的引用的变量。变量变化,这就是它们被称为“变量”的原因。如果你想要一些可以变化的东西,那就获取变量。

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

现在,捕获的委托本身捕获了变量“操作”,而不是恰好位于其中的

请记住:

  • 参数按值传递。
  • Lambda 捕获变量,而不是

有道理吗?

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?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文