初始化器中类字段上的C#-闭包?

初始化器中类字段上的C#-闭包?,c#,constructor,lambda,initialization,closures,C#,Constructor,Lambda,Initialization,Closures,考虑以下代码: using System; namespace ConsoleApplication2 { class Program { static void Main(string[] args) { var square = new Square(4); Console.WriteLine(square.Calculate()); } } class Mat

考虑以下代码:

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;
    }
}
使用系统;
命名空间控制台应用程序2
{
班级计划
{
静态void Main(字符串[]参数)
{
var平方=新平方(4);
Console.WriteLine(square.Calculate());
}
}
马修班
{        
受保护数学运算(Func-calc){u-calc=calc;}
public int Calculate(){return_calc();}
私有函数计算;
}
班级:马修
{
公共平方(整数操作数)
:base(()=>\u操作数*\u操作数)//运行时异常
{
_操作数=操作数;
}
私有整数操作数;
}
}
(忽略类设计;我实际上并不是在编写计算器!这段代码只是对一个需要一段时间才能缩小范围的大得多的问题进行了最小限度的修改)

我希望它要么:

  • 打印“16”,或
  • 如果在此场景中不允许关闭成员字段,则引发编译时错误

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

您是否尝试过使用
()=>operand*operand
?问题是无法确定在调用基时是否会设置_操作数。是的,它试图在您的方法上创建一个闭包,并且不能保证事情的顺序


因为您根本没有设置操作数,所以我建议只使用
()=>operand*operand

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

问题是在创建闭包时,
尚未初始化。当提供该参数时,构造函数还没有实际运行。因此,产生的
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)
    {
    }
}
类程序
{
静态void Main(字符串[]参数)
{
var测试=新衍生测试();
对象o=test.Func();
Console.WriteLine(o==null);
Console.ReadLine();
}
}
基于类的测试
{
公共基本测试(Func Func)
{
this.Func=Func;
}
公共Func Func{get;私有集;}
}
类派生测试:BaseTest
{
public-DerivedTest():基(()=>this)
{
}
}
猜猜这印的是什么?是的,它是
true
,闭包返回
null
,因为
在执行时未初始化

编辑

我对托马斯的声明很好奇,认为他们可能在随后的VS版本中改变了行为。事实上,我对这件事有了一些了解。它被关闭为“不会修复”。奇怪

正如微软在回复中所说,在基本构造函数调用的参数列表中使用
this
引用通常是无效的;引用在那个时间点上根本不存在,如果您尝试“裸体”使用它,实际上会得到一个编译时错误。因此,可以说它应该为闭包情况生成一个编译错误,但是
这个
引用对编译器是隐藏的,这(至少在VS 2008中)为了防止人们这样做,我们必须知道如何在封闭内部寻找它。事实并非如此,这就是为什么你最终会有这种行为。

这个怎么样:

using System;
using System.Linq.Expressions;

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

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

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

        private int _operand;
    }
}
使用系统;
使用System.Linq.Expressions;
命名空间控制台应用程序2
{
班级计划
{
静态void Main(字符串[]参数)
{
var平方=新平方(4);
Console.WriteLine(square.Calculate());
}
}
马修班
{
受保护的MathOp(表达式计算){u calc=calc.Compile();}
public int Calculate(){return_calc();}
私有函数计算;
}
班级:马修
{
公共平方(整数操作数)
:基(()=>\操作数*\操作数)
{
_操作数=操作数;
}
私有整数操作数;
}
}

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

它不能为我编译。。。。非静态字段、方法或属性“ConsoleApplication2.Square.\u操作数”需要对象引用。这是您的准确代码吗?是的,这是一个复制/粘贴,它为我编译。请注意,我在VS2008上--正如Aaron所指出的,2010编译器团队可能将其归类为错误(即同意我的观点:))这是编译器中的错误,它应该生成编译时错误。Eric Lippert已经从另一个线程中意识到了这一点。事实上,它是用VS2008编译的,而不是用VS2010@Thomas是的,我做了,它编译了,我得到了相同的运行时错误。奇怪的是,你得到了一个编译错误;我是VS2008,你是VS2010吗?也许他们把这归类为一个bug,并更新了编译器来检测它?+1很好的解释。我怀疑这一点,但无法确定我的手表窗口中缺少/this/指针不仅仅是一个VS怪癖(我发现它太容易混淆了)。@Thomas Levesque:这引发了更多的问题,因为,正如我现在在编辑中所指出的,这一问题已经报告给了微软,他们以“不会修复”结束了问题,然后他们修复了它!Sigh@Aaronaught:出色的调查,我希望我能多投一次票;)@Daniel Brückner:哈哈,我想如果代码在作为派生类的构造函数参数提供的闭包中,那么是的P如果
这个
在任何时候都是
空的
,那么你就有更大的事情要担心了!可以说,这违背了目的。在我的“真实”代码中,我有sev