Есть ли «противоположность» нулевому оператору объединения? (… На любом языке?)

93

нулевое объединение примерно означает return x, unless it is null, in which case return y

Мне часто нужно return null if x is null, otherwise return x.y

я могу использовать return x == null ? null : x.y;

Неплохо, но то, nullчто посередине, меня всегда беспокоит - кажется лишним. Я бы предпочел что-то вроде return x :: x.y;, где то, что следует за ::, оценивается, только если то, что ему предшествует, не оценивается null.

Я считаю это почти противоположностью слияния нулей, как бы смешанной с краткой встроенной нулевой проверкой, но я [ почти ] уверен, что такого оператора в C # нет.

Есть ли в других языках такой оператор? Если да, то как это называется?

(Я знаю, что могу написать для этого метод на C #; я использую return NullOrValue.of(x, () => x.y);, но если у вас есть что-нибудь получше, я тоже хотел бы это увидеть.)

Джей
источник
Некоторые просили что-то вроде x? .Y в C #, но ничего подобного не существует.
Энтони Пеграм
1
@ Энтони О, это было бы прекрасно. Спасибо.
Джей
2
В C ++ это было бы достаточно просто выразить как return x ? x.y : NULL. Ура для преобразования типов указателей в логические!
Фил Миллер
1
@Novelocrat, что меня больше всего раздражает в C #, это то, что они не следовали букве C, если это if (something) = true, за исключением тех случаев, когда if (0, false, null)
Крис Марисич,
2
@Chris: это не точное утверждение о C. Если у вас есть нескалярная переменная (например, структура), вы не можете использовать ее в условии.
Фил Миллер

Ответы:

62

В Groovy есть нулевой безопасный оператор разыменования (?.) ... Я думаю, это то, что вам нужно.

(Его также называют оператором безопасной навигации .)

Например:

homePostcode = person?.homeAddress?.postcode

Это даст нуль , если person, person.homeAddressили person.homeAddress.postcodeравно нулю.

(Теперь это доступно в C # 6.0, но не в более ранних версиях)

Джон Скит
источник
1
В Groovy также есть «оператор Элвиса», который позволяет использовать значения по умолчанию, отличные от null, например:def bar = foo ?: "<null>"
Craig Stuntz
28
Я назначаю эту функцию для C # 5.0. Меня не интересует, что такое Groovy на самом деле, но он достаточно интуитивно понятен, чтобы использовать его на любом языке.
Дьёрдь Андрасек
Однако сейчас он добавляется в C # 6, ура.
Джефф Меркадо,
1
Safe-навигация работа для тривиального примера отвечала, но вам все равно придется использовать тройной оператор , если вы собираетесь использовать значение ненулевого в вызове функции, например: Decimal? post = pre == null ? null : SomeMethod( pre );. Было бы неплохо, если бы я мог уменьшить его до "Decimal? Post = pre :: SomeMethod (pre);"
Дай
@Dai: Учитывая количество перестановок, которые вам могут понадобиться, я достаточно доволен тем, что у нас есть.
Джон Скит
36

Мы рассматривали возможность добавления? в C # 4. Это не помогло; это функция «приятно иметь», а не «обязательно». Мы еще раз рассмотрим его для гипотетических будущих версий языка, но на вашем месте я бы не стал задерживать дыхание. С течением времени это вряд ли станет более важным. :-)

Эрик Липперт
источник
Влияет ли простота реализации функции на решение о ее добавлении? Я спрашиваю об этом, потому что реализация этого оператора кажется довольно прямым и простым дополнением к языку. Я не эксперт (просто недавний выпускник с большой любовью к C #), поэтому, пожалуйста, поправьте меня!
Ник Струпат,
14
@nick: Конечно, реализация лексического анализатора и парсера занимает пять минут. Затем вы начинаете беспокоиться о таких вещах, как "хорошо ли с этим справляется механизм IntelliSense?" и "как с этим справляется система исправления ошибок, когда вы ввели? но еще не сделали.?" и сколько разных вещей делает "." в любом случае имеют в виду на C #, и сколько из них заслуживают? перед этим, и какими должны быть сообщения об ошибках, если вы ошиблись, и сколько тестирования потребуется этой функции (подсказка: много). И как мы собираемся задокументировать это и сообщить об изменении, и ...
Эрик Липперт
3
@nick: Отвечая на ваш вопрос - да, стоимость реализации - фактор, но небольшой. На том уровне, над которым мы работаем, нет дешевых функций, только более или менее дорогие функции. Работа разработчиков на пять долларов, чтобы заставить парсер работать в случае правильного кода, может легко превратиться в десятки тысяч долларов на проектирование, внедрение, тестирование, документацию и обучение.
Эрик Липперт
11
@EricLippert Я думаю, что это было бы одним из ГЛАВНЫХ «приятных моментов», которые сделали C # таким успешным, а также отлично послужили бы миссии по созданию максимально лаконичного и выразительного кода!
Константин
Я хотел бы, чтобы это было добавлено, я ДЕЙСТВИТЕЛЬНО хочу, чтобы оператор elvis: stackoverflow.com/questions/2929836/…
Крис Марисич
16

Если у вас есть особый вид логической логики короткого замыкания, вы можете сделать это (пример javascript):

return x && x.y;

Если xимеет значение null, он не будет оцениваться x.y.

Эрик
источник
3
Кроме этого, также происходит короткое замыкание на 0, "" и NaN, так что это не противоположность ??. Это противоположность ||.
ritaj
7

Было просто правильно добавить это в качестве ответа.

Я предполагаю, что причина, по которой в C # нет такой вещи, заключается в том, что, в отличие от оператора объединения (который действителен только для ссылочных типов), обратная операция может дать либо ссылочный, либо тип значения (то есть class xс элементомint y - поэтому, к сожалению, это будет непригоден для использования во многих ситуациях.

Однако я не говорю, что не хотел бы это видеть!

Потенциальным решением этой проблемы было бы автоматическое преобразование оператором выражения типа значения с правой стороны в значение, допускающее значение NULL. Но тогда у вас есть проблема, что x.yгде y - int, на самом деле вернет an, int?что было бы болью.

Другим, возможно, лучшим решением было бы, чтобы оператор возвращал значение по умолчанию (т. Е. Ноль или ноль) для типа с правой стороны, если выражение слева имеет значение NULL. Но тогда у вас возникают проблемы с различением сценариев, в которых фактически считывалось значение ноль / ноль x.yили было ли оно предоставлено оператором безопасного доступа.

Андраш Золтан
источник
когда я впервые прочитал вопрос ОП, эта проблема возникла у меня в голове, но я не мог понять, как ее сформулировать. Это очень серьезная проблема. +1
rmeador
это не серьезная проблема. Просто используйте int?в качестве возвращаемого значения по умолчанию, и пользователь может изменить его на int, если захочет. Например, если я хочу вызвать какой-нибудь метод Lookupсо значением по умолчанию -1, в случае, если одна из моих ссылок null:foo?.Bar?.Lookup(baz) ?? -1
Qwertie
Как насчет того, чтобы ?. :быть тернарным оператором, где правая часть должна быть того же типа, что и соответствующий член, указанный в середине?
supercat
6

В Delphi есть оператор: (а не.), Который является нулевым.

Они думали добавить?. оператор в C # 4.0, чтобы сделать то же самое, но с блокировкой.

А пока есть IfNotNull (), который царапает, что чешется. Это конечно больше чем? или:, но он позволяет вам составить цепочку операций, которая не вызовет у вас исключение NullReferenceException, если один из членов имеет значение null.

48клок
источник
Не могли бы вы привести пример использования этого оператора?
Макс Кэрролл,
1
Это ?.функция языка C # 6.0, и да, она делает именно то, что здесь попросил OP. Лучше поздно, чем никогда ^^
T_D
3

В Haskell вы можете использовать >>оператор:

  • Nothing >> Nothing является Nothing
  • Nothing >> Just 1 является Nothing
  • Just 2 >> Nothing является Nothing
  • Just 2 >> Just 1 является Just 1
dave4420
источник
3

У Haskell есть fmap, что в данном случае, я думаю, эквивалентно Data.Maybe.map. Haskell является чисто функциональным, поэтому то, что вы ищете, будет

fmap select_y x

Если xесть Nothing, это возвращается Nothing. Если xесть Just object, это возвращается Just (select_y object). Не так красиво, как точечная нотация, но, учитывая, что это функциональный язык, стили разные.

Норман Рэмси
источник
2

PowerShell позволяет ссылаться на свойства (но не вызывать методы) по нулевой ссылке, и она вернет значение NULL, если экземпляр имеет значение NULL. Сделать это можно на любой глубине. Я надеялся, что динамическая функция C # 4 будет поддерживать это, но это не так.

$x = $null
$result = $x.y  # $result is null

$x = New-Object PSObject
$x | Add-Member NoteProperty y 'test'
$result = $x.y  # $result is 'test'

Это некрасиво, но вы можете добавить метод расширения, который будет работать так, как вы описываете.

public static TResult SafeGet<T, TResult>(this T obj, Func<T, TResult> selector) {
    if (obj == null) { return default(TResult); }
    else { return selector(obj); }
}

var myClass = new MyClass();
var result = myClass.SafeGet(x=>x.SomeProp);
Джош
источник
2
public class ok<T> {
    T s;
    public static implicit operator ok<T>(T s) { return new ok<T> { s = s }; }
    public static implicit operator T(ok<T> _) { return _.s; }

    public static bool operator true(ok<T> _) { return _.s != null; }
    public static bool operator false(ok<T> _) { return _.s == null; }
    public static ok<T> operator &(ok<T> x, ok<T> y) { return y; }
}

Мне часто нужна такая логика для строк:

using ok = ok<string>;

...

string bob = null;
string joe = "joe";

string name = (ok)bob && bob.ToUpper();   // name == null, no error thrown
string boss = (ok)joe && joe.ToUpper();   // boss == "JOE"
Дж. Брайан Прайс
источник
1

Создайте где-нибудь статический экземпляр вашего класса со всеми правильными значениями по умолчанию для членов.

Например:

z = new Thingy { y=null };

тогда вместо твоего

return x != null ? x.y : null;

ты можешь написать

return (x ?? z).y;
Ник
источник
Иногда я использую эту технику (например, (x ?? "") .ToString ()), но она практична только для некоторых типов данных, таких как POD и строки.
Qwertie
1

Это добавляется в C # vNext (C # на базе Roslyn, выпуски с Visual Studio 2014).

Это называется нулевым распространением и указывается здесь как завершенное. https://roslyn.codeplex.com/wikipage?title=Language%20Feature%20Status

Он также указан здесь как полный: https://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/3990187-add-operator-to-c

Мика Золту
источник
1

Так называемый «нулевой условный оператор» был введен в C # 6.0 и Visual Basic 14.
Во многих ситуациях он может использоваться как полная противоположность оператору объединения с нулем:

int? length = customers?.Length; // null if customers is null   
Customer first = customers?[0];  // null if customers is null  
int? count = customers?[0]?.Orders?.Count();  // null if customers, the first customer, or Orders is null

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

Jpsy
источник