【问题标题】:What is meant with "The result is not considered to be constant"“结果不被认为是恒定的”是什么意思
【发布时间】:2017-05-26 06:27:18
【问题描述】:

MSDN,我找到了这句话:

?? 的结果即使它的两个参数都是常量,运算符也不被视为常量。

这意味着什么?他们的意思是编译器优化器不知道这个值是恒定的吗?我没有看到这可能是相关的另一种方式。

编辑: 为什么这不被认为是恒定的?这有什么合乎逻辑的原因吗?

编辑:这段代码给出了x的编译器错误,但y没有,为什么?:可以产生一个常量值,而??不能?

const string NULL = null;
const string val = "value";
const string x = NULL ?? val;
const string y = NULL == null ? val : NULL;

【问题讨论】:

  • 也许只是强调它不能用于编译器(语言规范?)需要常量表达式的地方,例如作为字段启动器。
  • 这正是它所说的 - 你不能在预期常量的地方使用结果 - 例如分配给另一个相同类型的常量。
  • 让我们等待@ericlippert :) 在那之前我们只能猜测
  • 至于它的逻辑原因,您必须作为 MS 来获得明确的答案,但使用带有 ?? 的常量在逻辑上毫无意义,为什么在固定的已知值上使用合并 const string = "a string" ?? "null" 永远不会为空,const string = null ?? "null" 将始终为“空”,所以应该使用右手而不用 ??
  • @Luaan:现在你知道我为什么要反驳“为什么语言不这样做?”这样的问题。 “为什么不呢?”问题很难回答;它预设了宇宙应该是一种它不是的方式,然后需要一个解释。没有人会想到“你为什么不在法国出生?”这样的问题。是合理的,但以某种方式询问语言设计者为什么没有做出特定决定被认为是公平的游戏。

标签: c# null-coalescing-operator


【解决方案1】:

这意味着您不能在常量中使用空合并运算符:

public const int MyInt = 42; // Fine
public const int MyOtherInt = new int?(42) ?? 8; // Compiler error

这主要与大多数其他运算符形成对比,其中恒定的操作数产生的结果也是恒定的:

public const int SomeResult = 12 + 42; // Fine
public const int OtherResult = SomeResult * 2; // Fine

这并不是真正的“优化”问题 - 事实上,?? 运算符在大多数情况下都经过大量优化:

var someValue = new int?(42) ?? 8; // Produces ldc.i4.8

【讨论】:

  • 我相信OP想知道为什么 ??具有常量参数的运算符不会产生常量值。例如。您可以将?: 与常量一起使用以获得常量。您可以使用const string s = false ? null : "abc";,但不能使用const string s = null ?? "abc";
  • @Sinatr hmm 从问题很明显 OP 理解什么是常量:“他们的意思是编译器优化器不知道这个值是常量吗?” 和他的编辑证明
  • @SergeyBerezovskiy 这是在我回答后添加到问题中的。但无论如何,它很可能是题外话——它不在规范中,所以它只是当前 C# 编译器的一个实现细节。除非编译器团队的某个人能够提供他们的推理,否则我们就会陷入困境。
【解决方案2】:

如果您编写的代码可能会导致此错误消息,答案将显而易见。

public void Test()
{
    const int x = ((int?)null ?? 3);
}

这会产生以下错误:

错误9 赋值给'x'的表达式必须是常量

所以编译器不会在编译时计算??的结果。

相比之下,? : 等许多其他运算符是在编译时计算的,因此以下不会产生错误:

const int x = (true ? 3 : 2);

【讨论】:

  • 实际上,如我的示例所示,在大多数情况下,编译器确实会在编译时计算?? 的结果。我认为持续评估只是在其他评估之前发生,而这就是不允许的(无论出于何种原因)。
  • 毫无疑问微软有最后期限,我猜添加错误消息比在编译时完全实现运算符要快。
【解决方案3】:

让我们考虑一下 ??常量表达式中的运算符。原则上是否可能?当然,因为我们在编译时拥有所有必需的信息。它是用 C# 实现的吗?没有。为什么?因为 C# 编译器团队选择不实现此功能。以下是Eric Lippert says 关于选择应该实现的功能的内容:

功能必须如此引人注目,以至于它们值得设计、实施、测试、记录和发布功能的巨额成本。它们必须值得付出使语言复杂化和使未来设计其他功能变得更加困难的成本。

我们什么时候可以使用 ??常量表达式中的运算符?当 expr-first 是空文字时。常量类型是对象或字符串(请记住,C# 团队已经决定不为常量添加 Nullables 支持)。以及此功能将为语言添加什么?

const string s = null ?? "foo";

这不会给语言增加任何便利。完全一样

const string s = "foo";

但更复杂。

【讨论】:

  • @MikeT 小数也可以。您可以使用对象作为常量类型。试试const object o = null。但是你只能用 null 文字来初始化它
  • @MikeT 似乎是个错误。 C# 规范 7.19 规定 常量表达式必须是 null 文字或具有以下类型之一的值:sbyte、byte、short、ushort、int、uint、long、ulong、char、float、double、decimal、bool 、object、字符串或任何枚举类型。。还有compiler says same :)
  • @MikeT 正如我之前写的那样你只能用空字面量来初始化对象。 10 不是 null 文字
  • @MikeT int 不是object。只有装箱的 int 是 object。这可能有点令人困惑,因为ints 在必要时会自动装箱。只是引用类型(不包括string)的唯一有效常量是null,但您仍然可以创建object(或Process 或其他)常量。 decimal 不是内置类型(它是扩展数字库的一部分),但可以是常量。真的,什么应该算作“内置”? intDateTime 是如何“内置”的,而 Action 不是?您只是根据自己的论点制定自己的定义。
猜你喜欢
  • 1970-01-01
  • 2011-05-18
  • 1970-01-01
  • 2012-11-02
  • 1970-01-01
  • 1970-01-01
  • 2013-09-11
  • 2019-11-19
相关资源
最近更新 更多