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

时间:2016-07-28 18:40:59

标签: c# anonymous-methods

我正在创建一个匿名方法并将其传递给稍后要调用的操作。我想将一些数字数据(regex = r'\b(%s)\b' % "|".join(words) re.search(regex, positive_words) )传递给我的匿名方法。为了按值传递数据,我是否需要创建副本?或者,数据是否会按值传递?

如果我不得不创建副本,我认为实现会是这样的:

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

在这种情况下,bus我创建的某个类)会通过引用还是按值传递?

3 个答案:

答案 0 :(得分:1)

闭包可以通过引用 * 显着捕获值 - 请注意,如果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. 
}

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

答案 1 :(得分:1)

您传递的内容不是按值复制。

如果您在执行操作之前不打算修改值,则无需担心如何传递值。但他们并没有超越价值。即使你从另一个方法返回动作和Invoke它。值保存在编译器生成的类中。无需担心

如果您希望在执行操作之前更改数据,那么您做错了。您正在使用的方法(将值类型复制到局部变量)应该在操作之外而不是在其内部完成。对于引用类型(Array),即使将其复制到局部变量,也会复制它所引用的引用,以便反映复制局部变量的任何更改。

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

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

答案 2 :(得分:0)

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

如果你从这个略微简化的课程开始:

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

闭包中捕获的局部变量不再是方法的局部变量,而是生成类中的公共字段。

现在你所知道的关于C#和类的一切都适用。