闭包 - 捕获变量和将它们作为参数读取之间的差异

时间:2015-12-14 19:16:45

标签: c# .net lambda closures anonymous-function

假设我们有这个课程:

// Provides deferred behaviour
public class Command<TResult>
{
     private Func<object[], TResult> _executeFunction;
     private object[] _args;

     public Command(Func<object[], TResult> execution, params object[] arguments)
     {
          _executeFunction = execution;
          _args = arguments;
     }

     public TResult Execute()
     {
          return _executeFunction(_args);
     }
}

这两个匿名函数之间有什么区别?

int a = 1;
int b = 4;

// a and b are passed in as arguments to the function
Command<int> sum = new Command<int>(args => (int)args[0] + (int)args[1], a, b);

// a and b are captured by the function
Command<int> sum2 = new Command<int>(_ => a + b);

Console.WriteLine(sum.Execute()); //Prints out 5
Console.WriteLine(sum2.Execute()); //Prints out 5

我特意寻找性能差异。

此外,我们知道如果某个班级持有sum2的引用,那么ab将超出他们定义的范围,可能永远不会被如果函数仍在任何地方引用,则为GC。

sum会发生同样的情况吗? (考虑参数是引用类型而不是本例中的值类型)

1 个答案:

答案 0 :(得分:1)

当你传递变量ab时,你就是这么做的。 14的值分别传入。但是,当您在lambda表达式的上下文(或范围)中引用ab时,值为&#34;已捕获&#34;。 lambda表达式范围内的变量ab被视为对范围之外的原始的引用,这意味着如果它们在lambda的范围内被更改 - 那么它们也是原件。当编译成IL时,它们驻留在共享实例的类中。

static void Main()
{
    int a = 1;
    int b = 4;

    // a and b are passed in as arguments to the function
    var sum = new Command<int>(args => (int)args[0] + (int)args[1], a, b);

    // a and b are captured by the function
    var sum2 = new Command<int>(_ =>
    {
        var c = a + b;

        a++;
        b++;

        return c;
    });

    Console.WriteLine(sum.Execute()); //Prints out 5
    Console.WriteLine(sum2.Execute()); //Prints out 5

    Console.WriteLine("a = " + a); // Prints 2
    Console.WriteLine("b = " + b); // Prints 5

    Console.ReadLine();
}

IL存在很小的差异,我不认为有任何性能影响值得避免。我通常更喜欢使用lambda表达式来实现可读性。

A look at some of the IL generated from some C# lambdas