Null-coalescing out 参数给出意外警告

Posted

技术标签:

【中文标题】Null-coalescing out 参数给出意外警告【英文标题】:Null-coalescing out parameter gives unexpected warning 【发布时间】:2019-08-07 23:22:59 【问题描述】:

使用这个结构:

var dict = new Dictionary<int, string>();
var result = (dict?.TryGetValue(1, out var value) ?? false) ? value : "Default";

我收到一条错误消息 CS0165 use of unassigned local variable 'value',这不是我所期望的。 value 怎么可能是未定义的?如果字典为 null,则内部语句将返回 false,这将使外部语句评估为 false,返回 Default

我在这里缺少什么?仅仅是编译器无法完全评估该语句吗?还是我搞砸了?

【问题讨论】:

特殊状态“在真表达式后确定分配”或“在假表达式后确定分配”仅针对有限数量的运算符进行跟踪。据我了解,?.?? 不在其中。你可以改用(dict != null &amp;&amp; dict.TryGetValue(1, out var value)) ? value : "Default" 是的,我就是这样做的,我还创建了一个扩展方法来简化事情。能够在例如 Linq Query 语法中使用这样的结构而不需要额外的扩展方法会很好。 【参考方案1】:

仅仅是编译器无法完全评估语句吗?

是的,或多或少。

编译器不跟踪未分配,它跟踪相反的“明确分配”。它必须在某个地方停下来,在这种情况下,它需要合并有关库方法TryGetValue() 的知识。它没有。

【讨论】:

但是在从方法返回之前必须分配所有out参数,除非方法抛出异常,对吧?所以编译器应该知道输出值至少有一个我期望的默认值。如果代码显示?? true,那将导致未分配的值。 是的,我认为 @PetSerAl 更接近 ?.?? 运算符不被考虑。【参考方案2】:

您的分析是正确的。这不是编译器进行的分析,因为编译器进行了 C# 规范所要求的分析。分析如下:

如果condition?consequence:alternative 表达式的条件是编译时常量true,则替代分支不可到达;如果false,则结果分支不可达;否则,两个分支都可以访问。

本例中的条件不是一个常数,因此结果和替代都是可达的。

局部变量value只有在dict不为空时才被明确赋值,因此value在达到结果时未明确赋值

但是结果要求value被明确赋值

所以这是一个错误。

编译器没有你聪明,但它是 C# 规范的准确实现。 (请注意,我没有在这里勾勒出针对这种情况的额外特殊规则,其中包括诸如“在真表达式之后肯定分配”等谓词。有关详细信息,请参阅 C# 规范。)

顺便说一句,C# 2.0 编译器太聪明了。例如,如果对于某个 int local x 有类似 0 * x == 0 的条件,它将推断“无论x 的值是什么,该条件始终为真”并将替代分支标记为不可访问。该分析在与现实世界相匹配的意义上是正确的,但在 C# 规范明确表示仅针对编译时常量进行推导并且同样明确表示涉及 变量不是常量

记住,这个东西的目的是找bug,什么更有可能呢?有人写了0 * x == 0 ? foo : bar 的意思是“总是foo”,或者他们不小心写了一个错误?我修复了编译器中的错误,从那时起它就严格符合规范。

在您的情况下,没有错误,但是代码太复杂而编译器无法分析,因此期望人类分析可能也太复杂了。看看能不能简化。我可能会做的是:

public static V GetValueOrDefault<K, V>(
  this Dictionary<K, V> d,
  K key,
  V defaultValue)

    if (d != null && d.TryGetValue(key, out var value))
      return value;
    return defaultValue;

…
var result = dict.GetValueOrDefault(1, "Default");

目标应该是使调用站点可读;我认为我的呼叫站点比您的更具可读性。

【讨论】:

同意,您的解决方案更具可读性。我不确定我是否想在调用站点隐藏 null 检查,但无论如何在扩展方法中检查 null 并没有什么坏处。不过有一个问题,您的意思是在第二个项目符号中说“在这种情况下的 条件 不是常数”吗?

以上是关于Null-coalescing out 参数给出意外警告的主要内容,如果未能解决你的问题,请参考以下文章

csharp 使用null-coalescing运算符的示例。

在 Null-coalescing assignmentmen C# 中分配空值

十进制转任意进制

在任意逻辑参数变化实验中显示模拟进度百分比

调用.sql文件有没有比较好的参数传递方法

可变参数类?