Что означают два знака вопроса вместе в C #?

1632

Наткнулся на эту строку кода:

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

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

Эдвард Тангей
источник
50
Это определенно не троичный оператор - у него только два операнда! Это немного похоже на условный оператор (который является троичным), но оператор слияния нуля является бинарным оператором.
Джон Скит
90
Я объяснил это в интервью, где потенциальный работодатель ранее выражал сомнения в моих возможностях C #, поскольку я уже некоторое время профессионально использовал Java. Они не слышали об этом раньше и не ставили под сомнение мое знакомство с C # после этого :)
Джон Скит
77
@Jon Skeet Не было такого эпоса, который не смог бы распознать умение со времен парня, который отказался от Битлз. :-) Отныне просто отправьте им копию вашей книги с URL-ссылкой на ваш SO-профиль, написанный на внутренней обложке.
Iain Holder
6
IainMH: Что бы это ни стоило, я еще не совсем начал писать книгу. (Или, может быть, я просто работал над главой 1 - что-то вроде этого.) По общему признанию, поиск меня быстро нашел бы мой блог + статьи и т. Д.
Джон Скит
7
Re: последнее предложение в q - для будущей ссылки SymbolHound отлично подходит для такого рода вещей, например, symbolhound.com/?q=%3F%3F&l=&e=&n=&u= [кому-либо подозрительному - я не связан в В любом случае, как хороший инструмент, когда я его найду ...]
Стив Чэмберс

Ответы:

2156

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

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

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

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

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

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

В английском это означает «Если то, что слева, не равно нулю, используйте это, в противном случае используйте то, что справа».

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

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

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

ЖХ.
источник
17
Потенциально опасно зацепить их, может быть
Coops
6
@CodeBlend как так?
жк.
68
@CodeBlend, это не опасно. Если бы вы расширились, у вас была бы серия вложенных операторов if / else. Синтаксис просто странный, потому что вы не привыкли его видеть.
Joelmdev
31
Это было бы
божьим посылом,
14
@Gusdor ??остается ассоциативным, так a ?? b ?? c ?? dчто эквивалентно ((a ?? b) ?? c ) ?? d. «Операторы присваивания и троичный оператор (? :) ассоциативны справа. Все остальные бинарные операторы ассоциативны слева». Источник: msdn.microsoft.com/en-us/library/ms173145.aspx
Марк Э.
284

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

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

a ?? b ?? c ?? d

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

Кроме того, если тип dне обнуляется, тип целого выражения также не обнуляется.

Джон Скит
источник
2
Мне нравится, как вы упростили его значение для «оператора объединения нулей». Это все, что мне нужно.
FrankO
2
Тангенциальный, но связанный: теперь он в Swift, но он совсем другой: «Оператор
слияния
69

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

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

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

РЕДАКТИРОВАТЬ: И это классная особенность из другого вопроса. Вы можете связать их

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

Iain Holder
источник
33
По крайней мере, теперь, это легко найти, даже если вы не знаете имя, я просто погуглил "двойной знак вопроса c #"
stivlo
27

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

// y = x, unless x is null, in which case y = -1.
int y = x ?? -1;
Эдвард Тангей
источник
4
Это намекает на важный аспект оператор - это было введено, чтобы помочь в работе с обнуляемыми типами. В вашем примере "x" имеет тип "int?" (Nullable <Int>).
Дрю Ноакс
9
@vitule no, если второй операнд оператора слияния NULL не обнуляется, то результат не обнуляется (и -1это просто обычное int, не обнуляемое).
Сюзанна Дюперон
Я действительно хотел бы, чтобы это работало так. Я хочу делать это все время, но не могу, потому что переменная, которую я использую, не имеет типа null. Работает в coffeescript y = x? -1
PandaWood
@PandaWood На самом деле, вы можете. Если xимеет тип int?, но yимеет тип int, вы можете написать int y = (int)(x ?? -1). Он будет разбирать xАня , intесли это не null, или назначить -1в yслучае xэто null.
Йохан Винтгенс
21

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

RedFilter
источник
19

введите описание изображения здесь

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

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

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

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

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

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

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

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

Это короткая рука для троичного оператора.

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

Или для тех, кто не делает троичный

if (formsAuth != null)
{
  FormsAuth = formsAuth;
}
else
{
  FormsAuth = new FormsAuthenticationWrapper();
}
Бенджамин Аутин
источник
3
Я только исправил написание слова «троичный», но на самом деле вы имеете в виду оператор условного оператора. Это единственный троичный оператор в C #, но в какой-то момент они могут добавить еще один, в этом случае «троичный» будет неоднозначным, а «условный» - нет.
Джон Скит
Это сокращение от того, что вы можете сделать с помощью троичного (условного) оператора. В вашей длинной форме и test ( != null), и второе formsAuth(после ?) могут быть изменены; в форме слияния null оба неявно принимают введенные вами значения.
Боб Сэммерс
12

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

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";
Сара Весселс
источник
4
x ||= yDesugars на что-то вроде x = x || y, так ??что на самом деле больше похоже на обычный ||в Ruby.
13
6
Обратите внимание , что ??только заботится о null, в то время как ||оператор в Ruby, как и в большинстве языков, больше о null, falseили что - нибудь , что можно рассматривать как логическое со значением false(например , в некоторых языках, ""). Это не хорошо или плохо, просто разница.
Тим С.
11

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

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

FormsAuth = formsAUth == null ? new FormsAuthenticationWrapper() : formsAuth
Ака
источник
11

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

КОД

int x = x1 ?? x2 ?? x3 ?? x4 ?? 0;
dqninh
источник
1
Таким образом, x1, x2, x3 и x4 могут быть типами Nullable, например: int? x1 = null;верно ли это
Кевин Мередит
1
@KevinMeredith x1- x4должен быть обнуляемыми типами: это не имеет никакого смысла говорить, по сути, «результат , 0если x4этому значение , которое оно не может принять» ( null). «Обнуляемый тип» здесь включает , конечно, как обнуляемые типы значений, так и ссылочные типы. Это ошибка времени компиляции, если одна или несколько цепочечных переменных (кроме последней) не обнуляются.
Боб Сэммерс
11

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

Нулевой оператор

Используется для проверки на нулевое значение перед выполнением операции доступа к члену ( ?. ) Или операции индекса ( ? [ ). Эти операторы помогают вам писать меньше кода для обработки нулевых проверок, особенно для перехода в структуры данных.

Например:

// 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; 

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

brakeroo
источник
10

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

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

в объекте:

? anArgument
    ^ self

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

? anArgument
    ^ anArgument

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

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

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

То, что вы действительно хотите, это:

return _formsAuthWrapper = _formsAuthWrapper ?? new FormsAuthenticationWrapper();

или

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

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

KingOfHypocrites
источник
Не ??оценивается ли короткий путь? new FormsAuthenticationWrapper();оценивается тогда и только тогда, когда _formsAuthWrapper ноль.
MSalters
Да, в этом весь смысл. Вы хотите вызывать метод, только если переменная равна нулю. @MSalters
KingOfHypocrites
8

Замечания:

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

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

Источник:

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

Типы значений Nullable

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

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

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

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

System.Nullable<int> myNullableInteger;

в

int? myNullableInteger;

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

int? myNullableInteger = null;
int myInteger = myNullableInteger;

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

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

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

Вывод:

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

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

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

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

С помощью которого обычному целому числу myInteger присваивается значение обнуляемого целого числа «myNullableInteger», если последнему присвоено допустимое целочисленное значение; в противном случае myInteger присваивается значение -1.

Мухаммед Вакас Дилавар
источник
1

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

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

Еще один интересный момент для этого: «Обнуляемый тип может содержать значение или он может быть неопределенным» . Таким образом, если вы попытаетесь назначить тип значения 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
Ашиш Мишра
источник
1
FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

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

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

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

A = A ?? B ?? throw new Exception("A and B are both NULL");
NolePTR
источник
Это действительно здорово, что вы включили примеры в свое сообщение, хотя вопрос был в поиске объяснения того, что оператор делает или делает.
TGP1994
1

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

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

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

Джубайер Хоссейн
источник
0

Другие описали это Null Coalescing Operatorдовольно хорошо. Для тех, кто заинтересован, существует сокращенный синтаксис, где это (вопрос SO):

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

эквивалентно этому:

FormsAuth ??= new FormsAuthenticationWrapper();

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

Тейн Пламмер
источник