为什么 null 在映射到 null 类型和从 null 类型映射时不会导致 NullReferenceException?

Why doesn't a null cause a NullReferenceException when mapping to and from a null type?

提问人:Dev X 提问时间:1/21/2019 最后编辑:Camilo TerevintoDev X 更新时间:1/21/2019 访问量:314

问:

我基本上是在尝试遍历大量数据,并将返回的数据查询转换为视图模型中更有限的对象。

我没有做一大段代码,而是调用 .ForEach(),然后向视图模型的列表中添加一个新条目。

这很好用,但是,有一个属性(地址)是可选的。

当我到达可选项目时,我会得到数据库中的项目是否没有条目。NullReferenceException

代码示例如下:

    var tmp = _context.Person.Include(x => x.Address).ToList();

    tmp.ForEach(x => vm.List.Add(new IndexListItem()
    {
        Name = x.Name,
        Address = x.Address.FirstLine + " " + x.Address.SecondLine,
        ID = x.ID

    }));

从那以后,我从这个网站上的另一个答案中发现,如果我更改地址行,使其显示为:

        Address = x.Address?.FirstLine + " " + x.Address?.SecondLine,

现在,当我在 中点击空条目时,该代码将起作用。tmp

我不明白这一点,因为 上的 Address 属性已经允许 null,而视图模型上的 Address 属性允许 null,那么,为什么更改行突然不返回错误?tmp

此外,我不必这样做的原因是因为这是一个字符串并且字符串已经可以为空吗?x.Address?.FirstLine?

C# LINQ NullReferenceException

评论

0赞 Dev X 1/21/2019
@camiloTerevinto - 我看到了编辑,并且对 linq 感到满意,但是,这与实体框架本身无关。
0赞 Dev X 1/21/2019
@CamiloTerevinto - 虽然它更通用......我没有使用实体框架,虽然我相信我会得到同样的东西,但我使用的是 NHibernate......
0赞 Phil1970 1/21/2019
通常,如果地址是 ,则不需要包含空格的字符串!null
0赞 Dev X 1/21/2019
@Phil1970 - 改天再问一个问题!...井。我想我已经工作了 - 我现在使用 Address = ParseAddress(x.Address) 并在该方法中检查了一个 null,但是,我在这里问的主要问题是让我烦恼的一点,我想要一个答案!

答:

5赞 Alex KeySmith 1/21/2019 #1

在此特定情况下,当您尝试访问父对象本身为 null 的属性时,会导致 null 引用异常。

x.Address.FirstLine

即,在您的情况下,地址为空。

它与您尝试设置的内容(即目标视图模型)无关。

这样做的原因:

x.Address?.FirstLine 

..是因为“在后台”它首先检查是否为 null。如果不是,则返回,如果是,则返回 null。它在语义上等同于:AddressFirstLine

if (x.Address == null)
{
    return null;
}
else
{
    return x.Address.FirstLine;
}

这是一篇关于介绍 ?.C# 中的运算符,用于一些背景阅读: https://blogs.msdn.microsoft.com/jerrynixon/2014/02/26/at-last-c-is-getting-sometimes-called-the-safe-navigation-operator/

评论

0赞 Dev X 1/21/2019
非常感谢 - 扩展该代码和您上面的解释突然使这对我来说有意义。
0赞 Alex KeySmith 1/21/2019
@DevX很高兴能提供帮助:-)
1赞 gilliduck 1/21/2019 #2

您的问题不是 null 并且您正在尝试将其分配给另一个允许 null 的属性,而是您尝试访问为 null 的内容。Address.FirstLine

如果为 null,那么您尝试执行的操作等同于不起作用。没有什么能容纳不住的东西。Address.FirstLinenull.FirstLine

您使用的有效符号仅有效,基本上是说如果不是 null 给我的值,如果它是 null,则给我 null?AddressAddress.FirstLine

2赞 Camilo Terevinto 1/21/2019 #3

我不明白这一点,因为 tmp 上的 Address 属性已经允许 null,而视图模型上的 Address 属性允许 null,那么,为什么更改行突然不返回错误?

您将保存数据与加载数据混为一谈。当您将数据保存到数据库时,是可以接受的,但当您尝试使用数据时,则不可接受。nullnull

null 条件运算符 () 允许您“缩短”语句,它类似于:?.if

Address = x.Address?.FirstLine + " " + x.Address?.SecondLine,

string Address = "";
if (x.Address != null)
{
    Address += x.Address.FirstLine;
}
// ....

此外,虽然与您的问题无关,但您使用的代码非常无效,您正在加载 2 个表来获取几个属性,而您可以直接从数据库中获取这些属性:

var vm = _context.Person
    .Select(x => new IndexListItem
    {
        Name = x.Name,
        Address = x.Address?.FirstLine + " " + x.Address?.SecondLine,
        ID = x.ID
    })
    .ToList();

评论

0赞 Dev X 1/21/2019
非常感谢你 - 我将接受 Alex 的回答,因为它稍微更好地解释了这个问题(而且,这确实是一个我试图了解 Nulls 的问题,而不是加载/保存数据..),但是,我喜欢你关于如何使代码更好的例子 - 所以,谢谢你,一旦我得到 15 个代表,我就会投赞成票。
0赞 Gert Arnold 1/21/2019
不能在表达式中使用 null 传播运算符。但请看这里:没有必要。我们俩都给出了大致相同的答案,因为我不知道这个问题。
0赞 Camilo Terevinto 1/21/2019
@GertArnold 谢谢你。我已经为你投了赞成票,并标记为重复。4 个答案,当你已经回答了......
1赞 Gert Arnold 1/21/2019
好的,谢谢,但我认为这个问题略有不同:这里不是关于 NRE 本身,而是关于操作员的工作。?
0赞 Dev X 1/21/2019
我正在学习,这是一个使用相同代码示例的不同问题......那个答案只是刚刚写的,是的,在你的答案中是一样的——我很感激学习,但在这两个问题中,我都没有要求改进代码。第一个问题是关于代码的修复(我从哪里学到了上课?),这个问题很简单......在代码按原样工作的情况下,为什么可为 null 的对象的行为就像它一样 - 上面已经很好地回答了这个问题。
2赞 Derviş Kayımbaşıoğlu 1/21/2019 #4

x.Address?.FirstLine其中 是 null 传播运算符,这意味着如果为 设置了 .?x.AddressnullnullFirstLine

空传播等效代码

if (x.Address == null)
   return null
else 
   return x.Address.FirstLine

所有引用类型变量都可以为 null。因此,将 null 分配给引用类型始终有效。

string是示例中的引用类型。因此,您不会收到错误,因为是有效的string x = null