【问题标题】:Corner case in using lambdas expression in base constructor在基本构造函数中使用 lambda 表达式的极端情况
【发布时间】:2009-12-09 18:46:52
【问题描述】:

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

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

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

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

【问题讨论】:

  • 这个问题之前问过,C# 4.0 会修复

标签: c# linq c#-3.0 lambda


【解决方案1】:

正如 asgerhallas 正确指出的那样,根据规范,这不应该是合法的。我们不小心允许这种虚假用法被错误检测器偷偷摸摸,该错误检测器会在合法之前搜索“this”的错误用法。我已经修复了这个错误; C# 4 编译器正确地将您的程序标记为错误。

对给您带来的不便深表歉意;这是我的错误。

【讨论】:

  • 哈哈,a) 你在编译器中遇到了一个错误,b) 负责的开发人员对你的问题做出了错误的回应:-P sweet!
【解决方案2】:

7.5.7 的 C# 规范说:“this-access 只允许在实例构造函数、实例方法或实例访问器的块中。”

在 10.11.1 中甚至更直接:“实例构造函数初始化程序无法访问正在创建的实例。因此,在构造函数初始化程序的参数表达式中引用 this 是编译时错误,因为它是 compile-参数表达式通过简单名称引用任何实例成员的时间错误。”

虽然实例是根据 7.5.10 创建的。

嗯。这其实很奇怪。我没有看到任何编译时错误。

【讨论】:

  • 我第一次不太对。当时有一个实例,但您无权访问它。答案已更新。
  • 您没有看到编译错误,因为编译器有错误。它应该是一个错误,在 C# 4 中,它是。
【解决方案3】:

我认为你是对的。调用基类构造函数时尚未构造子类,因此访问子类上的成员会给您一个空引用。 CLR 无法在编译时知道实例是否存在。

将逻辑移到构造函数主体应该可以解决问题。

【讨论】:

    【解决方案4】:

    lambda 捕获了“this”的值并捕获了 null,因为该对象尚未构造。这让我觉得这是一个编译器错误,它应该为此产生一个错误。像这样的代码通常会生成 CS0027(关键字“this”在当前上下文中不可用)或 CS0120(需要对象引用)。我敢打赌,这并不容易实现。

    Anyhoo,代码无法运行。 NameRenderer 类需要一个带有字符串参数的构造函数,以便它可以初始化基类。

    【讨论】:

      【解决方案5】:

      : base(()=&gt;this) 不合法吗?你可以做: this() 所以对它的引用似乎很好,只是不是它的属性。 : base(()=&gt;this) 不再合法的事实只是破坏了我在构建过程中所做的一些部分功能应用程序。可以通过将其移动到构造函数的主体中来修复,但是有一个顺序差异:基类不能再透明地将部分函数应用程序传递给自身(因为基类构造函数在子类构造函数的主体之前被调用) )。

      【讨论】:

        猜你喜欢
        • 2013-05-24
        • 1970-01-01
        • 2017-06-27
        • 2016-03-04
        • 2015-09-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多