Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >为什么C#编译器认为这个空引用可以是空的?

为什么C#编译器认为这个空引用可以是空的?
EN

Stack Overflow用户
提问于 2022-06-13 09:40:41
回答 2查看 142关注 0票数 0

我有一段代码,编译器说myObjValidateMyObj(myObj)中可能是空的。由于string.IsNullOrEmpty返回false并输入条件必须为非空,它如何考虑它可能为空?

但是最重要的是,它怎么会认为它在myObj.Exists中不能是空的

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var myObj = myObjCol.FirstOrDefault(x => x.Member);
if (!string.IsNullOrEmpty(myObj?.StrProp))
{
    ValidateMyObj(myObj);
    if (ViewBag.IsValid)
        myObj.Exists = true;
}

编辑:使用显式空检查

简单地添加显式null检查就会产生相同的行为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var myObj = myObjCol.FirstOrDefault(x => x.Member);
if (myObj != null && !string.IsNullOrEmpty(myObj?.StrProp))
{
    ValidateMyObj(myObj);
    if (ViewBag.IsValid)
        myObj.Exists = true;
}

这将编译为(根据ILSpy反编译程序,当禁用?操作符的反编译时)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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行就会被删除。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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``的参数上给出一个参数,那么它也是不正确的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var myObj = myObjCol.FirstOrDefault(x => x.Member);
if (!string.IsNullOrEmpty(myObj.StrProp))
{
    ValidateMyObj(myObj);
    if (ViewBag.IsValid)
        myObj.Exists = true;
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2022-06-16 04:03:55

C#编译器的空状态分析试图根据使用它的上下文跟踪每个变量的空状态。

空状态可能是:

  1. 值已知为空。
  2. 值已知为非空值。
  3. 空状态未知(可能为-空)。

在赋值之后,var myObj = myObjCol.FirstOrDefault(x => x.Member); myObj处于可能-空状态(这实际上取决于框架-参见下面)。

string.IsNullOrEmpty的调用不会更改空状态。string.IsNullOrEmpty方法只是一种普通的方法。如果没有额外的知识,编译器就不知道参数的空状态。

结果是,在.NET 6中,编译器得到了额外的帮助。在.NET 6中,该方法的签名是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static bool IsNullOrEmpty([NotNullWhen(false)] string? value);

然而,在.NET框架中,签名是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static bool IsNullOrEmpty(string value);

NotNullWhen属性告诉编译器,当方法返回false时,传递给string.IsNullOrEmpty的参数不是null。因此,在.NET 6中,您在原始代码中不会收到任何警告,因为编译器在if-块中将空状态更改为null。在.NET Framework4.8(或更早版本)中,它无法做到这一点,因此if块内的空状态与外部状态相同。

令人惊讶的是,在为myObj添加显式null检查之后,squiggly行并没有消失。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (myObj != null && !string.IsNullOrEmpty(myObj?.StrProp)) { ... }

等于

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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方法的返回值。

刚开始的时候你有:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var myObj = myObjCol.FirstOrDefault(x => x.Member);`

显然,这应该将myObj设置为可能-空状态,并在不检查null的情况下取消对变量的引用时发出警告:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (!string.IsNullOrEmpty(myObj.StrProp)) { ... } // should warn: Dereference of a pssibly null reference

当您使用.NET框架时,它似乎不会这样做。但是,当您在.NET 6上时,情况就会发生变化。

当您查看C#和.NET的历史时,可以解释这一点。C# 8是支持可空引用类型的第一个版本。它是在框架的最后一个版本 (4.8)之后不久发布的。

在.NET框架中,签名是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);

在.NET 6中,它是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static TSource? FirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);

这意味着当您在FirstOrDefault<T>框架上调用.NET时,可空上下文是不清楚的,但是在.NET 6上是明确的。

据我所知,C#编译器团队已经决定在上下文不明确时不发出警告,因为这会给出太多的错误警告,并会惹恼太多的人。

话虽如此:如果您想要更可靠的可空性分析,则应该使用框架的一个版本,该版本完全了解空。

票数 1
EN

Stack Overflow用户

发布于 2022-06-13 09:46:20

如果我做对了,那么我认为这是因为IsNullOrEmpty引用的是StrProp,而警告的目标是myObj

澄清:如果myObj为null,则不需要检查StrProp

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72606982

复制
相关文章
将爬取的数据保存到mysql中
      create database scrapy (我新建的数据库名称为scrapy)
py3study
2020/01/19
3.7K0
将存储过程执行的结果保存到临时表
CREATE PROCEDURE Proc1 @a varchar(50) AS SELECT Id, NAME FROM Table1 WHERE NAME=@a GO
全栈程序员站长
2022/07/15
1.7K0
MySQL批量更改数据库表结构字符集
# 根据转换字符集 修改 utf8mb4 ---> utf8mb4_general_ci SELECT CONCAT( 'ALTER TABLE ', TABLE_NAME, ' CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;' ) FROM information_schema.`TABLES` WHERE TABLE_SCHEMA = 'DATABASE_NAME'; # 更改 DATABASE_NAME
用户7657330
2021/12/07
4K0
使用python将csv文件快速转存到mysql
因为一些工作需要,我们经常会做一些数据持久化的事情,例如将临时数据存到文件里,又或者是存到数据库里。
我被狗咬了
2019/09/23
6.3K0
使用python将csv文件快速转存到mysql
JavaScript 使用 for 循环时出现的问题
有一些项目组在定位问题的时候发现,在使用 “for(x in array)” 这样的写法的时候,在 IE 浏览器下,x 出现了非预期的值。
四火
2022/07/15
4K0
JavaScript 使用 for 循环时出现的问题
运行basenji框架时出现的问题
2.分不清哪个是输入,输出是什么。另外每个数据集对应的参数param.json文件对应的什么
bye
2021/03/22
2.4K0
运行basenji框架时出现的问题
mysql数据库(6):将数据装入表中
mysql> INSERT INTO pet -> VALUES('hanhan','川川','hh','f','2021-7-21',NULL);
川川菜鸟
2021/10/18
3.1K0
SQLserver2019当保存时出现不允许保存更改,阻止保存要求重新创建表的更改解决方案。
第一步:工具→选项 2,取消勾选
SingYi
2022/07/14
1.6K0
SQLserver2019当保存时出现不允许保存更改,阻止保存要求重新创建表的更改解决方案。
更改 WordPress 数据库表名前缀
众所周知,WordPress 在安装时候可以选择表名前缀以在同一数据库安装多个WordPress 程序。但是很多空间商,尤其是老外的虚拟主机商 允许建立多个数据库,这样为了便于管理,很多人在安装多个WordPress 程序的时候选择多个数据库而不是修改表名前缀,当然这样的方式也是被提倡的。 所谓天有不测风云,人有旦夕祸福。如若碰到多个数据库合为单个数据库的时候,头疼事情就来了,这种情况往往在从国外往国内搬的时候,国内空间商不知道为何如此吝啬 🙁 table_prefix = ‘wp_’; 改为 既
Denis
2023/04/13
1.6K0
MYSQL分页查询时没有用ORDER BY出现数据重复的问题
产品反馈,用户在使用分页列表时,出现数据重复的问题,查看代码后发现对应的分页SQL并没有使用order by进行排序,但是印象中Mysql的InnoDB引擎会默认按照主键id进行排序,本地测试了一下的确出现了部分数据在不同的页都出现的问题。
翎野君
2023/05/12
1.7K0
MYSQL分页查询时没有用ORDER BY出现数据重复的问题
MySQL更改数据库名
执行上述shell脚本 chmod +x ./test.sh #使脚本具有执行权限 ./test.sh #执行脚本
用户10325771
2023/03/01
5.2K0
java 安装配置时出现的问题
Error: could not open `C:\Program Files\Java\jre6\lib\i386\jvm.cfg') jdkerror  前些日子装了个jdk7试了试,后来做项目需要换成jdk6,安装完jdk6,设置完环境变量后出现问题。运行java -version出现Error: could not open `C:\Program Files\Java\jre7\lib\i586\jvm.cfg'),运行javac -version则是正常的javac 1.6.0_32。googl
Gxjun
2018/03/22
2.2K0
Ubuntu安装时出现黑屏问题的解决
问题描述:Ubuntu使用光盘/USB安装时,出现"install ubuntu/ try ubuntu without installation"选择,但是Enter安装时,显示器显示没有信息,进行休眠
知忆
2021/06/07
13.9K0
【已解决】如果将MySQL数据库中的表生成PDM
有时候,我们需要MySQL数据库中的表生成对应的PDM文件,这里凯哥就讲讲第一种将MySQL数据库的表生成对应的PDM文件。
凯哥Java
2023/07/31
4740
【已解决】如果将MySQL数据库中的表生成PDM
Mysql - 删除表时出现: Cannot delete or update a parent row: a foreign key constraint fails
ERROR 1217 (23000): Cannot delete or update a parent row: a foreign key constraint fails
小菠萝测试笔记
2020/06/09
2K0
ASP.NET将Session保存到数据库中
因为ASP.NET中Session的存取机制与ASP相同,都是保存在进行中, 一旦进程崩溃,所有Session信息将会丢失,所以我采取了将Session信息保存到SQL Server中,尽管还有其它的 几个方式(本文不作介绍),要将Session保存到SQL Server中,需要有以下几个步骤: 1.首先要创建用于保存Session数据的数据库,以命令行的形式用aspnet_regsql.exe来完成,具体命令为 C:\WINDOWS\Microsoft.NET\Framework\v2.0.5072
磊哥
2018/04/26
2.6K0
爬取网站文章将图片保存到本地并将HTML的src属性更改到本地
每次当你爬取一篇文章时,不管是从csdn或者其他网站,基本内容都是保存在一个富文本编辑器中,将内容提取出来还是一个html,保存之后图片还在别人的图片服务器上。我今天要说的就是将图片保存之后并将它的src属性替换成本地的地址。并且以次替换,按照原文章排版顺序替换。
andrew_a
2019/07/30
1.9K0
MYSQL 生产环境字段更改的failed的问题
早上看到微信一个银行的同学问了小问题,希望他不要背锅,具体问题是MYSQL 一个50G的表要更改字段,将一个字段从varchar(3) 改成varchar(6). MYSQL 5.7 官版。因为根据官方和在测试系统测试的结果来看,不应该是缓慢的,应该是很快完成的。
AustinDatabases
2020/06/05
1.9K0
上传文件时出现跨域问题
所以啊,这根本不是跨域的问题,Tomcat默认上传的文件大小就是1MB,你上传的文件超过而已。
乐心湖
2020/07/31
3.6K0
上传文件时出现跨域问题
点击加载更多

相似问题

Regex替换引号之间的值

10

使用regex替换url

12

使用Javascript和Regex替换括号之间的值

14

使用Regex替换url路径

16

使用regex替换URL的位置?

21
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文