编译时间常量和引用类型

InB*_*een 6 c#

好的,请考虑以下代码:

    const bool trueOrFalse = false;
    const object constObject = null;

    void Foo()
    {
        if (trueOrFalse)
        {
            int hash = constObject.GetHashCode();
        }

        if (constObject != null)
        {
            int hash = constObject.GetHashCode();
        }
    }
Run Code Online (Sandbox Code Playgroud)

trueOrFalse是一个编译时常量,因此,编译器正确警告int hash = o.GetHashCode();无法访问.

此外,constObject是一个编译时常量,因此编译器再次正确警告,这int hash = o.GetHashCode();o != null永远不可达的true.

那么为什么编译器没有弄清楚:

    if (true)
    {
        int hash = constObject.GetHashCode();
    }
Run Code Online (Sandbox Code Playgroud)

是100%肯定是运行时异常,因此发出编译时错误?我知道这可能是一个愚蠢的角落案例,但编译器似乎非常聪明地推理编译时间常量值类型,因此,我期望它也可以找出这个带有引用类型的小角落情况.

Eri*_*ert 11

更新:这个问题是我的博客在2011年7月17日的主题 2.感谢您提出的好问题!

为什么编译器没有发现我的代码100%肯定是运行时异常,从而发出编译时错误?

为什么编译器会使代码保证会引发编译时错误?不会这样:

int M()
{
    throw new NotImplementedException();
}
Run Code Online (Sandbox Code Playgroud)

进入编译时错误?但这与你想要的完全相反; 您希望这是一个运行时错误,以便编译不完整的代码.

现在,你可能会说,嗯,解除引用null总是不可取的,而"未实现"的例外显然是可取的.那么编译器是否可以检测到保证发生空引用异常的这种特定情况,并给出错误?

当然可以.我们只需花费预算来实现一个数据流分析器,该分析器跟踪已知给定表达式何时始终为null,然后使其成为编译时错误(或警告)以取消引用该表达式.

接下来要回答的问题是:

  • 这个功能的价格是多少?
  • 用户累积了多少好处?
  • 是否有其他可能的功能具有更好的成本效益比,并为用户提供更多价值?

第一个问题的答案是"相当多" - 代码流分析器的设计和构建成本很高.第二个问题的答案是"不是很多" - 你可以证明 null将被解除引用的情况的数量非常少.在过去的十二年里,第三个问题的答案一直是"是".

因此,没有这样的功能.

现在,您可能会说,C#确实有一些有限的能力来检测表达式何时始终/永远为空; 可空算术分析器使用此分析生成更优的可空算术代码(*),显然流量分析器使用它来确定可达性.那么为什么不使用现有的可空性和流量分析器来检测何时总是取消引用空常量?

当然,实施起来很便宜.但相应的用户利益现在很小.实际代码中有多少次将常量初始化为null,然后取消引用它?似乎不太可能有人会这样做.

而且:是的,在编译时而不是运行时检测错误总是更好,因为它更便宜.但是这里的错误 - 保证取消引用null - 将在第一次测试代码时被捕获,并随后被修复.

所以基本上这里的功能请求是在编译时检测一个非常不可能和明显的错误情况,它总是会在第一次运行代码时立即被捕获并修复.因此,将预算用于实施它并不是一个很好的候选人; 我们有很多更高的优先事项.


(*)请参阅有关Roslyn编译器如何执行此操作的长篇文章,该文章始于http://ericlippert.com/2012/12/20/nullable-micro-optimizations-part-one/

  • @SebastianGodelet:非常好.如果像Resharper或FXCOP这样的工具已经进行了这种分析,那么就不要通过将它添加到C#编译器来重复工作. (2认同)