Instanciation d'objets nuls avec un opérateur à coalescence nulle


12

Considérez le scénario typique suivant:

if(myObject == null) {
    myObject = new myClass();
}

Je me demande ce que l'on pense du remplacement suivant en utilisant l'opérateur de coalescence nulle:

myObject = myObject ?? new myClass();

Je ne sais pas si je devrais utiliser le deuxième formulaire. Cela semble être un joli raccourci, mais la myObject = myObjectconstruction au début semble pouvoir ressembler à une odeur de code.

Est-ce une chose raisonnable à faire ou y a-t-il un meilleur raccourci qui me manque? Ou peut-être, "c'est trois lignes, surmontez-le!"?

Edit: Comme cela a été mentionné, peut-être appeler cela un scénario typique est quelque chose d'une surestimation. Je trouve généralement que je rencontre cette situation lorsque je récupère une entité d'une base de données qui possède une propriété de type de référence enfant qui peut ou non encore être remplie:

myClass myObject = myClassService.getById(id);
myObject.myChildObject = myObject.myChildObject ?? new myChildClass();

15
Les débutants croient que des choses comme ça sont "hackish", les développeurs plus expérimentés l'appellent juste "idiomatique".
Doc Brown

Si l'objet ressemble à un objet de valeur ("a une sémantique de valeur", en langage idiomatique), MyClassdevrait fournir un public static readonlymembre d'une Emptyvaleur de son type. Voir String.Emptysur MSDN .
rwong


5
N'y a-t-il pas un ??=opérateur?
aviv

1
@aviv J'aimerais qu'il y en ait!
Loren Pechtel

Réponses:


16

J'utilise toujours l'opérateur de coalescence nulle. J'aime sa concision.

Je trouve que cet opérateur est de nature similaire à l'opérateur ternaire (A? B: C). Il faut un peu de pratique avant que la lecture soit une seconde nature, mais une fois que vous y êtes habitué, je pense que la lisibilité s'améliore par rapport aux versions longues.

De plus, la situation que vous décrivez n'est qu'un scénario dans lequel l'opérateur est utile. Il est également pratique de remplacer des constructions comme celle-ci:

if (value != null)
{
    return value;
}
else
{ 
    return otherValue;
}

ou

return value != null ? value : otherValue;

avec

return value ?? otherValue;

Certainement d'accord en général sur l'utilisation de cet opérateur. Quand je vois des références à son utilisation, cependant, c'est généralement quelque chose comme myObject = myObject2 ?? myObject3. Ce que je me demande en particulier, c'est d'utiliser l'opérateur pour définir un objet sur lui-même, sauf s'il est nul.
grin0048

2
Je pense que le même raisonnement s'applique dans les deux cas. C'est une manière plus concise d'exprimer la même logique.
17 du 26

Je me souviens avoir regardé l'IL pour chacune de ces trois formes une fois. Le premier et le second étaient identiques, mais le troisième a été optimisé d'une manière qu'il pourrait supposer nullêtre une comparaison.
Jesse C. Slicer

2

Ce que je me demande précisément, c'est d'utiliser l'opérateur pour définir un objet sur lui-même, sauf s'il est nul.

Le ?? operatorest appelé l'opérateur de coalescence nulle et est utilisé pour définir une valeur par défaut pour les types de valeur nullable ou les types de référence. Il renvoie l'opérande de gauche si l'opérande n'est pas nul; sinon, elle renvoie l'opérande de droite.

myObject = myObject ?? new myObject(); - instantiate default value of object

Plus de détails et exemple de code sur l'opérateur de coalescence nulle - article MSDN .

Si vous vérifiez l' more than nullableétat, comme solution de rechange , vous pouvez utiliser l' opérateur ternaires.

Opérateur ternaire

Vous pouvez également consulter ?: Opérateur . Il est appelé opérateur ternaire ou conditionnel . L'opérateur conditionnel (? :) renvoie l'une des deux valeurs en fonction de la valeur d'une expression booléenne.

Un type nullable peut contenir une valeur, ou il peut être indéfini. Le ?? L'opérateur définit la valeur par défaut à renvoyer lorsqu'un type nullable est affecté à un type non nullable. Si vous essayez d'affecter un type de valeur nullable à un type de valeur non nullable sans utiliser le ?? , vous générerez une erreur de compilation. Si vous utilisez un transtypage et que le type de valeur nullable n'est actuellement pas défini, une exception InvalidOperationException sera levée.

Un exemple de code de MSDN -?: Opérateur (référence C #) :

int? input = Convert.ToInt32(Console.ReadLine());
string classify;

// ?: conditional operator.
classify = (input.HasValue) ? ((input < 0) ? "negative" : "positive") : "undefined";

Donc, en prenant l'exemple de la question et en appliquant l'opérateur conditionnel, nous aurions quelque chose comme myObject = (myObject == null) ? new myClass() : myObject. Donc, à moins que je manque une meilleure utilisation de l'opérateur conditionnel, il semble avoir le même "problème" avec la définition d'un objet sur lui-même (s'il n'est pas nul) que l'opérateur de coalescence nulle - et c'est moins concis.
grin0048

Si vous recherchez plus d'une condition (non nulle et supérieure à zéro) - alors l'opérateur conditionnel le fera. Cependant, une vérification uniquement nulle fonctionnerait pour ?? opérateur.
Yusubov

2

Je ne pense pas que ce scénario soit (ou du moins devrait être) typique.

Si vous souhaitez que la valeur par défaut de certains champs ne soit pas null, définissez-la dans l'initialiseur de champs:

myClass myObject = new myClass();

Ou, si l'initialisation est plus compliquée, définissez-la dans le constructeur.

Si vous souhaitez créer myClassuniquement lorsque vous en avez réellement besoin (par exemple, car sa création prend beaucoup de temps), vous pouvez utiliser Lazy<T>:

Lazy<myClass> myObject = new Lazy<myClass>();

(Cela appelle le constructeur par défaut. Si l'initialisation est plus compliquée, passez lambda qui crée myClassau Lazy<T>constructeur.)

Pour accéder à la valeur, utilisez myObject.Value, qui appellera l'initialisation si c'est la première fois que vous accédez myObject.Value.


La création paresseuse IMO n'est utile que si vous n'êtes pas assuré d'avoir besoin de l'objet résultant, la seule fois où j'utilise la création paresseuse est lorsque j'ai un résultat dérivé encaissé que je réinitialise lorsque quelque chose change et je sais que j'aurai besoin du même résultat beaucoup et le recalcul est cher. autrement, je ne veux juste pas m'embêter avec le chèque nul
ratchet freak

@ratchetfreak Oui, c'est pourquoi j'ai suggéré l'initialiseur de champ en premier.
svick

Il existe également d'autres cas. Ce que je regarde en ce moment: La première passe définit les cas d'exception. La deuxième passe définit les valeurs par défaut pour tout le reste. ?? = réduirait la ligne de moitié.
Loren Pechtel

1

J'aime particulièrement utiliser ??en conjonction avec des paramètres de méthode facultatifs. Je limite le besoin de surcharges et m'assure que la chose a une valeur.

public void DoSomething (MyClass thing1 = null) {
    thing1 = thing1 ?? new MyClass();
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.