Was bedeuten zwei Fragezeichen zusammen in C #?


1629

Bin über diese Codezeile gelaufen:

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

Was bedeuten die beiden Fragezeichen, ist es eine Art ternärer Operator? Es ist schwer, in Google nachzuschlagen.


50
Es ist definitiv kein ternärer Operator - es hat nur zwei Operanden! Es ist ein bisschen wie der Bedingungsoperator (das ist ternär) , aber der Null - Koaleszenz Operator ein binärer Operator ist.
Jon Skeet

90
Ich erklärte es in einem Interview, in dem der potenzielle Arbeitgeber zuvor Zweifel an meinen C # -Fähigkeiten geäußert hatte, da ich Java bereits eine Weile professionell verwendet hatte. Sie hatten noch nie davon gehört und meine Vertrautheit mit C # danach nicht in Frage gestellt :)
Jon Skeet

77
@ Jon Skeet Seit dem Typ, der die Beatles abgelehnt hat, hat es kein so episches Versagen gegeben, Fähigkeiten zu erkennen. :-) Senden Sie ihnen von nun an einfach eine Kopie Ihres Buches mit einem URL-Link zu Ihrem SO-Profil auf der Innenseite.
Iain Holder

6
IainMH: Für das, was es wert ist, hatte ich noch nicht ganz angefangen, das Buch zu schreiben. (Oder vielleicht habe ich gerade an Kapitel 1 gearbeitet - so ähnlich.) Zugegeben, eine Suche nach mir hätte schnell meinen Blog + Artikel usw. gefunden
Jon Skeet

7
Betreff: letzter Satz im q - für zukünftige Verweise ist SymbolHound großartig für solche Dinge, z. B. symbolhound.com/?q=%3F%3F&l=&e=&n=&u= [für jeden, der verdächtig ist - ich bin nicht angeschlossen Wie auch immer, genau wie ein gutes Werkzeug, wenn ich eines finde ...]
Steve Chambers

Antworten:


2155

Es ist der Null-Koaleszenz-Operator und ganz ähnlich dem ternären (Sofort-Wenn-) Operator. Siehe auch ?? Betreiber - MSDN .

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

erweitert sich zu:

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

was sich weiter ausdehnt auf:

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

Auf Englisch bedeutet dies: "Wenn das, was links ist, nicht null ist, verwenden Sie das, andernfalls verwenden Sie das, was rechts ist."

Beachten Sie, dass Sie eine beliebige Anzahl davon nacheinander verwenden können. Die folgende Anweisung weist die erste Nicht-Null Answer#zu Answer(wenn alle Antworten null sind, Answerist die null):

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

Erwähnenswert ist auch, dass die obige Erweiterung zwar konzeptionell äquivalent ist, das Ergebnis jedes Ausdrucks jedoch nur einmal ausgewertet wird. Dies ist wichtig, wenn ein Ausdruck beispielsweise ein Methodenaufruf mit Nebenwirkungen ist. (Dank an @Joey für den Hinweis.)


17
Möglicherweise gefährlich, diese zu verketten
Coops

6
@ CodeBlend wie so?
lc.

68
@ CodeBlend, es ist nicht gefährlich. Wenn Sie erweitern würden, hätten Sie nur eine Reihe verschachtelter if / else-Anweisungen. Die Syntax ist nur seltsam, weil Sie es nicht gewohnt sind, sie zu sehen.
Joelmdev

31
Es wäre ein
Geschenk

14
@Gusdor ??bleibt assoziativ, a ?? b ?? c ?? dentspricht also ((a ?? b) ?? c ) ?? d. "Die Zuweisungsoperatoren und der ternäre Operator (? :) sind rechtsassoziativ. Alle anderen binären Operatoren sind linksassoziativ." Quelle: msdn.microsoft.com/en-us/library/ms173145.aspx
Mark E. Haase

284

Nur weil noch niemand die magischen Worte gesagt hat: Es ist der Null-Koaleszenz-Operator . Es ist in Abschnitt 7.12 der C # 3.0-Sprachspezifikation definiert .

Es ist sehr praktisch, insbesondere aufgrund der Funktionsweise, wenn es in einem Ausdruck mehrmals verwendet wird. Ein Ausdruck der Form:

a ?? b ?? c ?? d

gibt das Ergebnis des Ausdrucks aus, awenn es nicht null ist, andernfalls versuchen b, andernfalls versuchen c, andernfalls versuchen d. Es schließt an jedem Punkt kurz.

Wenn der Typ von dnicht nullbar ist, ist auch der Typ des gesamten Ausdrucks nicht nullwertfähig.


2
Mir gefällt, wie Sie die Bedeutung des Begriffs "Null-Koaleszenz-Operator" vereinfacht haben. Das ist alles was ich brauchte.
FrankO

2
Tangential aber verwandt: Es ist jetzt in Swift, aber es ist ganz anders: "Nil Coalescing Operator" developer.apple.com/library/ios/documentation/Swift/Conceptual/…
Dan Rosenstark


27

Vielen Dank an alle, hier ist die prägnanteste Erklärung, die ich auf der MSDN-Website gefunden habe:

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

4
Dies deutet auf einen wichtigen Aspekt des ?? operator - Es wurde eingeführt, um die Arbeit mit nullbaren Typen zu erleichtern. In Ihrem Beispiel ist "x" vom Typ "int?" (Nullable <int>).
Drew Noakes

9
@vitule nein, wenn der zweite Operand des Null-Koaleszenzoperators nicht nullbar ist, ist das Ergebnis nicht nullwertfähig (und -1ist nur eine Ebene int, die nicht nullwertfähig ist).
Suzanne Dupéron

Ich wünschte wirklich, es hätte so funktioniert. Ich möchte dies die ganze Zeit tun, kann es aber nicht, da die von mir verwendete Variable kein nullbarer Typ ist. Funktioniert in Coffeescript y = x? -1
PandaWood

@ PandaWood Eigentlich kannst du. Wenn xes vom Typ ist int?, aber yvom Typ int, können Sie schreiben int y = (int)(x ?? -1). Es wird analysiert x, intwenn dies nicht der Fall ist null, oder zugewiesen -1, ywenn dies der Fall xist null.
Johan Wintgens

21

??dient dazu, einen Wert für einen nullbaren Typ bereitzustellen, wenn der Wert null ist. Wenn FormsAuth also null ist, wird neuer FormsAuthenticationWrapper () zurückgegeben.


19

Geben Sie hier die Bildbeschreibung ein

Die beiden Fragezeichen (??) zeigen an, dass es sich um einen Coalescing-Operator handelt.

Der Koaleszenzoperator gibt den ersten NON-NULL-Wert aus einer Kette zurück. Sie können dieses Youtube-Video sehen, das das Ganze praktisch demonstriert.

Aber lassen Sie mich noch mehr zu dem hinzufügen, was das Video sagt.

Wenn Sie die englische Bedeutung der Verschmelzung sehen, heißt es "zusammen konsolidieren". Im Folgenden finden Sie beispielsweise einen einfachen Koaleszenzcode, der vier Zeichenfolgen verkettet.

Also , wenn str1ist nulles versuchen str2, wenn str2ist nullwird es versuchen , str3und so weiter , bis er eine Saite mit einem Nicht-Null - Wert findet.

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

Mit einfachen Worten, der Coalescing-Operator gibt den ersten NON-NULL-Wert aus einer Kette zurück.


Ich mag diese Erklärung, weil sie ein Diagramm hat, und dann erklärt der letzte Satz sie in so einfachen Worten! Es macht es leicht zu verstehen und stärkt mein Vertrauen in die Verwendung. Vielen Dank!
Joshua K

14

Es ist eine kurze Hand für den ternären Operator.

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

Oder für diejenigen, die nicht ternär sind:

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

3
Ich habe nur die Schreibweise von "ternär" korrigiert, aber der Operator, den Sie meinen, ist der bedingte Operator. Es ist zufällig der einzige ternäre Operator in C #, aber irgendwann können sie einen weiteren hinzufügen. An diesem Punkt ist "ternär" mehrdeutig, "bedingt" jedoch nicht.
Jon Skeet

Es ist eine Abkürzung für etwas, das Sie mit dem ternären (bedingten) Operator tun können . In Ihrer langen Form können sowohl der Test ( != null) als auch der zweite formsAuth(nach dem ?) geändert werden. In der Null-Koaleszenz-Form nehmen beide implizit die von Ihnen angegebenen Werte an.
Bob Sammers

12

Wenn Sie mit Ruby vertraut sind, ||=scheint ??es mir C # zu ähneln. Hier ist etwas 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"

Und in C #:

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

4
x ||= yDesugars zu so etwas wie x = x || y, ??ist also eigentlich eher einfach ||in Ruby.
Qerub

6
Beachten Sie, dass ??nur Sorgen über null, während der ||Betreiber in Ruby, wie in den meisten Sprachen, mehr über ist null, falseoder alles , was ein boolean mit einem Wert von betrachtet werden kann false(zB in einigen Sprachen ""). Dies ist keine gute oder schlechte Sache, nur ein Unterschied.
Tim S.

11

Koaleszenzoperator

es ist äquivalent zu

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

11

Daran ist nichts Gefährliches. In der Tat ist es schön. Sie können einen Standardwert hinzufügen, wenn dies wünschenswert ist, zum Beispiel:

CODE

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

1
Also könnten x1, x2, x3 und x4 nullbare Typen sein, Beispiel: int? x1 = null;Ist das richtig
Kevin Meredith

1
@ KevinMeredith x1- x4MUSS nullfähige Typen sein: Es macht keinen Sinn, effektiv zu sagen: "Das Ergebnis ist, 0wenn x4es sich um einen Wert handelt, den es unmöglich annehmen kann" ( null). "Nullable type" umfasst hier natürlich sowohl nullable value- Typen als auch Referenztypen. Es ist ein Fehler beim Kompilieren, wenn eine oder mehrere der verketteten Variablen (mit Ausnahme der letzten) nicht nullwertfähig sind.
Bob Sammers

11

Wie in zahlreichen Antworten richtig angegeben, handelt es sich um den "Null-Koaleszenz-Operator" ( ?? ), von dem Sie vielleicht auch seinen Cousin, den "Null-bedingten Operator" ( ?. Oder ? [ ), Der ein Operator ist, überprüfen möchten oft wird es in Verbindung mit verwendet ?

Nullbedingter Operator

Wird verwendet, um vor dem Ausführen einer Mitgliedszugriffs- ( ?. ) Oder Indexoperation ( ? [ ) Auf Null zu testen . Mit diesen Operatoren können Sie weniger Code schreiben, um Nullprüfungen durchzuführen, insbesondere beim Abstieg in Datenstrukturen.

Zum Beispiel:

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

der alte Weg ohne ? und ?? dies zu tun ist

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

Das ist ausführlicher und umständlicher.


10

Nur zu Ihrer Unterhaltung (zu wissen, dass Sie alle C # -Typen sind ;-).

Ich denke, es hat seinen Ursprung in Smalltalk, wo es es schon seit vielen Jahren gibt. Es ist dort definiert als:

im Objekt:

? anArgument
    ^ self

in UndefinedObject (auch bekannt als Nullklasse):

? anArgument
    ^ anArgument

Es gibt sowohl evaluierende (?) Als auch nicht evaluierende Versionen (??) davon.
Es wird häufig in Getter-Methoden für faul initialisierte private (Instanz-) Variablen gefunden, die bis zur tatsächlichen Verwendung gleich Null bleiben.


klingt wie das Umschließen von ViewState mit einer Eigenschaft in einem UserControl. Initialisieren Sie nur beim ersten Abrufen, wenn es nicht vorher eingestellt wurde. =)
Seiti

9

Einige der hier aufgeführten Beispiele zum Abrufen von Werten mithilfe der Koaleszenz sind ineffizient.

Was Sie wirklich wollen, ist:

return _formsAuthWrapper = _formsAuthWrapper ?? new FormsAuthenticationWrapper();

oder

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

Dies verhindert, dass das Objekt jedes Mal neu erstellt wird. Anstatt dass die private Variable null bleibt und bei jeder Anforderung ein neues Objekt erstellt wird, wird sichergestellt, dass die private Variable zugewiesen wird, wenn das neue Objekt erstellt wird.


Wird die ??Abkürzung nicht ausgewertet? new FormsAuthenticationWrapper();wird genau dann ausgewertet, wenn_formsAuthWrapper Null ist.
MSalters

Ja, das ist der springende Punkt. Sie möchten die Methode nur aufrufen, wenn die Variable null ist. @ MSalters
KingOfHypocrites

8

Hinweis:

Ich habe diesen ganzen Thread und viele andere gelesen, aber ich kann keine so gründliche Antwort finden wie diese.

Womit ich das "Warum zu verwenden? Und wann zu verwenden? Und wie zu verwenden?" Vollständig verstand.

Quelle:

Windows-Kommunikationsgrundlage entfesselt Von Craig McMurtry ISBN 0-672-32948-4

Nullable Werttypen

Es gibt zwei häufige Umstände, unter denen man wissen möchte, ob einer Instanz eines Wertetyps ein Wert zugewiesen wurde. Die erste ist, wenn die Instanz einen Wert in einer Datenbank darstellt. In einem solchen Fall möchte man die Instanz untersuchen können, um festzustellen, ob tatsächlich ein Wert in der Datenbank vorhanden ist. Der andere Umstand, der für den Gegenstand dieses Buches relevanter ist, besteht darin, dass die Instanz ein Datenelement darstellt, das von einer entfernten Quelle empfangen wurde. Auch hier möchte man aus der Instanz ermitteln, ob ein Wert für dieses Datenelement empfangen wurde.

Das .NET Framework 2.0 enthält eine generische Typdefinition, die Fälle wie diese vorsieht, in denen einer Instanz eines Wertetyps null zugewiesen werden soll und geprüft werden soll, ob der Wert der Instanz null ist. Diese generische Typdefinition ist System.Nullable, wodurch die generischen Typargumente, die T ersetzen können, auf Werttypen beschränkt werden. Instanzen von Typen, die aus System.Nullable erstellt wurden, können den Wert null erhalten. In der Tat sind ihre Werte standardmäßig null. Aus System.Nullable erstellte Typen können daher als nullbare Werttypen bezeichnet werden. System.Nullable hat die Eigenschaft Value, mit der der einer Instanz eines daraus erstellten Typs zugewiesene Wert abgerufen werden kann, wenn der Wert der Instanz nicht null ist. Daher kann man schreiben:

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

Die Programmiersprache C # bietet eine abgekürzte Syntax zum Deklarieren von Typen, die aus System.Nullable erstellt wurden. Diese Syntax erlaubt es, abzukürzen:

System.Nullable<int> myNullableInteger;

zu

int? myNullableInteger;

Der Compiler verhindert, dass einer versucht, den Wert eines nullbaren Werttyps einem normalen Werttyp auf folgende Weise zuzuweisen:

int? myNullableInteger = null;
int myInteger = myNullableInteger;

Dies wird verhindert, da der nullbare Werttyp den Wert null haben könnte, den er in diesem Fall tatsächlich hätte, und dieser Wert keinem normalen Werttyp zugewiesen werden kann. Obwohl der Compiler diesen Code zulassen würde,

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

Die zweite Anweisung würde dazu führen, dass eine Ausnahme ausgelöst wird, da jeder Versuch, auf die Eigenschaft System.Nullable.Value zuzugreifen, eine ungültige Operation ist, wenn dem aus System.Nullable erstellten Typ kein gültiger Wert von T zugewiesen wurde, was in diesem Fall nicht geschehen ist Fall.

Fazit:

Eine geeignete Methode zum Zuweisen des Werts eines nullbaren Werttyps zu einem normalen Werttyp besteht darin, mithilfe der System.Nullable.HasValue-Eigenschaft festzustellen, ob dem nullbaren Werttyp ein gültiger Wert von T zugewiesen wurde:

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

Eine andere Option ist die Verwendung dieser Syntax:

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

Womit der gewöhnlichen Ganzzahl myInteger der Wert der nullbaren Ganzzahl "myNullableInteger" zugewiesen wird, wenn dieser ein gültiger Ganzzahlwert zugewiesen wurde; Andernfalls wird myInteger der Wert -1 zugewiesen.


1

Es ist ein Null-Koaleszenz-Operator, der ähnlich wie ein ternärer Operator funktioniert.

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

Ein weiterer interessanter Punkt hierfür ist: "Ein nullfähiger Typ kann einen Wert enthalten oder undefiniert sein" . Wenn Sie also versuchen, einem nicht nullbaren Werttyp einen nullbaren Werttyp zuzuweisen, wird ein Fehler bei der Kompilierung angezeigt.

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

Also, um das mit ?? Operator:

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

1
FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

ist äquivalent zu

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

Aber das Coole daran ist, dass man sie verketten kann, wie andere Leute sagten. Das einzige, was nicht angesprochen wird, ist, dass Sie es tatsächlich verwenden können, um eine Ausnahme auszulösen.

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

Es ist wirklich großartig, dass Sie Beispiele in Ihren Beitrag aufgenommen haben, obwohl die Frage nach einer Erklärung suchte, was der Bediener ist oder tut.
TGP1994

1

Der ??Operator wird als Null-Koaleszenz-Operator bezeichnet. Es gibt den linken Operanden zurück, wenn der Operand nicht null ist. Andernfalls wird der rechte Operand zurückgegeben.

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

Auf variable2den Wert setzen variable1, wenn variable1NICHT null ist; Andernfalls, wenn variable1 == null, eingestellt variable2auf 100.


0

Andere haben das Null Coalescing Operatorganz gut beschrieben. Für Interessierte gibt es eine verkürzte Syntax, wo dies (die SO-Frage):

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

ist gleichbedeutend damit:

FormsAuth ??= new FormsAuthenticationWrapper();

Einige finden es lesbarer und prägnanter.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.