C# - 关闭初始化程序中的类字段?

Ric*_*erg 12 c# lambda constructor closures initialization

请考虑以下代码:

using System;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            var square = new Square(4);
            Console.WriteLine(square.Calculate());
        }
    }

    class MathOp
    {        
        protected MathOp(Func<int> calc) { _calc = calc; }
        public int Calculate() { return _calc(); }
        private Func<int> _calc;
    }

    class Square : MathOp
    {
        public Square(int operand)
            : base(() => _operand * _operand)  // runtime exception
        {
            _operand = operand;
        }

        private int _operand;
    }
}
Run Code Online (Sandbox Code Playgroud)

(忽略课堂设计;我实际上并没有写一个计算器!这段代码只是代表了一个需要一段时间才能缩小范围的更大问题的最小代表)

我希望它能:

  • 打印"16",或
  • 如果在此方案中不允许关闭成员字段,则抛出编译时错误

相反,我得到了一个无意义的异常抛出指定的行.在3.0 CLR上,它是一个NullReferenceException ; 在Silverlight CLR上,臭名昭着的操作可能会破坏运行时的稳定性.

Eri*_*ert 14

这是一个已修复的编译器错误.代码本来就不应该是合法的,如果我们要允许它,我们至少应该生成有效的代码.我的错.很抱歉给您带来不便.


Aar*_*ght 11

它不会导致编译时错误,因为它一个有效的闭包.

问题是this在创建闭包时尚未初始化.当提供该参数时,您的构造函数实际上还没有运行.所以结果NullReferenceException实际上是非常合乎逻辑的.就是this这样null!

我会向你证明.让我们用这种方式重写代码:

class Program
{
    static void Main(string[] args)
    {
        var test = new DerivedTest();
        object o = test.Func();
        Console.WriteLine(o == null);
        Console.ReadLine();
    }
}

class BaseTest
{
    public BaseTest(Func<object> func)
    {
        this.Func = func;
    }

    public Func<object> Func { get; private set; }
}

class DerivedTest : BaseTest
{
    public DerivedTest() : base(() => this)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

猜猜这是什么印刷品?是的true,闭包返回,null因为this它在执行时没有初始化.

编辑

我很好奇托马斯的陈述,认为他们可能在随后的VS版本中改变了行为.我实际上发现了一个关于这个问题Microsoft Connect问题.它被关闭为"不会修复".奇.

正如微软在他们的回复中所说,在this基础构造函数调用的参数列表中使用引用通常是无效的; 该引用根本就不存在,如果你试图"裸",你实际上会得到一个编译时错误.因此,可以说它应该为闭包情况产生编译错误,但是this引用对编译器是隐藏的,至少在VS 2008中,它必须知道在闭包内查找它以防止人们这样做.它没有,这就是为什么你最终会遇到这种行为.

  • @Thomas Levesque:这提出了进一步的问题,因为正如我现在在编辑中注意到的那样,这是向微软报告的,他们将问题关闭为"不会修复" - 然后他们修复了它!*叹* (2认同)