【问题标题】:Take the greater of two nullable values取两个可为空值中的较大者
【发布时间】:2015-05-01 10:14:09
【问题描述】:

假设我有两个可以为空的整数:

int? a = 10;
int? b = 20;

我想取最大的非空值,这样如果两个值都为空,则结果为空。

我可以写一些冗长的东西,例如:

int? max;
if (a == null)
{
    max = b;
}
else if (b == null)
{
    max = a;
}
else
{
    max = a > b ? a : b;
}

这对我来说有点太笨重(而且可能容易出错)。返回较大值的最简单方法是什么,这也解释了空值的可能性?

【问题讨论】:

    标签: c# nullable


    【解决方案1】:

    在一行中使用空合并运算符:

    int? c = a > b ? a : b ?? a;
    

    【讨论】:

      【解决方案2】:

      这适用于任何可为空的:

      Nullable.Compare(a, b) > 0 ? a : b;
      

      【讨论】:

      • 绝对是最好的解决方案,所以 +1,除非它显然不适用于 any 可为空的......
      • 我也不得不查看Nullable.Compare 的行为,看看它做了什么。我发现这个解决方案的可读性低于大多数(所有)其他解决方案,因为它需要查找文档才能理解。但是,我敢肯定不同的人会记住不同的东西,所以也许这就是我。
      【解决方案3】:

      这些行通过一个小技巧展示了必要的逻辑:

      if (a == null) return b; // handles b== null also
      if (b == null) return a;
      // now a!=null, b!=null
      return Math.Max(a.Value, b.Value);
      

      或者在一行中使用?:(完全一样的逻辑)

       return a == null ? b : b == null ? a : Math.Max(a.Value, b.Value);
      


      编辑

      虽然上面的答案对于教育目的很有趣,但它不是解决这个问题的推荐方法。推荐的方法是不要重新发明轮子,而是找到匹配的轮子:

      正如@roman 指出的那样,存在一个Nullable.Compare() 方法,这使得它更具可读性:

      return Nullable.Compare(a, b) > 0 ? a : b;
      

      【讨论】:

      • 你的意思是Math.Max
      • 可能已经在使用 Visual Studio 2015 及其 static usings
      • @Yuval 代码更多是作为伪代码,但现在添加了命名空间 - 谢谢
      • @WaiHaLee,我认为我们应该等到VS2015完全发布。
      【解决方案4】:

      这是Null coalescing operator??的好地方。
      如果值不是null,则返回第一个值,否则返回第二个值。

      int? c = a > b ? a : b ?? a;
      

      Proof here

      如果任一值是null,比较运算符将返回false,则表达式将为您提供所需的结果:

      a        | b        || a>b | a   | b??a | a>b ? a : b??a
      --------------------||----------------------------------
      > b      | NOT NULL ||  T  | a   | --   | a
      ≤ b      | NOT NULL ||  F  | --  | b    | b
      NOT NULL | NULL     ||  F  | --  | a    | a
      NULL     | NOT NULL ||  F  | --  | b    | b
      NULL     | NULL     ||  F  | --  | NULL | NULL
      

      【讨论】:

        【解决方案5】:

        一个简短的版本是:

        var result = new[] { a, b }.Max();
        

        【讨论】:

        • 虽然是一个答案,但您真的想分配一个匿名类型的单值数组来找出哪个 int 更大吗?
        • yes :) C# 的主要目标是编写安全且简短的代码,而不是高性能代码。编译器完成其余的工作。编译器在这里创建一个 int?数组,没有匿名类型。与此处介绍的其他方法相比,性能大约差 2 倍。无论如何,如果您想编写高性能代码,请尽量避免可空类型。
        • 我非常感谢这个答案,因为它既简短又富有创意,但我仍然认为安全和简短的代码不会降低性能(通常)。如果您需要处理必须使用可空类型(存在性能缺陷)的情况,那么根本就没有足够的理由忘记性能。所以,当你说 C# 不是为了编写高性能代码时,我不同意你的看法。您可能无法达到本机代码的速度,但如果认真对待,可以通过一些最佳实践和编译器优化友好代码显着提高性能。
        【解决方案6】:

        这个怎么样

         private int? Greater(int? a, int? b)
         {
           if(a.HasValue && b.HasValue)
            return a > b ? a : b;
        
           if(a.HasValue)
             return a;
           if(b.HasValue)
             return b;
        
           return null;
          }
        

        或更简洁:

         private int? Greater(int? a, int? b)
         {
           if(a.HasValue && b.HasValue)
            return a > b ? a : b;
        
           return a.HasValue ? a : b;
          }
        

        【讨论】:

        【解决方案7】:
        !b.HasValue || a > b ? a : b
        

        如果 b 为空 (!b.HasValue),则 a 始终是正确答案。

        如果b 不为空但a 为空,那么a > b 将为假,b 将是正确答案。

        否则它与不可为空的整数将具有相同的a > b ? a : b

        【讨论】:

          【解决方案8】:

          如何让一个方法能够处理尽可能多的可为空值:

          public static int? GetLargestNonNull(params int?[] values)
          {
              IEnumerable<int?> nonNullValues = values.Where(v => v.HasValue);
          
              if (nonNullValues.Any())
              {
                  return nonNullValues.Select(v => v.Value).Max();
              }
          
              return null;
          }
          

          并像这样使用:

          int? result = GetLargestNonNull(a, b);
          

          除了这个还可以处理:

          int? result = GetLargestNonNull(a, b, c, d, e, f);
          

          如果您正在使用从其他来源收到的值,您也可以更改方法参数以接受列表。

          【讨论】:

          • 我宁愿花 2 分钟来写这篇文章,这样我的其余代码也能很好地阅读。谁愿意处理试图理解别人复杂的三元运算符:)
          【解决方案9】:

          我想补充一点,这里的单行解决方案很好。但是为了揭开代码一点点在空合并运算符

          周围添加一个方括号
          private int? Max(int? a, int? b)
          {
              return a > b ? a : (b ?? a);
              //returns a if bigger else non null prefering b
          }
          

          如果更大则返回a,否则返回b ?? a - 返回非空(如果两者都为空,则返回空)首选b

          【讨论】:

            【解决方案10】:

            这是一个非常直观易读的解决方案。这将适用于任意数量的值以及任何可为空的结构,例如 int?还是日期时间?

            此外,如果所有值都为 null,则返回 null。

             public static T? GetGreatestOrDefault<T>(IEnumerable<T?> values) where T : struct
                {
                    var any = values.Any(a => a.HasValue);
            
                    if (!any) return null;
            
                    var firstOrDefault = values.Where(v => v.HasValue)
                        .Select(v => v.Value)
                        .OrderByDescending(o => o)
                        .FirstOrDefault();
            
                    return firstOrDefault;
                }
            

            或者您可能想要执行以下操作:

              public static T? GreatestOrDefault<T>(this IEnumerable<T?> values) where T : struct
                    {
                        var any = values.Any(a => a.HasValue);
            
                        if (!any) return null;
            
                        var firstOrDefault = values.Where(v => v.HasValue).Max();
            
                        return firstOrDefault;
                    }
            

            【讨论】:

            • 如果您要使用IEnumerable&lt;T&gt;,在初始集合上使用Max() 不是更简单的方法吗?
            • 否,因为 Max() 不适用于 DateTime 和其他几个结构。
            • 你可以用几行代码自己测试一下; Max()DateTime? 值完全一样。在内部,它只是使用默认的比较方法。
            • 查看这个答案:stackoverflow.com/a/29985658/138578这是使用Max()的最简单方法
            • @PaulTurner 虽然它有效:1)它不是很直观; 2)对于没有经验的开发人员来说,它的可读性并不高。简而言之,并不是每个开发人员都能轻松地对其进行干代码审查,这对公司来说可能代价高昂。
            猜你喜欢
            • 2017-02-13
            • 1970-01-01
            • 2020-07-08
            • 1970-01-01
            • 1970-01-01
            • 2015-04-16
            • 2014-05-23
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多