Que signifient ensemble deux points d'interrogation en C #?

1632

Parcourez cette ligne de code:

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

Que signifient les deux points d'interrogation, s'agit-il d'une sorte d'opérateur ternaire? Il est difficile de rechercher dans Google.

Edward Tanguay
la source
50
Ce n'est certainement pas un opérateur ternaire - il n'a que deux opérandes! C'est un peu comme l'opérateur conditionnel (qui est ternaire) mais l'opérateur coalescent nul est un opérateur binaire.
Jon Skeet le
90
Je l'ai expliqué dans une interview où l'employeur potentiel avait précédemment exprimé des doutes sur mes capacités C #, car j'avais utilisé Java professionnellement pendant un certain temps auparavant. Ils n'en avaient jamais entendu parler auparavant et n'ont pas remis en question ma familiarité avec C # après cela :)
Jon Skeet
77
@ Jon Skeet Il n'y a pas eu un tel échec épique à reconnaître les compétences depuis le gars qui a rejeté les Beatles. :-) À partir de maintenant, envoyez-leur une copie de votre livre avec un lien URL vers votre profil SO écrit sur la couverture intérieure.
Iain Holder
6
IainMH: Pour ce que ça vaut, je n'avais pas encore commencé à écrire le livre. (Ou peut-être que je travaillais juste sur le chapitre 1 - quelque chose comme ça.) Certes, une recherche pour moi aurait rapidement trouvé mon blog + articles, etc.
Jon Skeet
7
Re: dernière phrase du q - pour une future référence, SymbolHound est idéal pour ce genre de chose, par exemple symbolhound.com/?q=%3F%3F&l=&e=&n=&u= [à quiconque est suspect - je ne suis pas affilié à de toute façon, tout comme un bon outil quand j'en trouve un ...]
Steve Chambers

Réponses:

2156

C'est l'opérateur coalescent nul, et tout comme l'opérateur ternaire (immédiat-si). Voir aussi ?? Opérateur - MSDN .

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

s'étend à:

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

qui s'élargit encore à:

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

En anglais, cela signifie "Si tout ce qui est à gauche n'est pas nul, utilisez-le, sinon utilisez ce qui est à droite."

Notez que vous pouvez utiliser n'importe quel nombre de ceux-ci dans l'ordre. L'instruction suivante affectera le premier non nul Answer#à Answer(si toutes les réponses sont nulles, le Answerest nul):

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

Il convient également de mentionner que, si l'expansion ci-dessus est conceptuellement équivalente, le résultat de chaque expression n'est évalué qu'une seule fois. Ceci est important si, par exemple, une expression est un appel de méthode avec des effets secondaires. (Nous remercions @Joey de l'avoir signalé.)

lc.
la source
17
Potentiellement dangereux de les enchaîner peut
Coops
6
@CodeBlend comment cela?
lc.
68
@CodeBlend, ce n'est pas dangereux. Si vous deviez développer, vous auriez juste une série d'instructions if / else imbriquées. La syntaxe est juste étrange parce que vous n'êtes pas habitué à la voir.
joelmdev
31
Cela aurait été un
don de
14
@Gusdor ??est laissé associatif, a ?? b ?? c ?? dest donc équivalent à ((a ?? b) ?? c ) ?? d. "Les opérateurs d'affectation et l'opérateur ternaire (? :) sont associatifs à droite. Tous les autres opérateurs binaires sont associatifs à gauche." Source: msdn.microsoft.com/en-us/library/ms173145.aspx
Mark E. Haase
284

Tout simplement parce que personne d'autre n'a encore prononcé les mots magiques: c'est l' opérateur de coalescence nul . Il est défini dans la section 7.12 de la spécification du langage C # 3.0 .

C'est très pratique, en particulier en raison de la façon dont il fonctionne lorsqu'il est utilisé plusieurs fois dans une expression. Une expression de la forme:

a ?? b ?? c ?? d

donnera le résultat de l'expression asi elle n'est pas nulle, sinon essayez b, sinon essayez c, sinon essayez d. Il court-circuite en tout point.

De plus, si le type de dest non nullable, le type de l'expression entière est non nullable également.

Jon Skeet
la source
2
J'aime la façon dont vous avez simplifié sa signification en "opérateur de coalescence nul". C'est tout ce dont j'avais besoin.
FrankO
2
Tangentiel mais lié: C'est maintenant dans Swift, mais c'est très différent: "Nil Coalescing Operator" developer.apple.com/library/ios/documentation/Swift/Conceptual/…
Dan Rosenstark
69

C'est l'opérateur coalescent nul.

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

Oui, presque impossible à rechercher à moins de savoir comment on l'appelle! :-)

EDIT: Et ceci est une fonctionnalité intéressante d'une autre question. Vous pouvez les enchaîner.

Caractéristiques cachées de C #?

Iain Holder
la source
33
Au moins maintenant, il est facile de le rechercher, même si vous ne connaissez pas le nom, je viens de googler "double point d'interrogation c #"
stivlo
27

Merci à tous, voici l'explication la plus succincte que j'ai trouvée sur le site MSDN:

// y = x, unless x is null, in which case y = -1.
int y = x ?? -1;
Edward Tanguay
la source
4
Cela fait allusion à un aspect important de la ?? opérateur - il a été introduit pour aider à travailler avec les types nullables. Dans votre exemple, "x" est de type "int?" (Nullable <int>).
Drew Noakes
9
@vitule no, si le deuxième opérande de l'opérateur de coalescence nul n'est pas nullable, alors le résultat est non nullable (et -1est juste un simple int, qui n'est pas nullable).
Suzanne Dupéron
Je souhaite vraiment que cela fonctionne comme ça. Je veux le faire tout le temps, mais je ne peux pas parce que la variable que j'utilise n'est pas un type nullable. Fonctionne en coffeescript y = x? -1
PandaWood
@PandaWood En fait, vous le pouvez. Si xest de type int?mais yde type int, vous pouvez écrire int y = (int)(x ?? -1). Il analysera xà un intsi ce n'est pas null, ou céder -1à ysi xest null.
Johan Wintgens
21

??est là pour fournir une valeur pour un type nullable lorsque la valeur est null. Ainsi, si formsAuth est null, il renverra un nouveau FormsAuthenticationWrapper ().

RedFilter
la source
19

entrez la description de l'image ici

Les deux points d'interrogation (??) indiquent qu'il s'agit d'un opérateur de coalescence.

L'opérateur de coalescence renvoie la première valeur NON NULL d'une chaîne. Vous pouvez voir cette vidéo youtube qui montre le tout pratiquement.

Mais permettez-moi d'ajouter plus à ce que dit la vidéo.

Si vous voyez le sens anglais de coalescence, il dit «consolider ensemble». Par exemple ci-dessous est un simple code coalescent qui enchaîne quatre chaînes.

Donc, s'il l' str1est, nullil essaiera str2, s'il l' str2est, nullil essayera str3et ainsi de suite jusqu'à ce qu'il trouve une chaîne avec une valeur non nulle.

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

En termes simples, l'opérateur de coalescence renvoie la première valeur NON NULL d'une chaîne.

Shivprasad Koirala
la source
J'aime cette explication parce qu'elle a un diagramme, puis la dernière phrase l'explique en termes si simples! Il le rend facile à comprendre et renforce ma confiance en l'utilisation. Merci!
Joshua K
14

C'est une main courte pour l'opérateur ternaire.

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

Ou pour ceux qui ne font pas de ternaire:

if (formsAuth != null)
{
  FormsAuth = formsAuth;
}
else
{
  FormsAuth = new FormsAuthenticationWrapper();
}
Benjamin Autin
la source
3
J'ai seulement corrigé l'orthographe de "ternaire" mais vraiment l'opérateur que vous voulez dire est l'opérateur conditionnel. Il se trouve que c'est le seul opérateur ternaire en C #, mais à un moment donné ils pourraient en ajouter un autre, auquel cas "ternaire" sera ambigu mais pas "conditionnel".
Jon Skeet,
C'est un raccourci pour quelque chose que vous pouvez faire avec l'opérateur ternaire (conditionnel). Dans votre forme longue, le test ( != null) et le second formsAuth(après le ?) peuvent être modifiés; sous la forme null coalesce, les deux prennent implicitement les valeurs que vous avez fournies.
Bob Sammers
12

Si vous êtes familier avec Ruby, cela me ||=ressemble aux C # ??. Voici quelques rubis:

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"

Et en C #:

string str1 = null;
str1 = str1 ?? "new value";
string str2 = "old value";
str2 = str2 ?? "another new value";
Sarah Vessels
la source
4
x ||= ydesugars à quelque chose comme x = x || y, donc ??est en fait plus similaire à plain ||en Ruby.
qerub
6
Notez que ??seuls soucis au sujet null, alors que l' ||opérateur Ruby, comme dans la plupart des langues, est plus sur null, falseou tout ce qui peut être considéré comme une valeur booléenne avec une valeur de false(par exemple , dans certaines langues, ""). Ce n'est pas une bonne ou une mauvaise chose, simplement une différence.
Tim S.
11

opérateur coalescent

c'est équivalent à

FormsAuth = formsAUth == null ? new FormsAuthenticationWrapper() : formsAuth
aku
la source
11

Rien de dangereux à ce sujet. En fait, c'est beau. Vous pouvez ajouter une valeur par défaut si cela est souhaitable, par exemple:

CODE

int x = x1 ?? x2 ?? x3 ?? x4 ?? 0;
dqninh
la source
1
Donc x1, x2, x3 et x4 pourraient être des types Nullable, exemple: int? x1 = null;est-ce vrai
Kevin Meredith
1
@KevinMeredith x1- x4DOIT être de type nullable: cela n'a aucun sens de dire, effectivement, "le résultat est 0if x4est une valeur qu'il ne peut pas prendre" ( null). Le "type nullable" inclut ici les types de valeur nullable et les types de référence, bien sûr. Il s'agit d'une erreur au moment de la compilation si une ou plusieurs des variables chaînées (à l'exception de la dernière) ne peuvent pas être annulées.
Bob Sammers
11

Comme indiqué à juste titre dans de nombreuses réponses, il s'agit de l '"opérateur de coalescence nul" ( ?? ), à propos duquel vous voudrez peut-être également consulter son cousin "l'opérateur Null-conditionnel" ( ?. Ou ? [ ) Qui est un opérateur qui plusieurs fois, il est utilisé en conjonction avec ??

Opérateur null-conditionnel

Utilisé pour tester la valeur null avant d'effectuer une opération d'accès aux membres ( ?. ) Ou d'index ( ? [ ). Ces opérateurs vous aident à écrire moins de code pour gérer les vérifications nulles, en particulier pour descendre dans les structures de données.

Par exemple:

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

à l'ancienne sans ?. et ?? de le faire est

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

qui est plus verbeux et encombrant.

brakeroo
la source
10

Pour votre amusement uniquement (sachant que vous êtes tous des gars C # ;-).

Je pense qu'il est originaire de Smalltalk, où il existe depuis de nombreuses années. Il y est défini comme:

dans Object:

? anArgument
    ^ self

dans UndefinedObject (aka la classe de nil):

? anArgument
    ^ anArgument

Il existe à la fois des versions d'évaluation (?) Et non d'évaluation (??).
On le trouve souvent dans les méthodes getter pour les variables privées (instance) initialisées paresseux, qui restent nuls jusqu'à ce qu'elles soient vraiment nécessaires.

blabla999
la source
sonne comme envelopper ViewState avec une propriété sur un UserControl. Initialiser uniquement sur le premier get, si ce n'est pas déjà fait. =)
Seiti
9

Certains des exemples d'obtention de valeurs à l'aide de la coalescence sont inefficaces.

Ce que vous voulez vraiment, c'est:

return _formsAuthWrapper = _formsAuthWrapper ?? new FormsAuthenticationWrapper();

ou

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

Cela empêche l'objet d'être recréé à chaque fois. Au lieu que la variable privée reste nulle et qu'un nouvel objet soit créé à chaque demande, cela garantit que la variable privée est affectée si le nouvel objet est créé.

KingOfHypocrites
la source
Le ??raccourci n'est-il pas évalué? new FormsAuthenticationWrapper();est évalué si et seulement si _formsAuthWrapper est nul.
MSalters
Oui, c'est tout. Vous ne souhaitez appeler la méthode que si la variable est nulle. @MSalters
KingOfHypocrites
8

Remarque:

J'ai lu tout ce fil et bien d'autres, mais je ne trouve pas de réponse aussi complète que celle-ci.

Par lequel j'ai complètement compris le "pourquoi utiliser ?? et quand utiliser ?? et comment utiliser ??."

La source:

Lancement de la fondation de communication Windows Par Craig McMurtry ISBN 0-672-32948-4

Types de valeur Nullable

Il existe deux circonstances courantes dans lesquelles on souhaite savoir si une valeur a été affectée à une instance d'un type de valeur. Le premier est lorsque l'instance représente une valeur dans une base de données. Dans un tel cas, on aimerait pouvoir examiner l'instance pour vérifier si une valeur est bien présente dans la base de données. L'autre circonstance, qui est plus pertinente pour le sujet de ce livre, est lorsque l'instance représente un élément de données reçu d'une source distante. Encore une fois, on voudrait déterminer à partir de l'instance si une valeur pour cet élément de données a été reçue.

Le .NET Framework 2.0 incorpore une définition de type générique qui prévoit des cas comme ceux-ci dans lesquels on veut attribuer null à une instance d'un type de valeur et tester si la valeur de l'instance est nulle. Cette définition de type générique est System.Nullable, qui contraint les arguments de type générique qui peuvent être substitués pour T aux types de valeur. Les instances de types construites à partir de System.Nullable peuvent se voir attribuer une valeur nulle; en effet, leurs valeurs sont nulles par défaut. Ainsi, les types construits à partir de System.Nullable peuvent être appelés types de valeurs nullables. System.Nullable a une propriété, Value, par laquelle la valeur affectée à une instance d'un type construit à partir de celle-ci peut être obtenue si la valeur de l'instance n'est pas nulle. Par conséquent, on peut écrire:

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

Le langage de programmation C # fournit une syntaxe abrégée pour déclarer des types construits à partir de System.Nullable. Cette syntaxe permet d'abréger:

System.Nullable<int> myNullableInteger;

à

int? myNullableInteger;

Le compilateur empêchera de tenter d'affecter la valeur d'un type de valeur nullable à un type de valeur ordinaire de cette façon:

int? myNullableInteger = null;
int myInteger = myNullableInteger;

Cela l'empêche de le faire car le type de valeur nullable pourrait avoir la valeur null, ce qu'il aurait en fait dans ce cas, et cette valeur ne peut pas être affectée à un type de valeur ordinaire. Bien que le compilateur autorise ce code,

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

La deuxième instruction entraînerait la levée d'une exception car toute tentative d'accès à la propriété System.Nullable.Value est une opération non valide si le type construit à partir de System.Nullable n'a pas reçu de valeur valide de T, ce qui ne s'est pas produit dans ce Cas.

Conclusion:

Une façon appropriée d'affecter la valeur d'un type de valeur nullable à un type de valeur ordinaire consiste à utiliser la propriété System.Nullable.HasValue pour vérifier si une valeur valide de T a été affectée au type de valeur nullable:

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

Une autre option consiste à utiliser cette syntaxe:

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

Par lequel l'entier ordinaire myInteger se voit attribuer la valeur de l'entier nullable "myNullableInteger" si ce dernier a reçu une valeur entière valide; sinon, myInteger reçoit la valeur -1.

Muhammad Waqas Dilawar
la source
1

C'est un opérateur coalescent nul qui fonctionne de manière similaire à un opérateur ternaire.

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

Un autre point intéressant pour cela est, "Un type nullable peut contenir une valeur, ou il peut être indéfini" . Donc, si vous essayez d'affecter un type de valeur nullable à un type de valeur non nullable, vous obtiendrez une erreur de compilation.

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

Donc, pour ce faire en utilisant ?? opérateur:

z = x ?? 1; // with ?? operator there are no issues
Ashish Mishra
la source
1
FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

est équivalent à

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

Mais ce qu'il y a de bien, c'est que vous pouvez les enchaîner, comme d'autres l'ont dit. La seule chose qui n'est pas abordée est que vous pouvez réellement l'utiliser pour lever une exception.

A = A ?? B ?? throw new Exception("A and B are both NULL");
NolePTR
la source
C'est vraiment génial que vous ayez inclus des exemples dans votre message, même si la question cherchait une explication de ce qu'est ou fait l'opérateur.
TGP1994
1

L' ??opérateur est appelé l'opérateur de coalescence nulle. Il renvoie l'opérande de gauche si l'opérande n'est pas nul; sinon, elle renvoie l'opérande de droite.

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

Définissez variable2la valeur de variable1, si variable1n'est PAS nul; sinon, si variable1 == null, réglé variable2sur 100.

Jubayer Hossain
la source
0

D'autres l'ont décrit Null Coalescing Operatorassez bien. Pour ceux qui sont intéressés, il existe une syntaxe raccourcie où cela (la question SO):

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

est équivalent à ceci:

FormsAuth ??= new FormsAuthenticationWrapper();

Certains le trouvent plus lisible et succinct.

Thane Plummer
la source