C# - 在初始化器中的类字段上的闭包? [英] C# - closures over class fields inside an initializer?
问题描述
请考虑以下代码:
使用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;
}
类Square:MathOp
{
public Square(int operand)
:base(()=> _operand * _operand)运行时异常
{
_operand = operand;
}
private int _operand;
}
}
(忽略类设计;
我希望它可以:
- 打印16,或
- 如果不允许在成员字段中关闭,方案
而是在指定的行上抛出一个无意义的异常。在3.0 CLR上,它是一个 NullReferenceException ;在Silverlight CLR上,臭名昭着的操作可能会破坏运行时的稳定性。
因为是有效的闭包。
问题是 this
在创建闭包时尚未初始化。当提供参数时,构造函数实际上没有运行。所以结果 NullReferenceException
实际上是相当逻辑的。
是
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;私人集}
}
class DerivedTest:BaseTest
{
public DerivedTest():base(()=> this)
{
}
}
猜猜这是什么打印?是的, true ,因为
this
$ b
$ b
语句,认为也许他们在后来的VS版本中改变了行为。我实际上发现了一个 Microsoft Connect问题关于这个事情。它被关闭,因为不会修复。 Odd。
正如Microsoft在其回应中所说,通常在参数中使用 this
基础构造函数调用列表;该引用根本不存在于那个时间点,如果你试图使用它裸,你实际上会得到一个编译时错误。所以,可以说应该产生一个编译错误的关闭case,但 this
引用隐藏从编译器,这(至少在VS 2008)必须知道要在封闭内部查看 ,以防止人们这样做。它不会,这就是为什么你最终以这种行为。
Consider the following code:
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;
}
}
(ignore the class design; I'm not actually writing a calculator! this code merely represents a minimal repro for a much bigger problem that took awhile to narrow down)
I would expect it to either:
- print "16", OR
- throw a compile time error if closing over a member field is not allowed in this scenario
Instead I get a nonsensical exception thrown at the indicated line. On the 3.0 CLR it's a NullReferenceException; on the Silverlight CLR it's the infamous Operation could destabilize the runtime.
It's not going to result in a compile-time error because it is a valid closure.
The problem is that this
is not initialized yet at the time the closure is created. Your constructor hasn't actually run yet when that argument is supplied. So the resulting NullReferenceException
is actually quite logical. It's this
that's null
!
I'll prove it to you. Let's rewrite the code this way:
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)
{
}
}
Guess what this prints? Yep, it's true
, the closure returns null
because this
is not initialized when it executes.
Edit
I was curious about Thomas's statement, thinking that maybe they'd changed the behaviour in a subsequent VS release. I actually found a Microsoft Connect issue about this very thing. It was closed as "won't fix." Odd.
As Microsoft says in their response, it is normally invalid to use the this
reference from within the argument list of a base constructor call; the reference simply does not exist at that point in time and you will actually get a compile-time error if you try to use it "naked." So, arguably it should produce a compile error for the closure case, but the this
reference is hidden from the compiler, which (at least in VS 2008) would have to know to look for it inside the closure in order to prevent people from doing this. It doesn't, which is why you end up with this behaviour.
这篇关于C# - 在初始化器中的类字段上的闭包?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!