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

нулевое объединение примерно переводится как 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);, но если у вас есть что-нибудь получше, я тоже хотел бы это увидеть.)


person Jay    schedule 28.05.2010    source источник
comment
Некоторые просили что-то вроде x? .Y в C #, но ничего подобного не существует.   -  person Anthony Pegram    schedule 28.05.2010
comment
@ Энтони О, это было бы прекрасно. Спасибо.   -  person Jay    schedule 28.05.2010
comment
В C ++ это достаточно просто выразить как return x ? x.y : NULL. Ура для преобразования типов указателей в логические!   -  person Phil Miller    schedule 28.05.2010
comment
@Novelocrat - одна из вещей, которая меня больше всего раздражает в C #, это то, что они не следовали букве C, если это if (something) = true, кроме тех случаев, когда это if (0, false, null)   -  person Chris Marisic    schedule 28.05.2010
comment
@Chris: это не точное утверждение о C. Если у вас есть нескалярная переменная (например, структура), вы не можете использовать ее в условии.   -  person Phil Miller    schedule 28.05.2010
comment
Если бы он был реализован, я думаю, что хорошим синтаксисом было бы x ?=> y: вернуть null, если x равно null, в противном случае вернуть y   -  person Andy    schedule 19.02.2014
comment
Предложения по функциям: return x == null ? null : Whatever(x);: visualstudio.uservoice.com/forums/121579-visual-studio/ return x == null ? null : x.Property;: visualstudio.uservoice.com/forums/121579-visual-studio/   -  person JohnLBevan    schedule 14.07.2014


Ответы (12)


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

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

Например:

homePostcode = person?.homeAddress?.postcode

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

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

person Jon Skeet    schedule 28.05.2010
comment
Groovy также имеет оператор Elvis, который позволяет использовать значения по умолчанию, отличные от null, например: def bar = foo ?: "<null>" - person Craig Stuntz; 28.05.2010
comment
Я назначаю эту функцию для C # 5.0. Я не знаю и не забочусь о том, что такое Groovy на самом деле, но он достаточно интуитивно понятен, чтобы использовать его на любом языке. - person György Andrasek; 28.05.2010
comment
Однако сейчас он добавляется в C # 6, ура. - person Jeff Mercado; 29.06.2015
comment
Безопасная навигация работает для представленного тривиального примера, но вам все равно нужно использовать тернарный оператор, если вы собираетесь использовать ненулевое значение в вызове функции, например: Decimal? post = pre == null ? null : SomeMethod( pre );. Было бы неплохо, если бы я мог уменьшить его до десятичного числа? сообщение = pre :: SomeMethod (предварительно); - person Dai; 28.07.2016
comment
@Dai: Учитывая количество перестановок, которые вам могут понадобиться, я достаточно доволен тем, что у нас есть. - person Jon Skeet; 28.07.2016

ОБНОВЛЕНИЕ: запрошенная функция была добавлена ​​в C # 6.0. Исходный ответ от 2010 года ниже следует рассматривать только как имеющий исторический интерес.


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

person Eric Lippert    schedule 29.05.2010
comment
Влияет ли простота реализации функции на решение о ее добавлении? Я спрашиваю об этом, потому что реализация этого оператора кажется довольно прямым и простым дополнением к языку. Я не эксперт (просто недавний выпускник с большой любовью к C #), поэтому, пожалуйста, поправьте меня! - person Nick Strupat; 25.10.2011
comment
@nick: Конечно, реализация лексического анализатора и парсера занимает пять минут. Затем вы начинаете беспокоиться о таких вещах, как хорошо ли справляется движок IntelliSense? и как с этим справляется система исправления ошибок, когда вы набираете? но не. пока что? и сколько разных вещей делает. в любом случае означает в C #, и сколько из них заслуживают? перед этим, и какие должны быть сообщения об ошибках, если вы ошиблись, и сколько тестирования потребуется этой функции (подсказка: много). И как мы собираемся задокументировать это и сообщить об изменении, и ... - person Eric Lippert; 25.10.2011
comment
@nick: Отвечая на ваш вопрос - да, стоимость реализации - фактор, но небольшой. На том уровне, над которым мы работаем, нет дешевых функций, только более или менее дорогие функции. Работа разработчиков на пять долларов, чтобы заставить парсер работать в случае правильного кода, может легко превратиться в десятки тысяч долларов на проектирование, внедрение, тестирование, документацию и обучение. - person Eric Lippert; 25.10.2011
comment
@EricLippert Я думаю, что это было бы одним из ГЛАВНЫХ приятных вещей, которые сделали C # настолько успешным, а также отлично послужили бы миссии по созданию максимально лаконичного и выразительного кода! - person Konstantin; 25.10.2011
comment
Я хотел бы, чтобы это было добавлено, я ДЕЙСТВИТЕЛЬНО хочу, чтобы оператор elvis: stackoverflow.com/questions/2929836/ - person Chris Marisic; 06.06.2012

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

return x && x.y;

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

person Eric    schedule 28.05.2010
comment
Кроме того, это также приводит к короткому замыканию на 0 и NaN, так что это не противоположность ??. Это противоположность ||. - person Roberto Zvjerković; 03.05.2020

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

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

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

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

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

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

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

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

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

person 48klocs    schedule 28.05.2010
comment
Не могли бы вы привести пример использования этого оператора? - person Max Carroll; 15.09.2015
comment
?. - это функция языка C # 6.0, и да, она делает именно то, что здесь попросил OP. Лучше поздно, чем никогда ^^ - person T_D; 04.07.2016

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

  • Nothing >> Nothing is Nothing
  • Nothing >> Just 1 is Nothing
  • Just 2 >> Nothing is Nothing
  • Just 2 >> Just 1 is Just 1
person dave4420    schedule 28.05.2010

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

fmap select_y x

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

person Norman Ramsey    schedule 28.05.2010

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);
person Josh    schedule 28.05.2010

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"
person J Bryan Price    schedule 12.03.2013

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

Например:

z = new Thingy { y=null };

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

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

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

return (x ?? z).y;
person Nick    schedule 04.01.2012
comment
Иногда я использую эту технику (например, (x ??) .ToString ()), но она практична только для некоторых типов данных, таких как POD и строки. - person Qwertie; 07.06.2012

Это добавляется в 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

person Micah Zoltu    schedule 30.11.2014

Так называемый «нулевой условный оператор» был введен в 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

person Jpsy    schedule 06.04.2018