Чи існує «протилежність» оператору нульового злиття? (... будь-якою мовою?)


92

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.

Я бачу в цьому майже протилежність нульовій коалесценції, якусь змішану з лаконічною, вбудованою нульовою перевіркою, але я [ майже ] певна, що такого оператора в C # немає.

Чи існують інші мови, які мають такого оператора? Якщо так, то як це називається?

(Я знаю, що можу написати для нього метод на C #; я використовую return NullOrValue.of(x, () => x.y);, але якщо у вас є щось краще, я хотів би це також побачити.)


Деякі просили щось на зразок x? .Y в C #, але нічого подібного не існує.
Ентоні Пеграм

1
@Anthony Oh, це було б чудово. Дякую.
Джей,

2
У C ++ це досить легко виразити як return x ? x.y : NULL. Yay за перетворення типів покажчиків у логічні!
Філ Міллер,

1
@Novelocrat, це одна з речей, яка мене найбільше дратує в C #, полягає в тому, що вони не слідували C, якщо if if (anything) = true, за винятком випадків if if (0, false, null)
Кріс Марісіч

2
@Chris: це не точне твердження про C. Якщо у вас є нескалярна змінна (наприклад, структура), ви не можете використовувати це в умові.
Філ Міллер,

Відповіді:


62

У Groovy є нульовий безпечний оператор переспрямування (?.) ... Я думаю, це те, що ви шукаєте.

(Його також називають оператором безпечної навігації .)

Наприклад:

homePostcode = person?.homeAddress?.postcode

Це дасть null if person, person.homeAddressабо person.homeAddress.postcodeє null.

(Це тепер доступно в C # 6.0, але не в попередніх версіях)


1
Groovy також має "оператора Елвіса", який допускає значення за замовчуванням, крім null, наприклад:def bar = foo ?: "<null>"
Крейг Стунтц,

28
Я призначив цю функцію для C # 5.0. Я не знаю чи піклуюся про те, що насправді є Groovy, але це досить інтуїтивно зрозуміло для використання на будь-якій мові.
Дьєрдь Андрасек,

Однак його зараз додають до C # 6, ура.
Джефф Меркадо,

1
Safe-навігація робота для тривіального прикладу відповідала, але вам все одно доведеться використовувати потрійний оператор , якщо ви збираєтеся використовувати значення ненульового у виклику функції, наприклад: Decimal? post = pre == null ? null : SomeMethod( pre );. Було б непогано, якби я міг зменшити його до "Decimal? Post = pre :: SomeMethod (pre);"
Dai

@Dai: Беручи до уваги кількість перестановок, яких ти можеш цього побажати, я задоволений тим, що у нас є.
Джон Скіт,

36

Ми розглядали додавання?. до C # 4. Це не зробило скорочення; це функція "приємно мати", а не "повинна бути". Ми ще раз розглянемо це для гіпотетичних майбутніх версій мови, але на вашому місці я б не затамував подих. З часом час навряд чи стане більш важливим. :-)


Чи легкість реалізації функції впливає на рішення про її додавання? Я запитую це, тому що реалізація цього оператора здається досить простим, простим доповненням до мови. Я не фахівець (просто недавній випускник із великою любов’ю до C #), тому, будь ласка, виправте мене!
Nick Strupat

14
@nick: Звичайно, впровадження лексера та парсера - це п’ять хвилин роботи. Тоді ви починаєте турбуватися про такі речі, як "чи добре працює механізм IntelliSense з цим?" і "як система відновлення помилок справляється з цим, коли ви ввели?, але ще не.?" і скільки різних речей робить "." маю на увазі в С # все одно, і скільки з них заслуговують мати? перед цим, і якими повинні бути повідомлення про помилки, якщо ви помиляєтесь, і скільки тестування знадобиться цій функції (підказка: багато.) І як ми будемо це документувати та повідомляти про зміни, і ...
Eric Lippert

3
@nick: Щоб відповісти на ваше запитання - так, вартість впровадження є фактором, але невеликим. На тому рівні, на якому ми працюємо, немає дешевих функцій, є лише більш-менш дорогі функції. Робота розробників вартістю п’ять доларів, щоб змусити парсер працювати у випадку з правильним кодом, може легко перетворитися на проектування, впровадження, тестування, документацію та освіту на десятки тисяч доларів.
Eric Lippert

11
@EricLippert Я думаю, це було б одним із ОСНОВНИХ "приємних", які зробили C # настільки успішним, а також чудово виконали б місію зробити код максимально стислим та виразним!
Костянтин

Я хотів би бачити це додало, що я дійсно хочу, оператор Елвіса: stackoverflow.com/questions/2929836 / ...
Кріс Marisic

16

Якщо у вас особливий вид логічної логіки короткого замикання, ви можете зробити це (приклад javascript):

return x && x.y;

Якщо xмає значення null, воно не буде оцінювати x.y.


2
Крім цього також коротке замикання на 0, "" і NaN, так що це не протилежність ??. Це протилежність ||.
ritaj

7

Просто було правильним додати це як відповідь.

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

Однак я не кажу, що я не хотів би це бачити!

Потенційним рішенням цієї проблеми було б для оператора автоматично підняти вираз типу значення праворуч до значення, що обнуляється. Але тоді у вас виникає проблема, що x.yде y - це int, насправді поверне, int?що було б болем.

Іншим, мабуть, кращим рішенням було б повернення оператором значення за замовчуванням (тобто нуль або нуль) для типу праворуч, якщо вираз зліва є нулем. Але тоді у вас виникають проблеми з розрізненням сценаріїв, з яких насправді було прочитано нуль / нуль x.yабо воно було надано оператором безпечного доступу.


коли я вперше прочитав питання ОП, саме це питання виникло мені в голові, але я не міг зрозуміти, як його сформулювати. Це дуже серйозна проблема. +1
rmeador

це не серйозна проблема. Просто використовуйте int?як значення за замовчуванням, і користувач може змінити його на int, якщо захоче. Наприклад, якщо я хотів би викликати якийсь метод Lookupзі значенням за замовчуванням -1 на випадок, якщо одне з моїх посилань null:foo?.Bar?.Lookup(baz) ?? -1
Qwertie

Як щодо того, щоб ?. :бути трикомпонентним оператором, де правий бік повинен бути того самого типу, що і відповідний член, поданий посередині?
supercat

6

Delphi має оператор: (а не.), Який є нульовим.

Вони думали про те, щоб додати знак. оператор до C # 4.0, щоб зробити те ж саме, але це отримало відбивний блок.

Тим часом є IfNotNull (), які подряпини сверблять. Це, звичайно, більше ніж. або:, але це дозволяє вам скласти ланцюжок операцій, який не буде перешкоджати вам NullReferenceException, якщо один із членів має значення null.


Ви можете навести приклад використання цього оператора, будь ласка?
Макс Керролл

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

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 (Roslyn працює на C #, випуски з 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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.