空合并参数给出意外警告

Null-coalescing out parameter gives unexpected warning

使用这个构造:

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 怎么可能是未定义的?如果字典为空,则内部语句将 return false 这将使外部语句评估为 false,returning Default.

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

Is it just the compiler being unable to evaluate the statement fully?

是的,或多或少。

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

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

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

  • 这种情况下的条件不是常数,因此结果和备选方案都是可达的。

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

  • 但结果要求value肯定赋值

  • 所以这是一个错误。

编译器没有你那么聪明,但它是C#规范的准确实现。 (请注意,我没有在这里勾画出针对这种情况的额外特殊规则,其中包括像 "definitely assigned after a true expression" 等谓词。有关详细信息,请参阅 C# 规范。)

顺便说一句,C# 2.0 编译器太聪明了。例如,如果你有一个像 0 * x == 0 这样的条件,对于 some int local x 它会推断出 "that condition is always true no matter what the value of x is" 并将替代分支标记为不可访问。该分析在符合现实世界的意义上是正确的,但在 C# 规范明确指出仅对编译时常量进行推导并且同样明确指出涉及 [= 的表达式的意义上是不正确的45=]变量 不是常量

记住,这东西的目的是找bug,还有什么更有可能呢?有人写 0 * x == 0 ? foo : bar 的意思是 "always 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");

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