【问题标题】:Good way to handle NullReferenceException before C# 6.0在 C# 6.0 之前处理 NullReferenceException 的好方法
【发布时间】:2015-10-13 08:38:55
【问题描述】:

我下面的代码给了我一个NullReferenceException 并且堆栈跟踪告诉我问题出在Count 方法中,所以我很确定在某些时候foobarbaz 是@ 987654326@.

我的代码:

IQueryable<IGrouping<string, Referral>> queryable= ...;
var dict = queryable.ToDictionary(g => g.Key.ToString(),
                                  g => g.Count(r => r.foo.bar.baz.dummy == "Success"));

我想知道处理null 案件的简洁方法是什么。 我了解到在 C# 6.0 中我可以做 foo?.bar?.baz?.dummy,但是我正在从事的项目不是 C# 6.0

【问题讨论】:

  • 你可以有一个g.Count() 的表达式主体,但是引入了空条件运算符,因为这样做的过程非常冗长。所以不幸的是,我认为你不会逃脱它
  • 您使用的是哪个版本?或者您正在寻找任何 6.0 之前的解决方案?
  • 这可能有点丑但是g.Count(r =&gt; r.foo == null ? false : r.foo.bar == null ? false : r.foo.bar.baz == null ? false : r.foo.bar.baz.dummy == "Success") ?
  • 您应该找出哪个为空以及为什么。不要只是隐藏问题。 “只是做......”这可能隐藏了一个真正的错误。
  • @usr AFAIK 没有IQueryable&lt;T&gt;.ToDictionary,因此选择了IEnumerable&lt;T&gt; 重载。

标签: c# entity-framework linq


【解决方案1】:

.Count(r => r.foo != null && 
            r.foo.bar != null && 
            r.foo.bar.baz != null && 
            r.foo.bar.baz.dummy == "Success")

对于复杂的构造,如上面的构造,引入了空传播运算符。

此外,您还可以将表达式重构为私有方法:

private Expression<Func<Referral, bool>> Filter(string value)
{
    return r => r.foo != null && 
                r.foo.bar != null && 
                r.foo.bar.baz != null && 
                r.foo.bar.baz.dummy == value;
}

并按如下方式使用:

g => g.Count(Filter("Success"))

【讨论】:

    【解决方案2】:

    您可以使用以下扩展方法。

    public static TResult With<TInput, TResult>(this TInput o, Func<TInput, TResult> evaluator)
        where TResult : class
        where TInput : class
    {
        return o == null ? null : evaluator(o);
    }
    
    public static TResult Return<TInput, TResult>(this TInput o, Func<TInput, TResult> evaluator, TResult failureValue)
        where TInput : class
    {
        return o == null ? failureValue : evaluator(o);
    }
    

    它们的组合为您提供了一个很好的、可读的 API 来处理nulls

    return foo
        .With(o => o.bar)
        .With(o => o.baz)
        .Return(o => o.dummy, null);
    

    【讨论】:

    • 虽然这通常是一个好主意,但它不能与IQueryable 配合得很好。 LINQ 无法理解这一点,因此会在客户端而不是数据库上执行查询。
    • @Georg 使用转换为 SQL 的 LINQ 提供程序,您通常可以只编写 foo.bar.baz.dummy 而不会得到任何 NullReferenceException,因此无论如何您都不需要这个 With 帮助程序。跨度>
    • @hvd 是的,因为您通常可以在数据库级别设置是否应传播 NUL 值,这也是默认行为。但是,最好不需要了解您将获得什么数据源。
    • @Georg 理论上同意,但我认为这不切实际。提供者之间已经存在许多差异,这些差异使相同的查询根据提供者的不同而不同地执行。根据我的经验,只有最简单最直接的过滤器和预测才能真正与提供者无关。但如果你的经历不同,那对你有好处,我明白你为什么要保持这种状态。
    【解决方案3】:

    问题在于 ToDictionary 方法实际上并没有在可查询对象上完成 - 相反,您获取整个集合,并在您的应用程序中进行聚合,而不是在数据库服务器上。

    所以不要直接使用ToDictionary,而是先使用Select

    IQueryable<IGrouping<string, Referral>> queryable= ...;
    var dict = queryable.Select(g => new { Key = g.Key.ToString(),
                                  Count = g.Count(r => r.foo.bar.baz.dummy == "Success") })
                        .ToDictionary(i => i.Key, i => i.Count);
    

    这将确保聚合是在数据库中完成的(您不关心那些空值)而不是在 C# 代码中(您会得到 NullReferenceException)。

    当然,这假设您使用的可查询对象是数据库查询(或者,更准确地说,是支持聚合并具有类似 ANSI SQL 的 NULL 语义的可查询对象)。如果您有不同的自定义可查询对象,它不会有帮助(除非您自己显式添加这些 NULL 语义)。

    【讨论】:

      【解决方案4】:
      // if null, use null 
      if(objectvariable == null) 
      {
       // throw exception 
      }
      
      // if not null
      if(objectvariable != null) 
      {
       // continue  
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-07-22
        • 2011-03-08
        • 1970-01-01
        • 2014-02-14
        相关资源
        最近更新 更多