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


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.