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

1404

Перейдите по этой строке кода:

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

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

  • 45
    Это определенно не троичный оператор - у него только два операнда! Это немного похоже на условный оператор (который является троичным), но оператор слияния нуля является бинарным оператором.
  • 84
    Я объяснил это в интервью, где потенциальный работодатель ранее выражал сомнения по поводу моих способностей в C #, поскольку я уже некоторое время профессионально использовал Java. Они не слышали об этом раньше и не ставили под сомнение мое знакомство с C # после этого :)
Показать ещё 8 комментариев
Теги:
null-coalescing-operator

17 ответов

1797
Лучший ответ

Это нулевой оператор слияния, и совсем как троичный (немедленный-если) оператор. Смотрите также ?? Оператор - 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 за указание на это.)

  • 347
    не знал, что можно связать эти
  • 15
    Потенциально опасно зацепить их
Показать ещё 25 комментариев
251

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

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

a ?? b ?? c ?? d

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

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

  • 1
    Мне нравится, как вы упростили его значение для "оператора слияния". Это все, что мне нужно.
  • 2
    Тангенциальный, но связанный: теперь он в Swift, но он совсем другой: «Оператор слияния ноль» developer.apple.com/library/ios/documentation/Swift/Conceptual/…
64

Это нулевой оператор слияния.

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

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

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

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

  • 26
    По крайней мере, теперь, это легко найти, даже если вы не знаете имя, я просто погуглил "двойной знак вопроса c #"
24

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

// y = x, unless x is null, in which case y = -1.
int y = x ?? -1;
  • 4
    Это намекает на важный аспект оператор - был введен для помощи в работе с обнуляемыми типами. В вашем примере "x" имеет тип "int?" (Nullable <Int>).
  • 3
    Я думаю, что фрагмент кода должен читать: int? у = х ?? -1;
Показать ещё 2 комментария
20

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

18

Изображение 954

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

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

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

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

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

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

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

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

Если вы знакомы с Ruby, его ||= мне кажется сродни С# ??. Вот некоторые 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"

И в С#:

string str1 = null;
str1 = str1 ?? "new value";
string str2 = "old value";
str2 = str2 ?? "another new value";
  • 4
    x ||= y означает, что x = x || y , так ?? на самом деле больше похож на обычный || в рубине.
  • 6
    Обратите внимание, что ?? заботится только о null , тогда как || Оператор в Ruby, как и в большинстве языков, больше относится к null , false или ко всему, что можно считать логическим значением false (например, в некоторых языках "" ). Это не хорошо или плохо, просто разница.
11

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

CODE

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

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

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

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

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

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

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

в Object:

? anArgument
    ^ self

в UndefinedObject (aka nil class):

? anArgument
    ^ anArgument

Есть и оценка (?) и неоценивающих версий (??) этого.
Он часто встречается в методах getter для lazy-initialized private (instance) переменных, которые остаются нулевыми до тех пор, пока они действительно не нужны.

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

коалесцирующий оператор

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

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

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

Что вы действительно хотите:

return _formsAuthWrapper = _formsAuthWrapper ?? new FormsAuthenticationWrapper();

или

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

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

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

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

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

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

Например:

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

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

7

Примечание:

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

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

Источник:

Основы коммуникаций Windows, выпущенные Craig McMurtry ISBN 0-672-32948-4

Типы недопустимых значений

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

В .NET Framework 2.0 используется общее определение типа, которое предусматривает такие случаи, когда требуется назначить значение 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);
}

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

System.Nullable<int> myNullableInteger;

к

int? myNullableInteger;

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

int? myNullableInteger = null;
int myInteger = myNullableInteger;

Это не позволяет сделать это, потому что тип значения с нулевым значением может иметь значение 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. ​​

0

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

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

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

0
FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

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

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

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

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

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

    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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню