Существует ли "противоположность" нулевому коалесцирующему оператору? (... на любом языке?)

null коалесценция грубо приближается к 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.

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

Существуют ли другие языки, имеющие такой оператор? Если да, то что называется?

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

Ответ 1

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

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

Например:

homePostcode = person?.homeAddress?.postcode

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

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

Ответ 2

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

Ответ 3

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

return x && x.y;

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

Ответ 4

Мне просто хотелось добавить это как ответ.

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

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

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

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

Ответ 5

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

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

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

Ответ 6

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

  • Nothing >> Nothing Nothing
  • Nothing >> Just 1 Nothing
  • Just 2 >> Nothing Nothing
  • Just 2 >> Just 1 Just 1

Ответ 7

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

fmap select_y x

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

Ответ 8

PowerShell позволяет ссылаться на свойства (но не на методы вызова) на нулевую ссылку и возвращает значение null, если экземпляр имеет значение null. Вы можете сделать это на любой глубине. Я надеялся, что динамическая функция С# 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);

Ответ 9

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

Например:

z = new Thingy { y=null };

а затем вместо

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

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

return (x ?? z).y;

Ответ 10

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"