传递给操作的匿名方法是按值还是按引用获取数据?

Do anonymous methods passed to actions obtain data by value or reference?

我正在创建一个匿名方法并将其传递给稍后调用的操作。我想将一些数字数据 (int) 传递到我的匿名方法中。为了按值传递数据,我是否需要创建副本?或者,数据会按值传递吗?

如果我必须创建副本,这就是我认为实现的样子:

private void CreateAction()
{
    int bus = 4;
    CustomObject[] data = new object[16];
    int length = 1500;

    this.doWorkLater = new Action(() =>
    {
        var busCopy = bus;
        var dataCopy = data;
        var lengthCopy = length;

        this.WorkMethod(busCopy, dataCopy, lengthCopy);
    });
}

为了按值获得 lengthbus,这个(上面的代码)是否必要?

在这种情况下,CustomObject[] data我创建的一些class)是按引用传递还是按值传递?

闭包通过引用捕获可观察到的值* - 请注意,您拥有的代码不能解决一般情况下的问题,如果 CreateAction 的全部要点是创建一个单一的行动它会起作用。

private void CreateAction()
{
    int bus = 4;

    this.doWorkLater = new Action(() =>
    {
        var busCopy = bus;

        this.WorkMethod(busCopy);
    });
    // if you change local `bus` before call to `doWorkLater` it will not work:
    bus = 42;
    doWorkLater(); // busCopy is 42. 
}

* 它实际上收集编译器中创建的所有变量 class 并使用对它的引用来访问方法和闭包中的变量。因此,即使是值类型看起来也像通过引用传递一样。

您传递的不是按值复制。

如果您不打算在执行操作之前修改值,则无需担心传递值的方式。但不,它们不是按价值传递的。即使您 return 操作和 Invoke 它来自另一种方法。这些值保存在编译器生成的 class 中。不用担心

如果您希望数据在执行操作之前发生变化,那么您就错了。您正在使用的方法(将值类型复制到局部变量)应该在操作之外而不是在操作内部完成。至于引用类型(数组),即使你将它复制到一个局部变量,它复制的引用所以复制局部变量的任何变化都会被反映出来。

private void CreateAction()
{
    int bus = 4;
    CustomObject[] data = new object[16];
    int length = 1500;

    var busCopy = bus; // a copy of bus
    var dataCopy = data; // reference copy
    var lengthCopy = length; // a copy of length

    this.doWorkLater = new Action(() =>
    {
        this.WorkMethod(busCopy, dataCopy, lengthCopy);
    });

    bus = 10; // No effect on the action
    length = 1700; // No effect on the action

    this.doWorkLater();
}

这看起来毫无意义,但有时您可能需要将一个局部变量复制到另一个局部变量,然后再将其传递给匿名方法。检查此 以修复报告的意外行为!

这可能会帮助您弄清楚发生了什么。

如果你从这个稍微简化的开始 class:

public class Example
{
    private void CreateAction()
    {
        int bus = 4;
        object[] data = new object[16];
        int length = 1500;

        Action doWorkLater = () =>
        {
            var busCopy = bus;
            var dataCopy = data;
            var lengthCopy = length;

            this.WorkMethod(busCopy, dataCopy, lengthCopy);
        };

        doWorkLater.Invoke();
    }

    public void WorkMethod(int bus, object[] data, int length)
    {
    }
}

...然后编译器基本上会产生这个:

public class Example
{
    private void CreateAction()
    {
        Example.GeneratedClass closure = new Example.GeneratedClass();
        closure.parent = this;
        closure.bus = 4;
        closure.data = new object[16];
        closure.length = 1500;

        // ISSUE: method pointer
        IntPtr method = __methodptr(closure.CreateAction);
        new Action((object)closure, method)();
    }

    public void WorkMethod(int bus, object[] data, int length)
    {
    }

    [CompilerGenerated]
    private sealed class GeneratedClass
    {
        public int bus;
        public object[] data;
        public int length;
        public Example parent;

        internal void CreateAction()
        {
            this.parent.WorkMethod(this.bus, this.data, this.length);
        }
    }
}

闭包中的局部变量不再是方法的局部变量,而是生成 class 中的 public 个字段。

现在,您对 C# 和 classes 的了解都适用了。