Что означают два вопросительных знака вместе в C#?

Просмотрите эту строку кода:

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

Что означают два вопросительных знака, это какой-то тернарный оператор? Сложно поискать в гугле.

Это определенно нет тернарный оператор - у него всего два операнда! Это немного похоже на условный оператор (который тернарный является), но нулевой оператор объединения является бинарным оператором.

Jon Skeet 15.01.2009 17:11

Я объяснил это в интервью, где потенциальный работодатель ранее выражал сомнения относительно моих способностей к C#, поскольку я уже некоторое время профессионально использовал Java. Они не слышали об этом раньше, и после этого не ставили под сомнение мое знакомство с C# :)

Jon Skeet 15.01.2009 19:32

@Jon Skeet: Не было такого эпического провала в признании мастерства с тех пор, как парень отверг Битлз. :-) С этого момента просто отправьте им копию своей книги со ссылкой на ваш профиль SO, написанной на внутренней стороне обложки.

Iain Holder 16.01.2009 12:58

IainMH: Как бы то ни было, я еще не начал писать книгу довольно. (Или, может быть, я просто работал над главой 1 - что-то в этом роде.) По общему признанию, поиск по мне быстро нашел бы мой блог + статьи и т. д.

Jon Skeet 18.01.2009 19:45

Re: последнее предложение в q - для будущего обращения, SymbolHound отлично подходит для такого рода вещей, например. symbolhound.com/?q=%3F%3F&l=&e=&n=&u= [всем подозрительным - я никоим образом не связан, просто как хороший инструмент, когда я его нахожу ...]

Steve Chambers 25.04.2013 12:44

В поисках C# ?? | C# '??' | C# "??" не приносит ожидаемых результатов. Проверяет ли поисковая система, если C# имеет значение NULL, и говорит: «Нет - это на самом деле C# - вот ваши результаты для C# - Да!

Piotr Kula 31.10.2013 21:17

@ppumkin Просто поищите double question mark c# в гугле.

user692942 25.05.2016 14:03
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1 760
7
470 464
18
Перейти к ответу Данный вопрос помечен как решенный

Ответы 18

Ответ принят как подходящий

Это оператор объединения с нулевым значением и очень похож на тернарный оператор (немедленное если). См. Также ?? Оператор - MSDN.

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

расширяется до:

FormsAuth = formsAuth != null ? formsAuth : new FormsAuthenticationWrapper();

который далее расширяется до:

if (formsAuth != null)
    FormsAuth = formsAuth;
else
    FormsAuth = new FormsAuthenticationWrapper();

По-английски это означает: «Если то, что находится слева, не является нулем, используйте это, иначе используйте то, что справа».

Обратите внимание, что вы можете использовать любое количество из них последовательно. Следующий оператор назначит первый ненулевой Answer# для Answer (если все ответы равны нулю, значит, Answer равен нулю):

string Answer = Answer1 ?? Answer2 ?? Answer3 ?? Answer4;

Также стоит упомянуть, что, хотя приведенное выше расширение концептуально эквивалентно, результат каждого выражения оценивается только один раз. Это важно, если, например, выражение является вызовом метода с побочными эффектами. (Благодарим @Joey за указание на это.)

Возможно, опасно связывать эти цепочки

Coops 04.08.2011 12:26

@CodeBlend, это не опасно. Если бы вы развернули, у вас была бы просто серия вложенных операторов if / else. Синтаксис просто странный, потому что вы не привыкли его видеть.

joelmdev 26.10.2011 23:33

@ jm2 Пока вы просматриваете то, что делаете, например, если вы хотели разделить, а не вложить, если это не подходит. (Прочитав это, я действительно показал мне достойный пример того, как это можно использовать) [rlacovara.blogspot.com/2009/07/…

Coops 27.10.2011 12:09

@CodeBlend ... это не случай "любого" кодирования - всегда проверять :-)

SF Developer 22.08.2012 22:08

Было бы полезно добавить в этот ответ ссылку на Документация MSDN - чтобы все было вместе. :) Потому что нашел в следующий ответ.

imy 24.09.2012 15:41

он также назначил бы его всем предыдущим переменным answear #, которые были нулевыми правильными ??

Xitcod13 15.10.2012 12:03

@ Xitcod13 Нет, это не так. Семантически вам нужно будет выполнить Answer1 ??= Answer2, чтобы присвоить значение Answer1, но ??= не является допустимым оператором.

lc. 15.10.2012 12:29

@lc просто потому, что он не очищает воду. Указанного вложенности нет - слияние нулей здесь последовательное. Однако концепция верна.

Gusdor 17.09.2013 11:15

Было бы здорово, если бы он работал и с пустыми строками :)

Zyphrax 19.11.2013 20:18

@Zyphrax, да. Подобно тому, как || работает в JavaScript.

IsmailS 21.04.2014 09:36

оценивается ли первый аргумент дважды, если он в первый раз возвращает ненулевое значение? Например: x = f1 () ?? f2 (); будет ли 'f1' оцениваться дважды, когда он в первый раз возвращает ненулевое значение?

Alex 30.09.2014 18:31

@Gusdor ?? является левоассоциативным, поэтому a ?? b ?? c ?? d эквивалентен ((a ?? b) ?? c ) ?? d. «Операторы присваивания и тернарный оператор (? :) являются правоассоциативными. Все остальные бинарные операторы левоассоциативны». Источник: msdn.microsoft.com/en-us/library/ms173145.aspx

Mark E. Haase 05.12.2014 23:45

Обратите внимание, что расширение не совсем корректно, как показано здесь, поскольку язык гарантирует, что левый операнд оценивается только один раз. В данном конкретном случае это не проблема, но когда у вас есть более сложное выражение, чем локальная переменная слева, это становится важным.

Joey 24.07.2017 10:54

?? предназначен для предоставления значения для типа, допускающего значение NULL, когда значение равно NULL. Итак, если formAuth имеет значение null, он вернет новый FormsAuthenticationWrapper ().

Это нулевой оператор объединения.

http://msdn.microsoft.com/en-us/library/ms173224.aspx

Да, почти невозможно найти, если не знаешь, как это называется! :-)

Обновлено: И это классная функция из другого вопроса. Вы можете связать их.

Скрытые возможности C#?

По крайней мере, сейчас его легко искать, даже если вы не знаете имя, я просто погуглил "двойной вопросительный знак C#"

stivlo 21.12.2011 12:18

Это сокращение от тернарного оператора.

FormsAuth = (formsAuth != null) ? formsAuth : new FormsAuthenticationWrapper();

Или для тех, кто не занимается тройным:

if (formsAuth != null)
{
  FormsAuth = formsAuth;
}
else
{
  FormsAuth = new FormsAuthenticationWrapper();
}

Я только исправил написание слова «троичный», но на самом деле оператор, который вы имеете в виду, является условным оператором. Это единственный тернарный оператор в C#, но в какой-то момент они могут добавить еще один, и тогда «тернарный» будет неоднозначным, а «условный» - нет.

Jon Skeet 15.01.2009 17:10

Это сокращение для чего-то, что вы можете делать с тернарным (условным) оператором. В вашей длинной форме можно изменить как тест (!= null), так и второй formsAuth (после ?); в форме объединения NULL оба неявно принимают указанные вами значения.

Bob Sammers 20.04.2016 15:24

оператор объединения

это эквивалентно

FormsAuth = formsAUth == null ? new FormsAuthenticationWrapper() : formsAuth

Просто потому, что никто еще не сказал волшебных слов: это нулевой оператор объединения. Это определено в разделе 7.12 Спецификация языка C# 3.0.

Это очень удобно, особенно из-за того, как работает, когда используется несколько раз в выражении. Выражение формы:

a ?? b ?? c ?? d

даст результат выражения a, если он не равен нулю, в противном случае попробуйте b, в противном случае попробуйте c, в противном случае попробуйте d. Короткое замыкание в каждой точке.

Кроме того, если тип d не допускает значения NULL, тип всего выражения также не допускает значения NULL.

Мне нравится, как вы упростили его значение до «нулевого оператора объединения». Это все, что мне было нужно.

FrankO 08.09.2015 18:25

Тангенциальный, но связанный: теперь он в Swift, но совсем по-другому: "Nil Coalescing Operator" developer.apple.com/library/ios/documentation/Swift/Conceptu‌ al /…

Dan Rosenstark 14.10.2015 22:21

Всем спасибо, вот самое краткое объяснение, которое я нашел на сайте MSDN:

// y = x, unless x is null, in which case y = -1.
int y = x ?? -1;

Это намекает на важный аспект ?? оператор - он был введен для помощи в работе с типами, допускающими значение NULL. В вашем примере «x» имеет тип «int?» (Обнуляемый <int>).

Drew Noakes 05.06.2009 16:56

@vitule нет, если второй операнд оператора объединения NULL не допускает значения NULL, то результат не допускает значения NULL (а -1 - это простой int, который не допускает значения NULL).

Suzanne Soy 11.07.2013 13:52

Только для вашего развлечения (зная, что вы все ребята из C# ;-).

Я думаю, что он зародился в Smalltalk, где он существует уже много лет. Там он определяется как:

в объекте:

? anArgument
    ^ self

в UndefinedObject (он же класс nil):

? anArgument
    ^ anArgument

Существуют как оценочные (?), Так и неоценочные версии (??).
Часто встречается в методах получения для закрытых (экземпляров) переменных с отложенной инициализацией, которые остаются равными нулю до тех пор, пока они действительно не понадобятся.

звучит как упаковка ViewState со свойством в UserControl. Инициализировать только при первом получении, если он не был установлен ранее. знак равно

Seiti 15.01.2009 17:52

Если вы знакомы с Ruby, мне кажется, что его ||= сродни C# ??. Вот немного Рубина:

irb(main):001:0> str1 = nil
=> nil
irb(main):002:0> str1 ||= "new value"
=> "new value"
irb(main):003:0> str2 = "old value"
=> "old value"
irb(main):004:0> str2 ||= "another new value"
=> "old value"
irb(main):005:0> str1
=> "new value"
irb(main):006:0> str2
=> "old value"

И в C#:

string str1 = null;
str1 = str1 ?? "new value";
string str2 = "old value";
str2 = str2 ?? "another new value";
x ||= y десахарирует что-то вроде x = x || y, поэтому ?? на самом деле больше похож на обычный || в Ruby.
qerub 02.02.2013 04:03

Обратите внимание, что ?? заботится только о null, тогда как оператор || в Ruby, как и в большинстве языков, больше касается null, false или всего, что может считаться логическим со значением false (например, в некоторых языках ""). Это не хорошо или плохо, просто разница.

Tim S. 09.05.2013 00:15

Ничего опасного в этом нет. На самом деле это красиво. Вы можете добавить значение по умолчанию, если это желательно, например:

КОД

int x = x1 ?? x2 ?? x3 ?? x4 ?? 0;

Итак, x1, x2, x3 и x4 могут быть типами, допускающими значение NULL, например: int? x1 = null; Это верно

Kevin Meredith 25.06.2013 23:02

@KevinMeredith x1 - x4 ДОЛЖНЫ быть типами, допускающими значение NULL: фактически нет смысла говорить, что «результат будет 0, если x4 - это значение, которое он не может принять» (null). «Обнуляемый тип» здесь, конечно, включает как обнуляемые типы значение, так и ссылочные типы. Ошибка времени компиляции, если одна или несколько связанных переменных (кроме последней) не допускают значения NULL.

Bob Sammers 20.04.2016 15:13

Два вопросительных знака (??) указывают на то, что это оператор объединения.

Оператор объединения возвращает первое значение NON-NULL из цепочки. Вы можете увидеть это видео на YouTube, который практически все это демонстрирует.

Но позвольте мне добавить больше к тому, что говорится в видео.

Если вы видите английское значение слияния, оно означает «объединяться вместе». Например, ниже приведен простой код объединения, который объединяет четыре строки в цепочку.

Итак, если str1 - это null, он будет пробовать str2, если str2 - это null, он будет пробовать str3 и так далее, пока не найдет строку с ненулевым значением.

string final = str1 ?? str2 ?? str3 ?? str4;

Проще говоря, оператор объединения возвращает первое значение NON-NULL из цепочки.

Мне нравится это объяснение, потому что в нем есть диаграмма, а последнее предложение объясняет это так просто! Это облегчает понимание и повышает мою уверенность в использовании. Спасибо!

Joshua K 02.08.2018 17:13

когда мы берем только английское значение слова coalesce, мы могли ожидать, что результатом будет своего рода объединение или слияние аргументов. Конечно, это не лучшая метафора того, что он делает на самом деле. Но есть давний предшественник, функция t-sql COALESCE, которая делает то же самое и - о чем могут знать немногие разработчики - имеет список переменных аргументов, поэтому она может принимать более двух значений кандидатов для объединения, как и .СЕТЬ ?? оператор.

Cee McSharpface 18.11.2020 12:43

Некоторые из приведенных здесь примеров получения значений с помощью объединения неэффективны.

Что вам действительно нужно:

return _formsAuthWrapper = _formsAuthWrapper ?? new FormsAuthenticationWrapper();

или же

return _formsAuthWrapper ?? (_formsAuthWrapper = new FormsAuthenticationWrapper());

Это предотвращает повторное создание объекта каждый раз. Вместо того, чтобы закрытая переменная оставалась нулевой и новый объект создавался при каждом запросе, это обеспечивает присвоение частной переменной при создании нового объекта.

Разве сокращение ?? не оценивается? new FormsAuthenticationWrapper(); оценивается если и только если_formsAuthWrapper равен нулю.

MSalters 14.06.2017 16:04

Да в том-то и дело. Вы хотите вызвать метод только в том случае, если переменная равна нулю. @MSalters

KingOfHypocrites 14.06.2017 21:57

Как правильно указано в многочисленных ответах, это «нулевой оператор объединения» (??), говоря о котором, вы также можете проверить его кузена «Нуль-условный оператор» (? или ? [), который является оператором, который много раз используется вместе с ??

Нуль-условный оператор

Used to test for null before performing a member access (?.) or index (?[) operation. These operators help you write less code to handle null checks, especially for descending into data structures.

Например:

// if 'customers' or 'Order' property or 'Price' property  is null,
// dollarAmount will be 0 
// otherwise dollarAmount will be equal to 'customers.Order.Price'

int dollarAmount = customers?.Order?.Price ?? 0; 

старый способ без ? и ?? выполнения этого

int dollarAmount = customers != null 
                   && customers.Order!=null
                   && customers.Order.Price!=null 
                    ? customers.Order.Price : 0; 

что более многословно и громоздко.

Примечание:

Я прочитал всю эту и многие другие ветки, но не могу найти столь подробного ответа, как этот.

По которым я полностью понял «зачем использовать ??, когда использовать ?? и как использовать ??».

Источник:

Основа коммуникации Windows, запущенная Крейгом Макмертри ISBN 0-672-32948-4

Типы значений, допускающих значение NULL

Есть два общих обстоятельства, при которых хотелось бы знать, значение было присвоено экземпляру типа значения. Первый - это когда экземпляр представляет значение в базе данных. В таком случае хотелось бы иметь возможность исследовать экземпляр, чтобы убедиться, действительно ли значение присутствует в базе данных. Другое обстоятельство, более подходящее для предмета этой книги, - это когда экземпляр представляет собой элемент данных, полученный из некоторого удаленного источника. Опять же, хотелось бы определить по экземпляру, было ли получено значение для этого элемента данных.

.NET Framework 2.0 включает определение универсального типа, которое обеспечивает такие случаи, когда нужно присвоить значение NULL экземпляру типа значения и проверить, является ли значение экземпляра NULL. Это определение универсального типа - System.Nullable<T>, которое ограничивает аргументы универсального типа, которые могут быть заменены на T, типами значений. Экземплярам типов, созданных из System.Nullable<T>, может быть присвоено значение null; действительно, их значения по умолчанию равны нулю. Таким образом, типы, построенные из System.Nullable<T> можно называть типами значений, допускающими значение NULL. System.Nullable<T> имеет свойство Value, с помощью которого значение, присвоенное экземпляру тип, построенный из него, может быть получен, если значение экземпляра не равно нулю. Следовательно, можно написать:

System.Nullable<int> myNullableInteger = null;
myNullableInteger = 1;
if (myNullableInteger != null)
{
Console.WriteLine(myNullableInteger.Value);
}

Язык программирования C# предоставляет сокращенный синтаксис для объявления типов. построенный из System.Nullable<T>. Этот синтаксис позволяет сокращать:

System.Nullable<int> myNullableInteger;

к

int? myNullableInteger;

Компилятор предотвратит попытку присвоить значение типа значения, допускающего значение NULL, обычному типу значения следующим образом:

int? myNullableInteger = null;
int myInteger = myNullableInteger;

Это мешает сделать это, потому что тип значения, допускающий значение NULL, может иметь значение NULL, которое он действительно имел бы в этом случае, и это значение не может быть присвоено обычному типу значения. Хотя компилятор разрешил этот код,

int? myNullableInteger = null;
int myInteger = myNullableInteger.Value;

Второй оператор вызовет исключение, потому что любая попытка доступ к свойству System.Nullable<T>.Value является недопустимой операцией, если тип построенный из System.Nullable<T>, не получил действительного значения T, чего не произошло в данном случае.

Заключение:

Один из правильных способов присвоить значение типа значения, допускающего значение NULL, обычному типу значения - использовать свойство System.Nullable<T>.HasValue, чтобы убедиться, что допустимое значение T было присвоено типу значения, допускающему значение NULL:

int? myNullableInteger = null;
if (myNullableInteger.HasValue)
{
int myInteger = myNullableInteger.Value;
}

Другой вариант - использовать этот синтаксис:

int? myNullableInteger = null;
int myInteger = myNullableInteger ?? -1;

По которому обычному целому числу myInteger присваивается значение обнуляемого целого числа "myNullableInteger", если последнему было присвоено допустимое целочисленное значение; в противном случае myInteger присваивается значение -1.

Это нулевой оператор объединения, который работает аналогично тернарному оператору.

    a ?? b  => a !=null ? a : b 

Еще один интересный момент для этого: «Тип, допускающий значение NULL, может содержать значение или быть неопределенным». Итак, если вы попытаетесь присвоить тип значения, допускающий значение NULL, типу значения, не допускающему значение NULL, вы получите ошибку времени компиляции.

int? x = null; // x is nullable value type
int z = 0; // z is non-nullable value type
z = x; // compile error will be there.

Так что делать с помощью ?? оператор:

z = x ?? 1; // with ?? operator there are no issues
FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

эквивалентно

FormsAuth = formsAuth != null ? formsAuth : new FormsAuthenticationWrapper();

Но самое интересное в том, что вы можете связать их, как говорят другие. Единственное, что не затронуто, это то, что вы можете использовать его для создания исключения.

A = A ?? B ?? throw new Exception("A and B are both NULL");

Это действительно здорово, что вы включили примеры в свой пост, хотя вопрос был связан с объяснением того, что оператор или что делает.

TGP1994 01.04.2018 00:53

Оператор ?? называется оператором объединения с нулем. Он возвращает левый операнд, если операнд не равен нулю; в противном случае возвращается правый операнд.

int? variable1 = null;
int variable2  = variable1 ?? 100;

Установите для variable2 значение variable1, если variable1 НЕ равен нулю; в противном случае, если variable1 == null, установите variable2 на 100.

Другие довольно хорошо описали Null Coalescing Operator. В случаях, когда требуется единичный тест на null, сокращенный синтаксис ??= может повысить удобочитаемость.

Устаревший нулевой тест:

if (myvariable == null)
{
    myvariable = new MyConstructor();
}

Используя оператор Null Coalescing, это можно записать:

myvariable = myvariable ?? new MyConstructor();

который также можно записать с сокращенным синтаксисом:

myvariable ??= new MyConstructor();

Некоторым он кажется более читаемым и лаконичным.

имейте в виду, что эта функция доступна только в C# 8 или более поздних версиях.

cfazilleau 12.11.2020 02:26

Другие вопросы по теме