我有一段代码,编译器说myObj
在ValidateMyObj(myObj)
中可能是空的。由于string.IsNullOrEmpty
返回false
并输入条件必须为非空,它如何考虑它可能为空?
但是最重要的是,它怎么会认为它在myObj.Exists
中不能是空的
var myObj = myObjCol.FirstOrDefault(x => x.Member);
if (!string.IsNullOrEmpty(myObj?.StrProp))
{
ValidateMyObj(myObj);
if (ViewBag.IsValid)
myObj.Exists = true;
}
编辑:使用显式空检查
简单地添加显式null检查就会产生相同的行为:
var myObj = myObjCol.FirstOrDefault(x => x.Member);
if (myObj != null && !string.IsNullOrEmpty(myObj?.StrProp))
{
ValidateMyObj(myObj);
if (ViewBag.IsValid)
myObj.Exists = true;
}
这将编译为(根据ILSpy反编译程序,当禁用?
操作符的反编译时)
MyObjType myObj = myObjCol.FirstOrDefault(x => x.Member);
if (myObj != null && !string.IsNullOrEmpty((myObj != null) ? myObj.StrProp : null))
{
ValidateMyObj(myObj);
if (ViewBag.IsValid)
myObj.Exists = true;
}
另一方面,如果我移除空条件运算符,那么squiggly行就会被删除。
var myObj = myObjCol.FirstOrDefault(x => x.Member);
if (myObj != null && !string.IsNullOrEmpty(myObj.StrProp))
{
ValidateMyObj(myObj);
if (ViewBag.IsValid)
myObj.Exists = true;
}
实际上,如果删除显式check和null条件运算符,也不会在传递给sNullOrEmpty``的参数上给出一个参数,那么它也是不正确的:
var myObj = myObjCol.FirstOrDefault(x => x.Member);
if (!string.IsNullOrEmpty(myObj.StrProp))
{
ValidateMyObj(myObj);
if (ViewBag.IsValid)
myObj.Exists = true;
}
发布于 2022-06-16 04:03:55
C#编译器的空状态分析试图根据使用它的上下文跟踪每个变量的空状态。
空状态可能是:
。
在赋值之后,var myObj = myObjCol.FirstOrDefault(x => x.Member);
myObj处于可能-空状态(这实际上取决于框架-参见下面)。
对string.IsNullOrEmpty
的调用不会更改空状态。string.IsNullOrEmpty
方法只是一种普通的方法。如果没有额外的知识,编译器就不知道参数的空状态。
结果是,在.NET 6中,编译器得到了额外的帮助。在.NET 6中,该方法的签名是:
public static bool IsNullOrEmpty([NotNullWhen(false)] string? value);
然而,在.NET框架中,签名是:
public static bool IsNullOrEmpty(string value);
NotNullWhen
属性告诉编译器,当方法返回false时,传递给string.IsNullOrEmpty
的参数不是null。因此,在.NET 6中,您在原始代码中不会收到任何警告,因为编译器在if-块中将空状态更改为null。在.NET Framework4.8(或更早版本)中,它无法做到这一点,因此if块内的空状态与外部状态相同。
令人惊讶的是,在为myObj
添加显式null检查之后,squiggly行并没有消失。
if (myObj != null && !string.IsNullOrEmpty(myObj?.StrProp)) { ... }
等于
if (myObj != null)
{
// null-state of myObj is not-null
if (!string.IsNullOrEmpty(myObj != null ? myObj.StrProp : null))
{
// the second test changed the null-state of myObj to maybe-null
}
}
现在删除第二个测试时,空状态不会改变,警告也会消失。
框架还会影响如何处理FirstOrDefault
方法的返回值。
刚开始的时候你有:
var myObj = myObjCol.FirstOrDefault(x => x.Member);`
显然,这应该将myObj
设置为可能-空状态,并在不检查null的情况下取消对变量的引用时发出警告:
if (!string.IsNullOrEmpty(myObj.StrProp)) { ... } // should warn: Dereference of a pssibly null reference
当您使用.NET框架时,它似乎不会这样做。但是,当您在.NET 6上时,情况就会发生变化。
当您查看C#和.NET的历史时,可以解释这一点。C# 8是支持可空引用类型的第一个版本。它是在框架的最后一个版本 (4.8)之后不久发布的。
在.NET框架中,签名是:
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
在.NET 6中,它是:
public static TSource? FirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
这意味着当您在FirstOrDefault<T>
框架上调用.NET时,可空上下文是不清楚的,但是在.NET 6上是明确的。
据我所知,C#编译器团队已经决定在上下文不明确时不发出警告,因为这会给出太多的错误警告,并会惹恼太多的人。
话虽如此:如果您想要更可靠的可空性分析,则应该使用框架的一个版本,该版本完全了解空。
发布于 2022-06-13 09:46:20
如果我做对了,那么我认为这是因为IsNullOrEmpty
引用的是StrProp
,而警告的目标是myObj
澄清:如果myObj
为null,则不需要检查StrProp
https://stackoverflow.com/questions/72606982
复制相似问题