【问题标题】:?. operator in C# not compiling for unknown reason?. C# 中的运算符由于未知原因未编译
【发布时间】:2017-07-11 12:05:44
【问题描述】:

在以下代码中,两个变体之一无法编译:

class C
{
    public decimal DecimalField;
}

static C GetC() { return new C(); } //Can return null in reality.

C c = GetC(); //Get a C value from somewhere, this might be null

string toString1 = c?.DecimalField?.ToString(); //Does not compile.
string toString2 = (c?.DecimalField)?.ToString(); //Compiles.

错误 CS0023:操作员“?”不能应用于“十进制”类型的操作数

为什么简单的表单无法编译?

表达式c?.DecimalField 不是decimal? 类型吗?该值可以为 null,因此应应用 ?. 运算符。我很确定这是因为在这段代码中:

var decimalValue = c?.DecimalField;

根据 IDE,var 确实解析为 decimal?

【问题讨论】:

  • 因为decimal 不能为空
  • 我认为十进制类型不能为null,所以这里的null-coalesce操作符没有意义。
  • 不是the simple form,加括号会改变表达式
  • 括号可以改变这样的表达式(使用两个相同的运算符)的明显原因是运算符是否是右结合的。不幸的是,没有包含?. 的官方规范,而且(在我看来)很难知道关联性在这里意味着什么。
  • @Damien_The_Unbeliever 这是一个很好的观点。我也一直不清楚与该运算符的关联性。显然,链必须从左到右执行,但类型似乎是从右到左?!

标签: c# .net c#-6.0 null-conditional-operator


【解决方案1】:

这是因为空条件访问运算符是“短路的”。也就是说,如果你写a?.b.c并执行它而anull,那么不仅.b没有被执行,而且.c也没有被执行。

因此,当您编写a?.b?.c 时,第二个空条件访问运算符并不是要检查aa.b 是否为null只是检查a.b 是否为null。所以,如果a.b 永远不能是null,就像你的情况一样,使用空条件访问运算符是没有意义的,这就是语言不允许它的原因。

这也解释了为什么a?.b?.c(a?.b)?.c 不是同一个东西。

有关这方面的更多信息,您可以阅读the C# Language Design Meeting notes where this was decided

【讨论】:

【解决方案2】:

decimal 类型不是可为空的类型。因此,您的第一个表达式无法编译。与第二个语句的不同之处在于,如果 cnull,括号的内部部分可能已经是 null。因此它可以编译。

这可能导致null 值:(c?.DecimalField)

结果类型为System.Nullable<decimal> 或简写为decimal?

【讨论】:

  • 为什么括号会改变表达式的类型?这似乎不寻常。
  • 在这种情况下非常有意义。如果c 为空,则表达式的类型必须是可为空的类型。但是十进制(DecimalField 的类型)不能为空。这意味着,结果类型不能是小数
  • c?.DecimalField 的类型为 decimal?。该字段应该无关紧要,C# 语言很少关心值的来源。它关心类型。在这里,类型应该支持?..你怎么看?
  • @boot4life “为什么括号会改变表达式的类型?这似乎很不寻常。” 这是您即将理解的非常重要的事情。你真正想要的是这样的:c?.DecimalField.ToString() 当你第一次看到它时,你可能会认为它和(c?.DecimalField).ToString() 一样但事实并非如此。假设cnull。然后后者将在另一个null 上调用ToString,在这种情况下会给出""。前者根本不会调用任何ToString,而是简单地替换为字符串类型的null 的default(string)
  • @JeppeStigNielsen 好的,所以整个链条都短路了。这就是我所缺少的。我现在还找到了一份规范草案,证实了这一点 (github.com/dotnet/csharplang/blob/master/spec/…)。
【解决方案3】:

decimal 类型不能为 null,因此 null-coalesce 运算符在这里没有意义。只需将 toString1 设置为某个值即可。

如果您想在所有情况下编译它,您应该编辑您的 DecimalField,通过在字段定义中添加 decimal ? insted of decimal 来将其与可空类型进行比较。

【讨论】:

    【解决方案4】:

    你有三个变种。

    你可以设置默认值,它返回DecimalField"0"

    string toString = c?.DecimalField.ToString() ?? decimal.Zero.ToString();
    

    没有默认值,返回DecimalFieldnull

    string toString = c?.DecimalField.ToString();
    

    或者你可以让DecimalField为空,它返回DecimalFieldnull

    public decimal? DecimalField;
    ...
    string toString1 = c?.DecimalField?.ToString(); //Compile now!
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-23
      • 2021-12-09
      • 2013-10-21
      • 1970-01-01
      相关资源
      最近更新 更多