C# - 在初始化器中的类字段上的闭包? [英] C# - closures over class fields inside an initializer?

查看:122
本文介绍了C# - 在初始化器中的类字段上的闭包?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请考虑以下代码:

 使用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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆