在我们构建的框架中,我们需要以下模式:
public class BaseRenderer
{
Func<string> renderer;
public BaseRenderer(Func<string> renderer)
{
this.renderer = renderer;
}
public string Render()
{
return renderer();
}
}
public class NameRenderer : BaseRenderer
{
public string Name{ get; set; }
public NameRenderer ()
: base(() =>this.Name)
{}
}
如您所见,我们在调用基础构造函数时创建了一个lambda。
public class Program
{
public static void Main()
{
Console.WriteLine(new NameRenderer(){Name = "Foo"}.Render());
}
}
奇怪的是,当尝试实际使用lambda时,它会抛出NullReferenceException(控制台应用程序)或某种ExecutionEngineExceptionexception(IIS上的Web应用程序)。
我认为原因是在调用基础构造函数之前该指针尚未就绪,因此lambda在此阶段无法捕获this.Name
。
不应该在“捕获时间”而不是“执行时间”中抛出异常吗? 这种行为是否有记录?
我可以用不同的方式重构代码,但我认为值得评论。
答案 0 :(得分:22)
正如asgerhallas正确指出的那样,根据规范,这不应该是合法的。我们意外地允许这种虚假用法被错误检测器偷偷摸摸,该错误检测器在合法的情况下搜索“this”的错误用法。我已经修复了这个bug; C#4编译器正确地将您的程序标记为错误。
许多道歉给您带来不便;这是我的错误。
答案 1 :(得分:6)
7.5.7的C#规范说:“只允许在实例构造函数,实例方法或实例访问器的块中进行此访问。”
甚至更直接地在10.11.1中:“实例构造函数初始化器无法访问正在创建的实例。因此,在构造函数初始化程序的参数表达式中引用它是一个编译时错误,因为它是编译器 - 参数表达式通过简单名称引用任何实例成员的时间错误。“
虽然该实例是根据7.5.10创建的。
嗯。这实际上很奇怪。我没有看到任何编译时错误。
答案 2 :(得分:4)
我认为你是对的。调用基类构造函数时尚未构造子类,因此访问子类上的成员会为您提供空引用。如果实例存在与否,CLR无法在编译时知道。
将逻辑移动到构造函数体应该可以解决问题。
答案 3 :(得分:2)
lambda捕获了“this”的值并捕获了null,因为该对象尚未构造。这让我觉得它是编译器错误,应该为此产生错误。像这样的代码通常生成CS0027(关键字'this'在当前上下文中不可用)或CS0120(需要对象引用)。我打赌这不容易实现。
Anyhoo,代码无法正常工作。 NameRenderer类需要一个带字符串参数的构造函数,以便初始化基类。
答案 4 :(得分:1)
不会: base(()=>this)
合法吗?您可以执行: this()
,因此对此的引用似乎没有问题,只是没有属性。 : base(()=>this)
不再合法的事实打破了我在施工期间所做的部分功能应用。可以通过将其移动到构造函数的主体来修复,但是存在顺序差异:基类不能再透明地将部分函数应用程序传递给自身(因为基类构造函数在子类构造函数的主体之前被调用) )。