Cosa significano insieme due punti interrogativi in ​​C #?


1632

Ho attraversato questa riga di codice:

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

Che cosa significano i due punti interrogativi, è una specie di operatore ternario? È difficile cercare su Google.


50
E 'sicuramente non un operatore ternario - ha solo due operandi! È un po 'come l'operatore condizionale (che è ternario) ma l'operatore a coalescenza nulla è un operatore binario.
Jon Skeet,

90
L'ho spiegato in un'intervista in cui il potenziale datore di lavoro aveva precedentemente espresso dubbi sulle mie capacità di C #, poiché avevo usato Java professionalmente per un po 'di tempo prima. Non ne avevano mai sentito parlare prima, e non hanno messo in dubbio la mia familiarità con C # dopo :)
Jon Skeet,

77
@Jon Skeet Non c'è stato un tale fallimento epico nel riconoscere l'abilità da quando ha rifiutato i Beatles. :-) D'ora in poi basta inviare loro una copia del tuo libro con un link url al tuo profilo SO scritto sulla copertina interna.
Iain Holder,

6
IainMH: Per quello che vale, non avevo abbastanza iniziato a scrivere il libro ancora. (O forse stavo solo lavorando al capitolo 1 - qualcosa del genere.) Certo una ricerca per me avrebbe rapidamente trovato il mio blog + articoli ecc.
Jon Skeet,

7
Ri: ultima frase nella q - per riferimento futuro, SymbolHound è ottimo per questo tipo di cose, ad esempio symbolhound.com/?q=%3F%3F&l=&e=&n=&u= [per chiunque sia sospetto - Non sono affiliato a in ogni caso, proprio come un buon strumento quando ne trovo uno ...]
Steve Chambers

Risposte:


2156

È l'operatore null coalescente, e abbastanza come l'operatore ternario (immediato). Vedi anche ?? Operatore - MSDN .

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

si espande in:

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

che si espande ulteriormente a:

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

In inglese significa "Se ciò che è a sinistra non è nullo, usalo, altrimenti usa ciò che è a destra."

Si noti che è possibile utilizzare qualsiasi numero di questi in sequenza. La seguente istruzione assegnerà il primo non null Answer#a Answer(se tutte le risposte sono null, allora Answerè null):

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

Inoltre vale la pena ricordare che l'espansione sopra è concettualmente equivalente, il risultato di ogni espressione viene valutato solo una volta. Ciò è importante se ad esempio un'espressione è una chiamata di metodo con effetti collaterali. (Ringraziamo @Joey per averlo sottolineato.)


17
Potenzialmente pericoloso incatenare questi forse
Coops il

6
@CodeBlend come?
lc.

68
@CodeBlend, non è pericoloso. Se dovessi espandersi avresti solo una serie di istruzioni if ​​/ else nidificate. La sintassi è semplicemente strana perché non sei abituato a vederlo.
joelmdev,

31
Sarebbe stato un
rinvio

14
@Gusdor ??è associativo, quindi a ?? b ?? c ?? dequivale a ((a ?? b) ?? c ) ?? d. "Gli operatori di assegnazione e l'operatore ternario (? :) sono associativi di destra. Tutti gli altri operatori binari sono associativi di sinistra." Fonte: msdn.microsoft.com/en-us/library/ms173145.aspx
Mark E. Haase,

284

Solo perché nessun altro ha ancora detto le parole magiche: è l' operatore null coalescente . È definito nella sezione 7.12 della specifica del linguaggio C # 3.0 .

È molto utile, soprattutto per il modo in cui funziona quando viene usato più volte in un'espressione. Un'espressione del modulo:

a ?? b ?? c ?? d

darà il risultato dell'espressione ase è diverso da null, altrimenti prova b, altrimenti prova c, altrimenti prova d. Cortocircuita in ogni punto.

Inoltre, se il tipo di dè non annullabile, anche il tipo dell'intera espressione è non annullabile.


2
Mi piace il modo in cui hai semplificato il suo significato di "operatore a coalescenza nulla". Questo è tutto ciò di cui avevo bisogno.
FrankO

2
Tangenziale ma correlato: ora è in Swift, ma è molto diverso: "Nil Coalescing Operator" developer.apple.com/library/ios/documentation/Swift/Conceptual/…
Dan Rosenstark


27

Grazie a tutti, ecco la spiegazione più concisa che ho trovato sul sito MSDN:

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

4
Questo suggerisce un aspetto importante del ?? operatore - è stato introdotto per aiutare a lavorare con tipi nullable. Nel tuo esempio, "x" è di tipo "int?" (Nullable <int>).
Drew Noakes,

9
@vitule no, se il secondo operando dell'operatore di coalescenza null è non annullabile, il risultato è non annullabile (ed -1è solo un semplice int, che è non annullabile).
Suzanne Dupéron,

Vorrei davvero che funzionasse così. Voglio farlo tutto il tempo, ma non posso perché la variabile che sto usando non è un tipo nullable. Funziona in coffeescript y = x? -1
PandaWood

@PandaWood In realtà, puoi. Se xè di tipo int?, ma yè di tipo int, è possibile scrivere int y = (int)(x ?? -1). Analizzerà xun intse non lo è null, o assegnerà -1a yse lo xè null.
Johan Wintgens,

21

??è lì per fornire un valore per un tipo nullable quando il valore è null. Quindi, se formsAuth è null, restituirà new FormsAuthenticationWrapper ().


19

inserisci qui la descrizione dell'immagine

I due punti interrogativi (??) indicano che si tratta di un operatore di coalescenza.

L'operatore di coalescenza restituisce il primo valore NON-NULL da una catena. Puoi vedere questo video di YouTube che dimostra praticamente tutto.

Vorrei aggiungere altro a ciò che dice il video.

Se vedi il significato inglese di coalescenza, dice "consolidare insieme". Ad esempio, di seguito è riportato un semplice codice a coalescenza che incatena quattro stringhe.

Quindi se str1è nullproverà str2, se str2è nullproverà str3e così via fino a quando non trova una stringa con un valore non nullo.

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

In parole semplici, l'operatore di coalescenza restituisce il primo valore NON-NULL da una catena.


Mi piace questa spiegazione perché ha un diagramma, e poi l'ultima frase lo spiega in termini così semplici! Rende facile la comprensione e aumenta la mia fiducia nell'uso. Grazie!
Joshua K,

14

È una scorciatoia per l'operatore ternario.

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

O per coloro che non fanno ternario:

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

3
Ho corretto solo l'ortografia di "ternario", ma in realtà l'operatore che intendi è l'operatore condizionale. Capita di essere l'unico operatore ternario in C #, ma a un certo punto potrebbero aggiungerne un altro, a quel punto "ternario" sarà ambiguo ma "condizionale" no.
Jon Skeet,

È una scorciatoia per qualcosa che puoi fare con l'operatore ternario (condizionale). Nella tua forma lunga, sia il test ( != null) che il secondo formsAuth(dopo il ?) potrebbero essere modificati; nella forma di coalescenza nulla, entrambi implicitamente assumono i valori forniti.
Bob Sammers,

12

Se hai familiarità con Ruby, ||=sembra simile a C # ??per me. Ecco un po 'di rubino:

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 in C #:

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

4
x ||= ydesugars a qualcosa del genere x = x || y, quindi in ??realtà è più simile alla pianura ||in Ruby.
qerub,

6
Si noti che ??solo si preoccupa null, mentre l' ||operatore in Ruby, come nella maggior parte delle lingue, è più su null, falseo tutto ciò che può essere considerato un valore booleano con un valore di false(ad esempio, in alcune lingue, ""). Questa non è una cosa buona o cattiva, solo una differenza.
Tim S.

11

operatore a coalescenza

è equivalente a

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

11

Niente di pericoloso al riguardo. In effetti è bellissimo. È possibile aggiungere un valore predefinito se ciò è desiderabile, ad esempio:

CODICE

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

1
Quindi x1, x2, x3 e x4 potrebbero essere tipi Nullable, esempio: int? x1 = null;è giusto
Kevin Meredith,

1
@KevinMeredith x1- x4DEVE essere tipi nullable: non ha senso dire, in effetti, "il risultato è 0se x4è un valore che non può assumere" ( null). "Tipo Nullable" qui include sia i tipi di valore annullabili sia i tipi di riferimento, ovviamente. È un errore in fase di compilazione se una o più variabili incatenate (tranne l'ultima) non è nulla.
Bob Sammers,

11

Come indicato correttamente in numerose risposte, si tratta dell '"operatore a coalescenza nulla" ( ?? ), a proposito del quale potresti anche voler dare un'occhiata a suo cugino "Operatore a condizione nulla" ( ?. O ? [ ) Che è un operatore che molte volte è usato insieme a ??

Operatore nullo-condizionale

Utilizzato per verificare la presenza di null prima di eseguire un'operazione di accesso ( ?. ) O indice ( ? [ ) Del membro . Questi operatori ti aiutano a scrivere meno codice per gestire i controlli null, in particolare per la discesa nelle strutture di dati.

Per esempio:

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

alla vecchia maniera senza ? e ?? di fare questo è

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

che è più prolisso e ingombrante.


10

Solo per il tuo divertimento (sapendo che siete tutti ragazzi C # ;-).

Penso che abbia avuto origine in Smalltalk, dove esiste da molti anni. È definito lì come:

nell'oggetto:

? anArgument
    ^ self

in UndefinedObject (aka classe zero):

? anArgument
    ^ anArgument

Esistono versioni di valutazione (?) E non di valutazione (??).
Si trova spesso nei metodi getter per variabili private (istanza) inizializzate in modo pigro, che vengono lasciate nulle fino a quando non sono realmente necessarie.


sembra avvolgere ViewState con una proprietà su UserControl. Inizializza solo al primo get, se non è stato impostato prima. =)
Seiti,

9

Alcuni esempi qui di ottenere valori usando la coalescenza sono inefficienti.

Quello che vuoi davvero è:

return _formsAuthWrapper = _formsAuthWrapper ?? new FormsAuthenticationWrapper();

o

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

Ciò impedisce di ricreare l'oggetto ogni volta. Invece che la variabile privata rimanga nulla e un nuovo oggetto venga creato su ogni richiesta, questo assicura che la variabile privata venga assegnata se viene creato il nuovo oggetto.


La ??scorciatoia non viene valutata? new FormsAuthenticationWrapper();viene valutato se e solo se _formsAuthWrapper è nullo.
Salterio

Sì, questo è il punto. Si desidera chiamare il metodo solo se la variabile è nulla. @MSalters
KingOfHypocrites

8

Nota:

Ho letto tutto questo thread e molti altri, ma non riesco a trovare una risposta completa come questa.

Con il quale ho capito completamente il "perché usare ?? e quando usare ?? e come usare ??."

Fonte:

Fondazione di comunicazione Windows scatenata da Craig McMurtry ISBN 0-672-32948-4

Tipi di valori nullable

Esistono due circostanze comuni in cui si vorrebbe sapere se un valore è stato assegnato a un'istanza di un tipo di valore. Il primo è quando l'istanza rappresenta un valore in un database. In tal caso, si vorrebbe poter esaminare l'istanza per accertare se un valore è effettivamente presente nel database. L'altra circostanza, che è più pertinente all'argomento di questo libro, è quando l'istanza rappresenta un elemento di dati ricevuto da una fonte remota. Ancora una volta, si vorrebbe determinare dall'istanza se è stato ricevuto un valore per quell'elemento dati.

.NET Framework 2.0 incorpora una definizione di tipo generico che prevede casi come questi in cui si desidera assegnare null a un'istanza di un tipo di valore e verificare se il valore dell'istanza è null. Tale definizione di tipo generico è System.Nullable, che limita gli argomenti di tipo generico che possono essere sostituiti da T a tipi di valore. Alle istanze di tipi costruite da System.Nullable può essere assegnato un valore null; in effetti, i loro valori sono nulli per impostazione predefinita. Pertanto, i tipi costruiti da System.Nullable possono essere definiti tipi di valore nullable. System.Nullable ha una proprietà, Value, in base alla quale il valore assegnato a un'istanza di un tipo costruito da essa può essere ottenuto se il valore dell'istanza non è nullo. Pertanto, si può scrivere:

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

Il linguaggio di programmazione C # fornisce una sintassi abbreviata per dichiarare i tipi costruiti da System.Nullable. Tale sintassi consente di abbreviare:

System.Nullable<int> myNullableInteger;

per

int? myNullableInteger;

Il compilatore impedirà a uno di tentare di assegnare il valore di un tipo di valore nullable a un tipo di valore ordinario in questo modo:

int? myNullableInteger = null;
int myInteger = myNullableInteger;

Impedisce di farlo perché il tipo di valore nullable potrebbe avere il valore null, che in realtà avrebbe in questo caso, e quel valore non può essere assegnato a un tipo di valore ordinario. Sebbene il compilatore consentirebbe questo codice,

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

La seconda istruzione provocherebbe il lancio di un'eccezione perché qualsiasi tentativo di accedere alla proprietà System.Nullable.Value è un'operazione non valida se al tipo costruito da System.Nullable non è stato assegnato un valore valido di T, che non è avvenuto in questo Astuccio.

Conclusione:

Un modo corretto di assegnare il valore di un tipo di valore nullable a un tipo di valore ordinario è utilizzare la proprietà System.Nullable.HasValue per accertare se un valore valido di T è stato assegnato al tipo di valore nullable:

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

Un'altra opzione è utilizzare questa sintassi:

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

In base al quale al numero intero ordinario myInteger viene assegnato il valore del numero intero nullable "myNullableInteger" se a quest'ultimo è stato assegnato un valore intero valido; altrimenti, a myInteger viene assegnato il valore di -1.


1

È un operatore a coalescenza nulla che funziona in modo simile a un operatore ternario.

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

Un altro punto interessante per questo è: "Un tipo nullable può contenere un valore o può essere indefinito" . Pertanto, se si tenta di assegnare un tipo di valore annullabile a un tipo di valore non annullabile, verrà visualizzato un errore in fase di compilazione.

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

Quindi per farlo usando ?? operatore:

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

1
FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

è equivalente a

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

Ma la cosa bella è che puoi incatenarli, come hanno detto altre persone. L'unico aspetto non toccato è che puoi effettivamente usarlo per generare un'eccezione.

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

È davvero bello che tu abbia incluso esempi nel tuo post, anche se la domanda cercava una spiegazione di ciò che l'operatore è o fa.
TGP1994

1

L' ??operatore è chiamato operatore a coalescenza nulla. Restituisce l'operando di sinistra se l'operando non è nullo; altrimenti restituisce l'operando di destra.

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

Impostato variable2sul valore di variable1, se variable1NON è null; altrimenti, se variable1 == nullimpostato variable2su 100.


0

Altri hanno descritto Null Coalescing Operatorabbastanza bene. Per coloro che sono interessati, esiste una sintassi abbreviata in cui questa (la domanda SO):

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

è equivalente a questo:

FormsAuth ??= new FormsAuthenticationWrapper();

Alcuni lo trovano più leggibile e succinto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.