在基础构造函数中使用lambdas表达式的例子

时间:2009-12-09 18:46:52

标签: c# linq c#-3.0 lambda

在我们构建的框架中,我们需要以下模式:

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

不应该在“捕获时间”而不是“执行时间”中抛出异常吗? 这种行为是否有记录?

我可以用不同的方式重构代码,但我认为值得评论。

5 个答案:

答案 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)不再合法的事实打破了我在施工期间所做的部分功能应用。可以通过将其移动到构造函数的主体来修复,但是存在顺序差异:基类不能再透明地将部分函数应用程序传递给自身(因为基类构造函数在子类构造函数的主体之前被调用) )。

相关问题