这个值真的可以为空,还是 C# 编译器只是被代码混淆了?

Can this value really ever be null, or is the C# compiler just confused by code?

我发现一些代码与 C# 8 中新的可空引用类型冲突,说我可能正在引用一个 null,但我无法弄清楚它怎么可能为空:

https://dotnetfiddle.net/AoEMzp

public static DateTime JoinNullableDateTime(DateTime? date, DateTime? time)
{
   if ((date == null) && (time == null))
      return DateTime.MinValue;
   else if ((date != null) && (time == null))
      return date.Value.Date;
   else if ((date == null) && (time != null))
      return DateTime.MinValue.Add(time.Value.TimeOfDay);
   else
      return date.Value.Date.Add(time.Value.TimeOfDay);
}

Visual Studio 和 .NET Fiddle return 最后一行的警告:

return date.Value.Date.Add(time.Value.TimeOfDay);

.NET Fiddle

Visual Studio

所以我的问题是,任何人都可以给出 datetime 值的任何示例,这些值会导致此代码中的 NullReferenceException 吗?

一开始,可能的空值组合是:

date time Possible?
null null Yes
not-null null Yes
null not-null Yes
not-null not-null Yes
if ((date == null) && (time == null))`
   return DateTime.MinValue;

此时可能的空值组合是:

date time Possible?
null null No (because we returned)
not-null null Yes
null not-null Yes
not-null not-null Yes
else if ((date != null) && (time == null))
   return date.Value.Date;

此时可能的空值组合是:

date time Possible?
null null No (because we returned)
not-null null No (because we returned)
null not-null Yes
not-null not-null Yes
else if ((date == null) && (time != null))
   return DateTime.MinValue.Add(time.Value.TimeOfDay);

最后可能的空性组合是:

date time Possible?
null null No (because we returned)
not-null null No (because we returned)
null not-null No (because we returned)
not-null not-null Yes

所以此时 不可能 日期和时间为空。两者都保证NOT NULL

else
   return date.Value.Date.Add(time.Value.TimeOfDay);

我看不出 datetime 可以在最后一行 null 的任何方式 - 但它是:

CS8629 Nullable value type may be null.

所以我错过了什么?

事实上,空值消失得更快

第一行之后

(date == null) && (time == null)

我们知道它们中的 一个 仍然有可能为空 - 但不可能两个都为空 。那么我们进行第二次检查:

(date != null) && (time == null)

在这一行之前我们知道只有一个是空的。现在我们找到了原来的那个:time.

这意味着在第 3 行,不可能 date 成为 null。然而我还是添加了一个检查,因为编译器告诉我这样做,而且编译器是绝对可靠的。但无论如何我都有支票:

(date == null) && (time != null)

所以即使在第三个 if 上的额外检查也是多余的。当然它在 4 日是多余的 if。然而还有编译器。

我做错了什么?

我确定我可以 re-write the function 理解一些更难理解的东西,所以它不会让 Roslyn 感到困惑。但这不是关于这个函数,而是关于正在使用的 pattern - 如果这个模式潜伏着 NullReferenceExceptions: i想了解一下!

奖金

我与 .NET Fiddle 进行了交叉核对,以防它是我 Visual Studio 中的工件。

将您的 Visual studio 更新为 2022。

2022 不显示任何可空警告。

this 下载 link

代码分析器可能不够智能,无法得出您的 datetime 永远不能为空的结论。

您可以使用 'null forgiving' operator:

通知它这一事实
public static DateTime JoinNullableDateTime(DateTime? date, DateTime? time)
{
   ...
   else
      return date!.Value.Date.Add(time!.Value.TimeOfDay);
}