【问题标题】:Is copying performed when capturing a value-type into a lambda?将值类型捕获到 lambda 时是否执行复制?
【发布时间】:2009-12-30 19:58:17
【问题描述】:
struct SomeStruct
{
    public int Num { get; set; }
}

class Program
{
    static Action action;

    static void Foo()
    {
        SomeStruct someStruct = new SomeStruct { Num = 5 };
        action = () => Console.WriteLine(someStruct.Num);
    }

    static void Main()
    {
        Foo();
        action.Invoke();
    }
}
  1. 创建 lambda 时是否创建了 someStruct 的副本?
  2. 是否在 Foo 返回时创建了 someStruct 的副本?
  3. 我可以验证没有发生复制吗?在 C++ 中,我将实现复制构造函数并从其中打印。

对标准的引用将不胜感激。任何相关的在线文章也是如此。

【问题讨论】:

    标签: c# .net struct value-type


    【解决方案1】:

    不会有副本。 Lambda 捕获变量,而不是值。

    您可以使用 Reflector 查看编译代码:编译器会将“someStruct”变量移动到辅助类中。

    private static void Foo()
    {
        DisplayClass locals = new DisplayClass();
        locals.someStruct = new SomeStruct { Num = 5 };
        action = new Action(locals.b__1);
    }
    private sealed class DisplayClass
    {
        // Fields
        public SomeStruct someStruct;
    
        // Methods
        public void b__1()
        {
            Console.WriteLine(this.someStruct.Num);
        }
    }
    

    复制结构永远不会导致用户定义的代码运行,因此您无法真正检查它。 实际上,代码在分配给“someStruct”变量时会进行复制。即使对于没有任何 lambda 的局部变量,它也会这样做。

    【讨论】:

      【解决方案2】:

      它不会被复制,它会创建一个闭包。基本上它将结构封装在一个对象中,而不是在堆栈中创建它。

      如果您想确保始终可以使用反射器,但没有必要,请在 Raymond Chen blog 上解释该行为。

      【讨论】:

      • 唯一的问题是您不能为结构创建无参数构造函数。
      【解决方案3】:

      见The implementation of anonymous methods in C# and its consequences (part 1)。你的代码实际上是这样的:

         class SomeHiddenClass {
           SomeStruct someStruct;
           someHiddenMethod() {
             Console.WriteLine(someStruct.Num);
           }
         }
      
         SomeHiddenClass someHiddenVar = new SomeHiddenClass();
         someHiddenVar.someStruct.Num = 5;
         action = someHiddenVar.someHiddenMethod;
      

      【讨论】:

        【解决方案4】:

        不,它不会复制,原因与您在 lambda 中捕获变量时不会创建其他值类型的副本的原因相同(编译器会创建一个幕后类来保存该值)。

        例如,如果你这样做:

        int i = 7;
        Action a = () => Console.WriteLine("lambda i=" + i);
        i++;
        a(); //prints 8
        Console.WriteLine("main i=" + i); //prints 8
        

        lambda 与声明范围共享“i”。你的结构也会发生同样的事情。您可以将其作为测试来证明不会发生复制。

        【讨论】:

          猜你喜欢
          • 2019-05-06
          • 2020-04-17
          • 1970-01-01
          • 1970-01-01
          • 2021-02-04
          • 1970-01-01
          • 1970-01-01
          • 2021-09-15
          • 2013-11-09
          相关资源
          最近更新 更多