使用LINQ的.where子句时出现空引用异常

7

我想从一个可能为空的对象数组中获取属性,但总是得到一个空引用异常。

我如何告诉LINQ,在这种情况下不要处理它或返回一个空字符串?

foreach (Candidate c in candidates) {
   results.Add(new Person 
      { 
         firstName = c.firstname, //ok
         lastName = c.Name, //ok

         // contactItems is an array of ContactItem
         // so it can be null that's why I get null exception 
         // when it's actually null
         phone = c.address.contactItems.Where( ci => ci.contactType == ContactType.PHONE).First().contactText 
      }
   );
}

我也尝试过不使用null。但是我不知道如何告诉LINQ如果数组为空就不进行处理。

phone = c.address.contactItems.Where( ci => ci != null && ci.contactType == ContactType.PHONE).First().contactText

第二个查询应该可以工作,你确定 addresscontactItems 不是 null 吗? - James
你可以尝试使用Single()而不是Where和First。这样更高效,并且允许管理异常。 - ykatchou
将查询放在对象初始化之外有什么问题,从而消除了一次性完成的“需求”?这样做会使你的代码难以阅读。 - J. Steen
6个回答

13

您可以通过?:(三元)运算符检查它是否为null

phone = c.address.contactItems == null ? ""
    : c.address.contactItems.Where( ci => ci.contactType == ContactType.PHONE).First().contactText 

如果由于没有匹配到ContactType.PHONEFirst 抛出异常,那么可以使用DefaultIfEmpty并设置自定义默认值:

c.address.contactItems.Where( ci => ci.contactType == ContactType.PHONE)
                      .DefaultIfEmpty(new Contact{contactText = ""})
                      .First().contactText 

请注意,由于我提供了默认值,First现在不能再抛出异常了。


1
是的,这很好,但如果它不是null,并且找不到一个contactType == PHONE的项目呢? - Arno 2501
从 OP 以防它为空或返回一个空字符串"" - Hamlet Hakobyan
为了防止其他异常,请使用 FirstOrDefault(); 并检查 null 结果,而不是 Where().First(); - Renatas M.
1
非常感谢,这正是我想要的!!!所有的东西加上DefaultIfEmpty! - Arno 2501
这在我的情况下也有效。谢谢。但是,First()与FirstOrDefault(),应该选择哪一个?有建议吗? - Mohammed Hameed
@mdhameed 它们是相同的,不同之处在于 First 如果没有项则会抛出异常,而 FirstOrDefault 在这种情况下返回默认值(对于引用类型为 null)。因此,在确保至少有一个项目时使用 First(例如在我的 DefaultIfEmpty 答案中)。 - Tim Schmelter

2
尝试下面的代码(假设contactText是一个string)。您可能希望标准化公共属性名称的大写方式,以便所有属性名称都以大写字母开头。
foreach (Candidate c in candidates) {
    string contactText =
        c.address.contactItems
            .Where(ci => ci.contactType == ContactType.PHONE)
            .Select(ci => ci.contactText)
            .FirstOrDefault()

    results.Add(
        new Person 
        { 
            firstName = c.firstname,
            lastName = c.Name,
            phone = contactText ?? string.Empty
        });
}

在这种情况下,“.Where”和“.FirstOrDefault”是多余的,可以简化。 - Eonasdan
如果你移除了 WhereFirstOrDefault,它就不能工作。你能详细说明一下你的评论吗? - Adrian Thompson Phillips
可能是 c.address.contactItems.FirstOrDefault(ci => ci.contactType == ContactType.PHONE).Select(ci => ci.contactText)(我认为,没有测试但应该是正确的) - Eonasdan
接近正确,但如果您首先使用 FirstOrDefault,则存在稍后尝试从 null 对象选择属性的风险。 - Adrian Thompson Phillips

1

尝试:

var contact = c.address.contactItems.Where( ci => ci.contactType == ContactType.PHONE).FirstOrDefault();
 phone = contact != null ? contact.contactText : "";

我无法在新的 Response {} 语句内声明变量。 - Arno 2501
不行,但你可以在foreach的范围之外进行。 - J. Steen

1

null 值是 contactType,因此我们添加 (ci.contactType != null)


    var phone = c.address.contactItems.Where( ci => (ci.contactType != null) && ci.contactType == ContactType.PHONE).First().contactText

0

避免在Linq中出现空参数异常,可以像下面这样操作:

 Summaries = (from r in Summaries
              where r.Contains(SearchTerm)
              orderby r
              select r).ToArray();

在这种情况下,如果searchTerm为null,则可以像下面这样检查null表达式。
 Summaries = (from r in Summaries
              where string.IsNullOrEmpty(SearchTerm) ||r.Contains(SearchTerm)
              orderby r
              select r).ToArray();

这对我有效!


0
foreach (Candidate c in candidates) {
results.Add(new Person 
  { 
     firstName = c.firstname, //ok
     lastName = c.Name, //ok

     // contactItems is an array of ContactItem
     // so it can be null that's why I get null exception 
     // when it's actually null
     phone = c.address.contactItems == null
          ? string.Empty
          :c.address.contactItems.Where( ci => ci.contactType == ContactType.PHONE).First().contactText 
  }

); }


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接