Просмотрите эту строку кода:
FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();
Что означают два вопросительных знака, это какой-то тернарный оператор? Сложно поискать в гугле.
Я объяснил это в интервью, где потенциальный работодатель ранее выражал сомнения относительно моих способностей к C#, поскольку я уже некоторое время профессионально использовал Java. Они не слышали об этом раньше, и после этого не ставили под сомнение мое знакомство с C# :)
@Jon Skeet: Не было такого эпического провала в признании мастерства с тех пор, как парень отверг Битлз. :-) С этого момента просто отправьте им копию своей книги со ссылкой на ваш профиль SO, написанной на внутренней стороне обложки.
IainMH: Как бы то ни было, я еще не начал писать книгу довольно. (Или, может быть, я просто работал над главой 1 - что-то в этом роде.) По общему признанию, поиск по мне быстро нашел бы мой блог + статьи и т. д.
Re: последнее предложение в q - для будущего обращения, SymbolHound отлично подходит для такого рода вещей, например. symbolhound.com/?q=%3F%3F&l=&e=&n=&u= [всем подозрительным - я никоим образом не связан, просто как хороший инструмент, когда я его нахожу ...]
В поисках C# ?? | C# '??' | C# "??" не приносит ожидаемых результатов. Проверяет ли поисковая система, если C# имеет значение NULL, и говорит: «Нет - это на самом деле C# - вот ваши результаты для C# - Да!





Это оператор объединения с нулевым значением и очень похож на тернарный оператор (немедленное если). См. Также ?? Оператор - 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 за указание на это.)
Возможно, опасно связывать эти цепочки
@CodeBlend, это не опасно. Если бы вы развернули, у вас была бы просто серия вложенных операторов if / else. Синтаксис просто странный, потому что вы не привыкли его видеть.
@ jm2 Пока вы просматриваете то, что делаете, например, если вы хотели разделить, а не вложить, если это не подходит. (Прочитав это, я действительно показал мне достойный пример того, как это можно использовать) [rlacovara.blogspot.com/2009/07/…
@CodeBlend ... это не случай "любого" кодирования - всегда проверять :-)
Было бы полезно добавить в этот ответ ссылку на Документация MSDN - чтобы все было вместе. :) Потому что нашел в следующий ответ.
он также назначил бы его всем предыдущим переменным answear #, которые были нулевыми правильными ??
@ Xitcod13 Нет, это не так. Семантически вам нужно будет выполнить Answer1 ??= Answer2, чтобы присвоить значение Answer1, но ??= не является допустимым оператором.
@lc просто потому, что он не очищает воду. Указанного вложенности нет - слияние нулей здесь последовательное. Однако концепция верна.
Было бы здорово, если бы он работал и с пустыми строками :)
@Zyphrax, да. Подобно тому, как || работает в JavaScript.
оценивается ли первый аргумент дважды, если он в первый раз возвращает ненулевое значение? Например: x = f1 () ?? f2 (); будет ли 'f1' оцениваться дважды, когда он в первый раз возвращает ненулевое значение?
@Gusdor ?? является левоассоциативным, поэтому a ?? b ?? c ?? d эквивалентен ((a ?? b) ?? c ) ?? d. «Операторы присваивания и тернарный оператор (? :) являются правоассоциативными. Все остальные бинарные операторы левоассоциативны». Источник: msdn.microsoft.com/en-us/library/ms173145.aspx
Обратите внимание, что расширение не совсем корректно, как показано здесь, поскольку язык гарантирует, что левый операнд оценивается только один раз. В данном конкретном случае это не проблема, но когда у вас есть более сложное выражение, чем локальная переменная слева, это становится важным.
?? предназначен для предоставления значения для типа, допускающего значение NULL, когда значение равно NULL. Итак, если formAuth имеет значение null, он вернет новый FormsAuthenticationWrapper ().
Это нулевой оператор объединения.
http://msdn.microsoft.com/en-us/library/ms173224.aspx
Да, почти невозможно найти, если не знаешь, как это называется! :-)
Обновлено: И это классная функция из другого вопроса. Вы можете связать их.
По крайней мере, сейчас его легко искать, даже если вы не знаете имя, я просто погуглил "двойной вопросительный знак C#"
Это сокращение от тернарного оператора.
FormsAuth = (formsAuth != null) ? formsAuth : new FormsAuthenticationWrapper();
Или для тех, кто не занимается тройным:
if (formsAuth != null)
{
FormsAuth = formsAuth;
}
else
{
FormsAuth = new FormsAuthenticationWrapper();
}
Я только исправил написание слова «троичный», но на самом деле оператор, который вы имеете в виду, является условным оператором. Это единственный тернарный оператор в C#, но в какой-то момент они могут добавить еще один, и тогда «тернарный» будет неоднозначным, а «условный» - нет.
Это сокращение для чего-то, что вы можете делать с тернарным (условным) оператором. В вашей длинной форме можно изменить как тест (!= null), так и второй formsAuth (после ?); в форме объединения NULL оба неявно принимают указанные вами значения.
оператор объединения
это эквивалентно
FormsAuth = formsAUth == null ? new FormsAuthenticationWrapper() : formsAuth
Просто потому, что никто еще не сказал волшебных слов: это нулевой оператор объединения. Это определено в разделе 7.12 Спецификация языка C# 3.0.
Это очень удобно, особенно из-за того, как работает, когда используется несколько раз в выражении. Выражение формы:
a ?? b ?? c ?? d
даст результат выражения a, если он не равен нулю, в противном случае попробуйте b, в противном случае попробуйте c, в противном случае попробуйте d. Короткое замыкание в каждой точке.
Кроме того, если тип d не допускает значения NULL, тип всего выражения также не допускает значения NULL.
Мне нравится, как вы упростили его значение до «нулевого оператора объединения». Это все, что мне было нужно.
Тангенциальный, но связанный: теперь он в Swift, но совсем по-другому: "Nil Coalescing Operator" developer.apple.com/library/ios/documentation/Swift/Conceptu al /…
Всем спасибо, вот самое краткое объяснение, которое я нашел на сайте MSDN:
// y = x, unless x is null, in which case y = -1.
int y = x ?? -1;
Это намекает на важный аспект ?? оператор - он был введен для помощи в работе с типами, допускающими значение NULL. В вашем примере «x» имеет тип «int?» (Обнуляемый <int>).
@vitule нет, если второй операнд оператора объединения NULL не допускает значения NULL, то результат не допускает значения NULL (а -1 - это простой int, который не допускает значения NULL).
Только для вашего развлечения (зная, что вы все ребята из C# ;-).
Я думаю, что он зародился в Smalltalk, где он существует уже много лет. Там он определяется как:
в объекте:
? anArgument
^ self
в UndefinedObject (он же класс nil):
? anArgument
^ anArgument
Существуют как оценочные (?), Так и неоценочные версии (??).
Часто встречается в методах получения для закрытых (экземпляров) переменных с отложенной инициализацией, которые остаются равными нулю до тех пор, пока они действительно не понадобятся.
звучит как упаковка ViewState со свойством в UserControl. Инициализировать только при первом получении, если он не был установлен ранее. знак равно
Если вы знакомы с 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.
Обратите внимание, что ?? заботится только о null, тогда как оператор || в Ruby, как и в большинстве языков, больше касается null, false или всего, что может считаться логическим со значением false (например, в некоторых языках ""). Это не хорошо или плохо, просто разница.
Ничего опасного в этом нет. На самом деле это красиво. Вы можете добавить значение по умолчанию, если это желательно, например:
КОД
int x = x1 ?? x2 ?? x3 ?? x4 ?? 0;
Итак, x1, x2, x3 и x4 могут быть типами, допускающими значение NULL, например: int? x1 = null; Это верно
@KevinMeredith x1 - x4 ДОЛЖНЫ быть типами, допускающими значение NULL: фактически нет смысла говорить, что «результат будет 0, если x4 - это значение, которое он не может принять» (null). «Обнуляемый тип» здесь, конечно, включает как обнуляемые типы значение, так и ссылочные типы. Ошибка времени компиляции, если одна или несколько связанных переменных (кроме последней) не допускают значения NULL.

Два вопросительных знака (??) указывают на то, что это оператор объединения.
Оператор объединения возвращает первое значение NON-NULL из цепочки. Вы можете увидеть это видео на YouTube, который практически все это демонстрирует.
Но позвольте мне добавить больше к тому, что говорится в видео.
Если вы видите английское значение слияния, оно означает «объединяться вместе». Например, ниже приведен простой код объединения, который объединяет четыре строки в цепочку.
Итак, если str1 - это null, он будет пробовать str2, если str2 - это null, он будет пробовать str3 и так далее, пока не найдет строку с ненулевым значением.
string final = str1 ?? str2 ?? str3 ?? str4;
Проще говоря, оператор объединения возвращает первое значение NON-NULL из цепочки.
Мне нравится это объяснение, потому что в нем есть диаграмма, а последнее предложение объясняет это так просто! Это облегчает понимание и повышает мою уверенность в использовании. Спасибо!
когда мы берем только английское значение слова coalesce, мы могли ожидать, что результатом будет своего рода объединение или слияние аргументов. Конечно, это не лучшая метафора того, что он делает на самом деле. Но есть давний предшественник, функция t-sql COALESCE, которая делает то же самое и - о чем могут знать немногие разработчики - имеет список переменных аргументов, поэтому она может принимать более двух значений кандидатов для объединения, как и .СЕТЬ ?? оператор.
Некоторые из приведенных здесь примеров получения значений с помощью объединения неэффективны.
Что вам действительно нужно:
return _formsAuthWrapper = _formsAuthWrapper ?? new FormsAuthenticationWrapper();
или же
return _formsAuthWrapper ?? (_formsAuthWrapper = new FormsAuthenticationWrapper());
Это предотвращает повторное создание объекта каждый раз. Вместо того, чтобы закрытая переменная оставалась нулевой и новый объект создавался при каждом запросе, это обеспечивает присвоение частной переменной при создании нового объекта.
Разве сокращение ?? не оценивается? new FormsAuthenticationWrapper(); оценивается если и только если_formsAuthWrapper равен нулю.
Да в том-то и дело. Вы хотите вызвать метод только в том случае, если переменная равна нулю. @MSalters
Как правильно указано в многочисленных ответах, это «нулевой оператор объединения» (??), говоря о котором, вы также можете проверить его кузена «Нуль-условный оператор» (? или ? [), который является оператором, который много раз используется вместе с ??
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
Есть два общих обстоятельства, при которых хотелось бы знать, значение было присвоено экземпляру типа значения. Первый - это когда экземпляр представляет значение в базе данных. В таком случае хотелось бы иметь возможность исследовать экземпляр, чтобы убедиться, действительно ли значение присутствует в базе данных. Другое обстоятельство, более подходящее для предмета этой книги, - это когда экземпляр представляет собой элемент данных, полученный из некоторого удаленного источника. Опять же, хотелось бы определить по экземпляру, было ли получено значение для этого элемента данных.
.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");
Это действительно здорово, что вы включили примеры в свой пост, хотя вопрос был связан с объяснением того, что оператор или что делает.
Оператор ?? называется оператором объединения с нулем. Он возвращает левый операнд, если операнд не равен нулю; в противном случае возвращается правый операнд.
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 или более поздних версиях.
Это определенно нет тернарный оператор - у него всего два операнда! Это немного похоже на условный оператор (который тернарный является), но нулевой оператор объединения является бинарным оператором.