Ответ 1
Это работает для любого обнуляемого:
Nullable.Compare(a, b) > 0 ? a : b;
Предположим, что у меня есть два нулевых целых числа:
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;
}
Это кажется слишком неуклюжим (и, возможно, подверженным ошибкам) по моему вкусу. Какой самый простой способ вернуть большее значение, что также учитывает возможность нулевых значений?
Это работает для любого обнуляемого:
Nullable.Compare(a, b) > 0 ? a : b;
В одной строке с использованием оператора нулевой коалесценции:
int? c = a > b ? a : b ?? a;
Эти строки показывают необходимую логику с небольшим трюком:
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;
Это хорошее место для оператора Null coalescing ??
.
Он возвращает первое значение, если значение не равно null
, в противном случае оно возвращает второе значение.
int? c = a > b ? a : b ?? a;
Используя тот факт, что оператор сравнения вернет 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
Короткий вариант:
var result = new[] { a, b }.Max();
Как насчет этого
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;
}
!b.HasValue || a > b ? a : b
Если b
равно null (!b.HasValue
), то a
всегда будет правильным ответом.
Если b
не является нулевым, но a
is, то a > b
будет ложным, а b
будет правильным ответом.
В противном случае это то же самое 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);
Или вы можете изменить параметр метода для приема списка, если вы работаете со значениями, полученными от другого источника.
Я хотел бы добавить, что однолинейные решения здесь хороши. Но для demystify код немного добавит скобки вокруг оператора нулевого коалесцирования
private int? Max(int? a, int? b)
{
return a > b ? a : (b ?? a);
//returns a if bigger else non null prefering b
}
Возвращает a
, если он больше, else return b ?? a
- возвращает не null (или null, если оба значения null) предпочитает b
Вот очень интуитивное и удобочитаемое решение. Это будет работать для любого числа значений, а также для любой допускающей обнуление структуры, например, скажем, int? или DateTime?
Кроме того, если все значения равны нулю, возвращается ноль.
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;
}