【问题标题】:Avoiding SingleOrDefault null reference exception避免 SingleOrDefault 空引用异常
【发布时间】:2019-12-29 17:37:42
【问题描述】:

我正在添加参数以将信息插入数据库并遇到潜在的空引用异常。通过使用SingleOrDefault LINQ 表达式,我认为如果一个实例没有名为“名称”的Tag,那么Tag 将默认为空值。只要实例有某种Tag,情况就是如此。如果实例根本没有标签,则会发生空引用异常。这是一种罕见的情况,但我仍然需要更好的方法来处理它。有没有比捕获异常更好的方法来解决这个问题?

cmd.Parameters.AddWithValue("@Name", runningInstance.Tag.SingleOrDefault(t => t.Key == "Name").Value);

【问题讨论】:

    标签: c# linq


    【解决方案1】:

    唯一的好方法是稍微重构您的查询:

    instance.Tag.Where(t => t.Key == "Name").Select(T => T.Value).SingleOrDefault();
    

    这样就可以了。


    更新:

    我怀疑您的 Tag 属性属于 IDictionary<,> 类型。在这种情况下,检索值的最佳选择是:

    if (instance.Tag.TryGetValue("Name", out TValue value))
    {
        // We have the value in the value. :)
    }
    else
    {
        // We don't.
    }
    

    【讨论】:

      【解决方案2】:

      你为什么不先检查一下标签:

      cmd.Parameters.AddWithValue("@Name",
      runningInstance.Tag==null ? null : runningInstance.Tag.SingleOrDefault(t => t.Key == "Name").Value);
      

      【讨论】:

        【解决方案3】:

        在模型视图控制范式中,模型应该进行尽可能多的处理并包含处理其内部结构的逻辑。

        我建议向 runningInstance 添加一个方法,例如 GetTag,它将检查对象是否具有标签,如果有,则返回具有给定键的标签。此方法将执行所需的所有错误检查,并且在您需要从实例获取标记的任何其他情况下都可以重新使用。

        【讨论】:

          【解决方案4】:

          C# 的行为是在instance 为空时像instance.MethodName() 一样在调用时抛出NullReferenceException
          对于扩展方法(如SingleOrDefault),技术上可以编写在instance 为空的情况下不会抛出NullReferenceException 的方法。例如:

          public static class MyExtensions
          {
              public static string SmartToString(this object instance)
              {
                  if(instance == null)return "";
                  return instance.ToString()
              }
          }
          

          虽然这是非常不常见的方法,因为它通常会让阅读这样的代码的人大吃一惊:

          var instance = null;
          // .. several lines later
          instance.SmartToString();// why exception is not thrown?? how does this code work at all? ... oh, I see ... that's lame ..
          

          回到您的问题。 SingleOrDefault 的实现方式应如此:如果 instance 为 null - 则引发异常。这就是为什么你必须确保Tag 不为空,代码如下:

          if(runningInstance.Tag != null)
          {
              cmd.Parameters.AddWithValue("@Name", runningInstance.Tag.SingleOrDefault(t => t.Key == "Name").Value);
          }
          else
          {
              Console.WriteLine("Tag is null!");
          }
          

          希望对您有所帮助。

          【讨论】:

            【解决方案5】:

            我有以下非常通用的扩展方法:

            public static TResult ValueOrDefault<T, TResult>(this T obj, System.Func<T, TResult> getter)
            {
                return obj != null ? getter(obj) : default(TResult);
            }
            
            public static TResult ValueOrDefault<T, TResult>(this T obj, System.Func<T, TResult> getter, TResult Default)
            {
                return obj != null ? getter(obj) : Default;
            }
            

            然后你这样称呼它:

            instance.Tag.SingleOrDefault(t => t.Key == "Name").ValueOrDefault(x => x.Value);
            

            英文的意思是:从枚举中获取单个值。如果该值不为空,则对其调用指定的选择器函数 (x => x.Value) 并返回该值。如果为 null,则返回该类型的默认值(或者您也可以指定另一个所需的默认值,如果您愿意)。

            我喜欢这种语法,因为它很清楚你想要发生什么,最后加上了 null 处理。

            【讨论】:

            • 这根本不会编译,因为TResult 不是class。它很容易成为一个结构。
            • 正如我已经告诉你的那样,这不会编译。我看到至少两个错误。
            • @AgentFire:对,忘了 C# 不允许默认的空可选参数。已在代码中更新。
            • @AgentFire:现在编译错误已修复,我希望你能删除该反对票。
            • 呵呵,你会在 IDE 中检查自己的代码吗? ;) 那仍然无法编译。
            【解决方案6】:

            编辑:我刚刚看到这个问题已经有几年的历史了,所以对于最终来到这里的人来说,这是一个新的问题。

            您可以使用 null 条件运算符 (?.),如果要检查的对象为 null,则返回 null;如果对象不为 null,则返回属性值。从 C# 6.0 开始可用。

            参考:https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-conditional-operators

            你的例子:

            cmd.Parameters.AddWithValue("@Name", runningInstance.Tag.SingleOrDefault(t => t.Key == "Name")?.Value);
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2012-01-31
              • 2010-09-29
              • 2011-10-01
              • 2014-07-03
              • 2015-11-25
              • 2019-04-24
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多