#c# #coalesce
#c# #объединение
Вопрос:
Я хочу сделать что-то вроде этого:
public override int CompareTo (Foo rhs)
{
return Bar.CompareTo(rhs.Bar) ??
Baz.CompareTo(rhs.Baz) ??
Fuz.CompareTo(rhs.Fuz) ?? 0;
}
Это работает не так, как написано; есть ли какой-нибудь минимальный обходной путь, чтобы заставить его работать? По сути, я хочу, чтобы 0 цеплялось до ненулевого значения (или конца цепочки).
Комментарии:
1. Я не знаю ответа на ваш вопрос, но если для определения возвращаемого значения можно использовать три значения, вы можете рассмотреть возможность перепроектирования вашего класса.
2. @DJ
CompareTo
связан с сортировкой; на самом деле это довольно распространенное явление — например, сортировка по фамилии — если это то же самое, затем сортировка по имени, затем, если все равно равно, сортировка по некоторому уникальному идентификатору (например, идентификатор базы данных).3.
CompareTo
Возвращают ли методы null???
работает только с null.4. @Marc Интересно, я раньше с этим не сталкивался. Я вижу, что compareTo является частью класса IComparable. Мне нужно будет прочитать больше, чтобы получить представление. Спасибо
Ответ №1:
Не поддерживается языком. Но вы можете написать небольшой помощник, подобный этому:
public override int CompareTo (Foo rhs)
{
return FirstNonZeroValue(
() => Bar.CompareTo(rhs.Bar),
() => Baz.CompareTo(rhs.Baz),
() => Fuz.CompareTo(rhs.Fuz));
}
private int FirstNonZeroValue(params Func<int>[] comparisons)
{
return comparisons.Select(x => x()).FirstOrDefault(x => x != 0);
}
Комментарии:
1. Тьфу. Подобные решения иллюстрируют, насколько отстойно, что у нас не может быть глобальных функций.
2. Всегда можно поместить его в функцию расширения, если он часто используется и т. Д.
3. Расширение для чего? Int32? Это вообще возможно?
4. Вы можете сделать это на int, хотя я бы не рекомендовал это. Вы могли бы сделать это
IComparable
, чтобы разрешить код, подобныйthis.FirstNonZeroValue( ... )
или onIEnumerable<Func<int>>
(но тогда вы теряете удобство использования спискаparams
параметров.5. Ах, я не думал делать это в интерфейсе. Это здорово!
Ответ №2:
В принципе, нет, но было бы неплохо, если бы это было так (IIRC, Джон подробно упомянул аналогичную идею в C #). Вероятно, вы могли бы связать условные выражения, но я склонен просто использовать:
int delta = Bar.CompareTo(rhs.Bar);
if(delta == 0) delta = Baz.CompareTo(rhs.Baz);
if(delta == 0) delta = Fuz.CompareTo(rhs.Fuz);
return delta;
Ответ №3:
Не совсем, ??
работает только для нулевых значений (ссылочных типов или обнуляемых структур)
int i;
i = Bar.CompareTo(rhs.Bar);
if (i != 0) return i;
i = Baz.CompareTo(rhs.Baz);
if (i != 0) return i;
i = Fuz.CompareTo(rhs.Fuz);
if (i != 0) return i;
return 0;
Ответ №4:
Если короткое замыкание не требуется, вы можете выполнить некоторую двоичную арифметику. Не требуется, чтобы результаты сравнения были -1, 0 или 1, но они должны быть < 0, = 0, > 0 . Таким образом, используя 2 ^ n в качестве коэффициента, где n — приоритет, вы можете суммировать результаты сравнения и получить число> 0 или<0 по мере необходимости.
Итак, используйте
return 4 * Bar.CompareTo(rhs.Bar)
2 * Baz.CompareTo(rhs.Baz)
1 * Fuz.CompareTo(rhs.Fuz);
Ответ №5:
Вы также можете изменить ноль на null с помощью функции и получить отложенную оценку без Func<>
:
public override int CompareTo (Foo rhs) =>
Bar.CompareTo(rhs.Bar).GetNullIfZero() ??
Baz.CompareTo(rhs.Baz).GetNullIfZero() ??
Fuz.CompareTo(rhs.Fuz);
public static class IntExtensions
{
public static int? GetNullIfZero(this int i) => i == 0 ? null : (int?)i;
}
Существует аналогичный способ для ленивых операций со строковыми цепочками GetNullIfEmpty
GetNullIfWhiteSpace
с расширениями a и .