取两个可为空的值中的较大者

Take the greater of two nullable values

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

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;
}

我觉得这有点太笨重(而且可能容易出错)。 return 更大值的最简单方法是什么,这也说明了空值的可能性?

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

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;

这个怎么样

 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;
  }

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

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

一个简短的版本是:

var result = new[] { a, b }.Max();
!b.HasValue || a > b ? a : b

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

如果 b 不为空而 a 为空,则 a > b 为假,b 为正确答案。

否则它与不可为 null 的整数所具有的 a > b ? a : b 相同。

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

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);

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

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

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

Proof here

使用比较运算符将 return false 如果任一值是 null,表达式将给出所需的结果:

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

这适用于任何可空值:

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

我想补充一点,这里的 one-line 解决方案很好。但是为了 demystify 代码在 null coalescing operator

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

Returns a 如果它更大,否则 return b ?? a - returns 非空(如果都为空则为空)更喜欢 b

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

此外,如果所有值都为空,则它 returns 为空。

 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;
        }