O que dois pontos de interrogação juntos significam em C #?


1631

Atravessou esta linha de código:

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

O que significam os dois pontos de interrogação, é algum tipo de operador ternário? É difícil procurar no Google.


50
Definitivamente, não é um operador ternário - ele possui apenas dois operandos! É um pouco como o operador condicional (que é ternário), mas o operador coalescente nulo é um operador binário.
11139 Jon Skeet

90
Expliquei em uma entrevista em que o empregador em potencial já havia expressado dúvidas sobre minhas habilidades em C #, pois eu estava usando Java profissionalmente há um tempo. Eles não tinham ouvido falar dele antes, e não questionou minha familiaridade com C #, depois que :)
Jon Skeet

77
@ Jon Skeet Não houve uma falha épica em reconhecer habilidades desde o cara que recusou os Beatles. :-) A partir de agora, envie-lhes uma cópia do seu livro com um link de URL para o seu perfil SO escrito na capa interna.
Titular Iain)

6
IainMH: Por que vale a pena, eu não tinha muito comecei a escrever o livro ainda. (Ou talvez eu só estava trabalhando no capítulo 1 -. Algo parecido) É certo que uma pesquisa para me teria encontrado rapidamente meu blog artigos + etc
Jon Skeet

7
Re: última frase no q - para referência futura, SymbolHound é ótimo para esse tipo de coisa, por exemplo: symbolhound.com/?q=%3F%3F&l=&e=&n=&u= [para alguém suspeito - eu não sou afiliado em qualquer maneira, assim como uma boa ferramenta quando eu encontrar um ...]
Steve Chambers

Respostas:


2156

É o operador coalescente nulo e bem como o operador ternário (se-imediato). Veja também ?? Operador - MSDN .

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

expande para:

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

que se expande ainda mais para:

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

Em inglês, significa "Se o que estiver à esquerda não for nulo, use-o; caso contrário, use o que está à direita".

Observe que você pode usar qualquer número destes em sequência. A seguinte declaração irá atribuir o primeiro não-nula Answer#para Answer(se todas as respostas são nulos então o Answeré null):

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

Também vale a pena mencionar, enquanto a expansão acima é conceitualmente equivalente, o resultado de cada expressão é avaliado apenas uma vez. Isso é importante se, por exemplo, uma expressão for uma chamada de método com efeitos colaterais. (Agradecemos a @Joey por apontar isso.)


17
Potencialmente perigoso acorrentar esses talvez
Coops

6
@CodeBlend como assim?
lc.

68
@ CodeBlend, não é perigoso. Se você fosse expandir, teria apenas uma série de instruções if / else aninhadas. A sintaxe é estranha, porque você não está acostumado a vê-la.
Joelmdev 26/10/11

31
Teria sido um deus-send se ele trabalhou para cadeias vazias, bem :)
Zyphrax

14
@Gusdor ??é deixado associativo, então a ?? b ?? c ?? dé equivalente a ((a ?? b) ?? c ) ?? d. "Os operadores de atribuição e o operador ternário (? :) são associativos à direita. Todos os outros operadores binários são associativos à esquerda." Fonte: msdn.microsoft.com/pt-br/library/ms173145.aspx
Mark E. Haase

284

Só porque ninguém mais disse as palavras mágicas ainda: é o operador coalescente nulo . É definido na seção 7.12 da especificação de idioma do C # 3.0 .

É muito útil, principalmente devido ao modo como funciona quando é usado várias vezes em uma expressão. Uma expressão do formulário:

a ?? b ?? c ?? d

dará o resultado da expressão ase for não nulo; caso contrário b, tente c, caso contrário, tente , caso contrário, tente d. Curto-circuito em todos os pontos.

Além disso, se o tipo de dfor não nulo, o tipo de toda a expressão também não será nulo.


2
Eu gosto de como você simplificou seu significado para "operador coalescente nulo". Isso é tudo que eu precisava.
FRANKO

2
Tangencial, mas relacionados: É agora na Swift, mas é muito diferente: "Nil Coalescing Operator" developer.apple.com/library/ios/documentation/Swift/Conceptual/...
Dan Rosenstark


27

Obrigado a todos, aqui está a explicação mais sucinta que encontrei no site do MSDN:

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

4
Isso sugere um aspecto importante do ?? operador - foi introduzido para ajudar no trabalho com tipos anuláveis. No seu exemplo, "x" é do tipo "int?" (Anulável <int>).
Drew Noakes

9
@vitule no, se o segundo operando do operador coalescente nulo não for anulável, o resultado será não anulável (e -1é apenas uma planície int, que não é anulável).
Suzanne Dupéron

Eu realmente gostaria que funcionasse assim. Quero fazer isso o tempo todo, mas não posso, porque a variável que estou usando não é do tipo anulável. Trabalhos em coffeescript y = x? -1
PandaWood

@PandaWood Na verdade, você pode. Se xé do tipo int?, mas yé do tipo int, você pode escrever int y = (int)(x ?? -1). Ele irá analisar xa um int, se não é null, ou atribuir -1a yse xé null.
Johan Wintgens

21

??existe para fornecer um valor para um tipo anulável quando o valor é nulo. Portanto, se formsAuth for nulo, ele retornará novo FormsAuthenticationWrapper ().


19

insira a descrição da imagem aqui

Os dois pontos de interrogação (??) indicam que é um operador de coalescência.

O operador coalescente retorna o primeiro valor NON-NULL de uma cadeia. Você pode ver este vídeo do youtube que demonstra praticamente tudo.

Mas deixe-me adicionar mais ao que o vídeo diz.

Se você vir o significado em inglês de coalescer, ele diz "consolidar juntos". Por exemplo, abaixo, está um código coalescente simples que encadeia quatro cadeias.

Então, se str1é nullque vai tentar str2, se str2é nullque vai tentar str3e assim por diante até encontrar uma string com um valor não nulo.

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

Em palavras simples, o operador Coalescing retorna o primeiro valor NON-NULL de uma cadeia.


Eu gosto dessa explicação porque ela tem um diagrama e, em seguida, a última frase explica em termos tão simples! Isso facilita a compreensão e aumenta minha confiança no uso. Obrigado!
Joshua K

14

É mão curta para o operador ternário.

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

Ou para aqueles que não fazem ensino ternário:

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

3
Corrigi apenas a ortografia de "ternário", mas na verdade o operador que você quer dizer é o operador condicional. Por acaso, é o único operador ternário em C #, mas em algum momento eles podem adicionar outro. Nesse ponto, "ternário" será ambíguo, mas "condicional" não.
11139 Jon Skeet

É uma abreviação de algo que você pode fazer com o operador ternário (condicional). Em sua forma longa, tanto o teste ( != null) quanto o segundo formsAuth(após o ?) podem ser alterados; na forma de coalescência nula, ambos assumem implicitamente os valores que você forneceu.
Bob Sammers

12

Se você conhece Ruby, ||=parece C # ??para mim. Aqui estão alguns 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"

E em C #:

string str1 = null;
str1 = str1 ?? "new value";
string str2 = "old value";
str2 = str2 ?? "another new value";

4
x ||= ydesugars para algo como x = x || y, então ??é realmente mais parecido com o plain ||em Ruby.
qerub

6
Note-se que ??só se preocupa null, enquanto que o ||operador em Ruby, como na maioria dos idiomas, é mais sobre null, falseou qualquer coisa que pode ser considerado um booleano com um valor de false(por exemplo, em algumas línguas, ""). Isso não é uma coisa boa ou ruim, apenas uma diferença.
Tim S.

11

operador coalescente

é equivalente a

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

11

Nada de perigoso nisso. De fato, é lindo. Você pode adicionar um valor padrão se isso for desejável, por exemplo:

CÓDIGO

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

1
Então x1, x2, x3 e x4 poderia ser tipos anuláveis, exemplo: int? x1 = null;Está certo
Kevin Meredith

1
@KevinMeredith x1- x4DEVE ser do tipo anulável: não faz sentido dizer, efetivamente, "o resultado é 0se x4for um valor que ele não pode aceitar" ( null). "Tipo anulável" aqui inclui tipos de valores anuláveis e tipos de referência, é claro. É um erro em tempo de compilação se uma ou mais das variáveis ​​encadeadas (exceto a última) não forem anuláveis.
Bob Sammers

11

Como apontado corretamente em várias respostas, que é o "operador coalescente nulo" ( ?? ), falando sobre o qual você também pode querer conferir ao primo o "Operador nulo-condicional" ( ?. Ou ? [ ) Que é um operador que muitas vezes é usado em conjunto com ??

Operador nulo-condicional

Usado para testar nulo antes de executar uma operação de acesso de membro ( ?. ) Ou índice ( ? [ ). Esses operadores ajudam a escrever menos código para lidar com verificações nulas, especialmente para a descida nas estruturas de dados.

Por exemplo:

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

o jeito antigo sem ? e ?? de fazer isso é

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

o que é mais detalhado e complicado.


10

Apenas para sua diversão (sabendo que todos vocês são C # guys ;-).

Eu acho que se originou em Smalltalk, onde existe há muitos anos. É definido lá como:

no objeto:

? anArgument
    ^ self

em UndefinedObject (também conhecido como classe nil):

? anArgument
    ^ anArgument

Existem versões de avaliação (?) E não avaliação (??) disso.
É freqüentemente encontrado em métodos getter para variáveis ​​privadas (instância) inicializadas preguiçosamente, que são deixadas nulas até serem realmente necessárias.


parece envolver o ViewState com uma propriedade em um UserControl. Inicialize apenas no primeiro get, se não estiver definido antes. =)
Seiti 15/01/09

9

Alguns dos exemplos aqui de obtenção de valores usando coalescência são ineficientes.

O que você realmente quer é:

return _formsAuthWrapper = _formsAuthWrapper ?? new FormsAuthenticationWrapper();

ou

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

Isso impede que o objeto seja recriado toda vez. Em vez de a variável privada permanecer nula e um novo objeto ser criado em cada solicitação, isso garante que a variável privada seja atribuída se o novo objeto for criado.


O ??atalho não é avaliado? new FormsAuthenticationWrapper();é avaliado se e somente se _formsAuthWrapper for nulo.
MSalters

Sim, esse é o ponto. Você deseja chamar o método apenas se a variável for nula. @MSalters
KingOfHypocrites

8

Nota:

Eu li todo este tópico e muitos outros, mas não consigo encontrar uma resposta tão completa quanto esta.

Pelo qual eu entendi completamente o "por que usar ?? e quando usar ?? e como usar ??."

Fonte:

Fundação de comunicação do Windows desencadeada Por Craig McMurtry ISBN 0-672-32948-4

Tipos de valor anulável

Há duas circunstâncias comuns em que alguém gostaria de saber se um valor foi atribuído a uma instância de um tipo de valor. A primeira é quando a instância representa um valor em um banco de dados. Nesse caso, alguém gostaria de poder examinar a instância para verificar se um valor está realmente presente no banco de dados. A outra circunstância, mais pertinente ao assunto deste livro, é quando a instância representa um item de dados recebido de alguma fonte remota. Novamente, gostaríamos de determinar a partir da instância se um valor para esse item de dados foi recebido.

O .NET Framework 2.0 incorpora uma definição de tipo genérico que fornece casos como esses nos quais se deseja atribuir nulo a uma instância de um tipo de valor e testar se o valor da instância é nulo. Essa definição de tipo genérico é System.Nullable, que restringe os argumentos de tipo genérico que podem ser substituídos por T para valorizar tipos. Instâncias de tipos construídos a partir de System.Nullable podem receber um valor nulo; de fato, seus valores são nulos por padrão. Assim, os tipos construídos a partir de System.Nullable podem ser chamados de tipos de valor anulável. System.Nullable possui uma propriedade, Value, pela qual o valor atribuído a uma instância de um tipo construído a partir dela pode ser obtido se o valor da instância não for nulo. Portanto, pode-se escrever:

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

A linguagem de programação C # fornece uma sintaxe abreviada para declarar tipos criados a partir de System.Nullable. Essa sintaxe permite abreviar:

System.Nullable<int> myNullableInteger;

para

int? myNullableInteger;

O compilador impedirá que alguém tente atribuir o valor de um tipo de valor anulável a um tipo de valor comum desta maneira:

int? myNullableInteger = null;
int myInteger = myNullableInteger;

Isso impede que se faça isso porque o tipo de valor anulável poderia ter o valor nulo, o que realmente teria nesse caso, e esse valor não pode ser atribuído a um tipo de valor comum. Embora o compilador permita esse código,

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

A segunda instrução faria com que uma exceção fosse lançada porque qualquer tentativa de acessar a propriedade System.Nullable.Value é uma operação inválida se o tipo construído a partir de System.Nullable não recebeu um valor válido de T, o que não aconteceu neste caso.

Conclusão:

Uma maneira adequada de atribuir o valor de um tipo de valor nulo a um tipo de valor comum é usar a propriedade System.Nullable.HasValue para verificar se um valor válido de T foi atribuído ao tipo de valor nulo:

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

Outra opção é usar esta sintaxe:

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

Pelo qual o número inteiro ordinário myInteger é atribuído ao valor do número inteiro anulável "myNullableInteger" se este último recebeu um valor inteiro válido; caso contrário, myInteger recebe o valor de -1.


1

É um operador coalescente nulo que funciona de maneira semelhante a um operador ternário.

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

Outro ponto interessante para isso é: "Um tipo anulável pode conter um valor ou pode ser indefinido" . Portanto, se você tentar atribuir um tipo de valor nulo a um tipo de valor nulo, você receberá um erro em tempo de compilação.

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

Então, para fazer isso usando? operador:

z = x ?? 1; // with ?? operator there are no issues

1
FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

é equivalente a

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

Mas o mais legal é que você pode prendê-los, como outras pessoas disseram. O que você não mencionou é que você pode realmente usá-lo para lançar uma exceção.

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

É realmente ótimo que você tenha incluído exemplos em sua postagem, embora a pergunta estivesse procurando uma explicação sobre o que o operador é ou faz.
315

1

O ??operador é chamado de operador de coalescência nula. Retorna o operando esquerdo se o operando não for nulo; caso contrário, ele retornará o operando do lado direito.

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

Defina variable2como o valor de variable1, se variable1NÃO for nulo; caso contrário, se for variable1 == nulldefinido variable2como 100.


0

Outros descreveram Null Coalescing Operatormuito bem. Para os interessados, há uma sintaxe abreviada em que esta (a questão SO):

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

é equivalente a isso:

FormsAuth ??= new FormsAuthenticationWrapper();

Alguns acham mais legível e sucinto.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.