33
votes

"L'opérateur ne peut pas être invoqué inconditionnellement car le récepteur peut être nul" Erreur après avoir migré vers la sécurité nul de Dart

Je mets à niveau un package personnel basé sur le framework Flutter. J'ai remarqué ici Dans le code source du widget de texte Flutter selon lequel il y a une vérification nul:

class MyClass {
  String? _myString;
  
  String get myString {
    if (_myString == null) {
      return '';
    }
    
    return _myString; //   <-- error here
  }
}

Cependant, textSpan! utilise toujours l'opérateur ! . Ne devrait-il pas être promu textSpan en un type non nullable sans avoir à utiliser l'opérateur ! ? Cependant, essayer de supprimer l'opérateur donne l'erreur suivante:

An expression whose value can be 'null' must be null-checked before it can be dereferenced.
Try checking that the value isn't 'null' before dereferencing it.

Voici un exemple autonome:

if (textSpan != null) {
  properties.add(textSpan!.toDiagnosticsNode(name: 'textSpan', style: DiagnosticsTreeStyle.transition));
}

J'obtiens une erreur de compilation:

Erreur: une valeur de type 'String?' ne peut pas être renvoyé de la fonction «mystring» car il a un type de retour de «chaîne».

ou si j'essaie d'obtenir _mysting.length j'obtiens l'erreur suivante:

La «longueur» de la propriété ne peut pas être accessible sans condition car le récepteur peut être «nul».

Je pensais que faire la vérification null favoriserait _mystring à un type non nullable. Pourquoi n'est-ce pas?

ma question a été résolue sur github donc je poste une réponse ci-dessous.


3 commentaires

Il s'agit essentiellement d'une autre version de stackoverflow.com/q/56764592 (Je suis sûr qu'il y a aussi d'autres questions existantes).


@jamesdlin, c'est intéressant. Je ne savais pas que c'était plus généralisé que la sécurité nul.


Je l'ai mentionné dans la question liée , mais cela vaut probablement la peine de pointer ici aussi: cela est couvert par dart.dev/tools/non-pomotion-reasons


3 Réponses :


36
votes

L'ingénieur de dart Erik Ernst indique sur github :

La promotion de type ne s'applique qu'aux variables locales. ... La promotion d'une variable d'instance n'est pas solide, car elle pourrait être remplacée par un getteur qui exécute un calcul et renvoie un objet différent chaque fois qu'il est invoqué. Cf. dart-lang / langage # 1188 Pour les discussions sur un mécanisme similaire à type promotion mais basée sur des vérifications dynamiques, avec quelques liens vers des discussions connexes.

La promotion de type local fonctionne donc:

class MyClass {
  String? _myString;
  
  String myMethod() {
    if (_myString == null) {
      return '';
    }
    
    return _myString!;
  }
}

mais les variables d'instance ne font pas la promotion. Pour cela, vous devez dire manuellement à Dart que vous êtes sûr que la variable d'instance n'est pas nul dans ce cas en utilisant l'opérateur ! :

  String myMethod(String? myString) {
    if (myString == null) {
      return '';
    }
    
    return myString;
  }
p>


2 commentaires

Peut être réécrit myMethod comme string myMethod () {return _mystring ?? ''} ?


@GegoByte, oui (si vous ajoutez un point-virgule).



20
votes

L'erreur:

Disons, c'est votre code et vous faites une vérification nul sur la variable d'instance et en voyant toujours une erreur:

double bar() {
  return i!.toDouble(); // <-- Bang operator in play.
}

La méthode «Todouble» ne peut pas être invoquée inconditionnellement car le récepteur peut être «nul».

L'erreur que vous voyez dans le code comme celle-ci est parce que les getters ne sont pas promus avec leurs homologues non nuls . Parlons de la raison.


Raison de l'erreur:

Disons, il y a une classe bar qui étend foo et remplacer i variable et ne lui attribue aucune valeur (en gardant null ):

double bar() {
  return i?.toDouble() ?? -1; // Provide some default value.
}

Donc, si vous pouviez faire

double bar() {
  var i = this.i; // <-- Use of local variable.
  if (i != null) return i.toDouble();
  return -1;
}

, vous auriez rencontré une erreur nulle d'exécution, c'est pourquoi la promotion de type Getters est interdite.


Solutions:

Nous devons rejeter la nullabilité de int? . Il existe généralement 3 façons de le faire (plus de façons d'inclure l'utilisation de comme , est , etc.)

  • Utilisez la variable locale (recommandée)

    print(Bar().func() * 2);
    
  • utilisation ?. avec ??

    class Bar extends Foo {
      @override
      int? i;
    }
    
  • Utilisez l'opérateur de bang (!)

    Vous ne devez utiliser cette solution que lorsque vous êtes sûr à 100% que la variable ( i ) ne sera jamais null .

    class Foo {
      int? i = 0;
    
      double func() {
        if (i != null) return i.toDouble(); // <-- Error
        return -1;
      }
    }
    


6 commentaires

La solution de variable locale ressemble à la meilleure option, surtout lorsque vous devez utiliser + = ou * = opérateurs


Détails sur la documentation: dart.dev/guides/language/language-tour#other- Opérateurs


La solution pour utiliser une variable locale est-elle votre recommandation personnelle, ou l'équipe DART a-t-elle dit quelque chose à ce sujet?


@ Hanneshultergård Il provient d'un article écrit par l'un des membres de l'équipe DART.


Veuillez me corriger si je me trompe, mais à moins que je ne manque à quelque chose de fondamental ici, je pense que cet exemple n'illustre pas vraiment le raisonnement derrière la prévention de la promotion Getter, puisque i évidemment ne change jamais après le chèque nul. Ne serait-il pas un meilleur exemple pour i dans bar être int? obtenir i => random (). nextbool ()? 0: null; comme celui montré ici ? De cette façon, vous pouvez vraiment voir comment le compilateur ne pourrait jamais garantir que i n'est pas nul, car sa valeur peut passer à NULL lorsqu'elle est accessible après la vérification nul.


@Omarsharaki Oui, j'aurais dû changer l'exemple pour utiliser un bool au lieu de int qui ferait quelque chose comme ce que vous avez mentionné. Je pense que j'ai publié quelque part un code similaire.



0
votes

Style: thème.of (Context) .TextTheme.headline5! .CopyWith (

style: Theme.of(context).textTheme.headline5!.copyWith(
                        color: Colors.white

Essayez de rendre l'appel conditionnel en utilisant? Ou un vérificateur de sécurité nul -!

p>


1 commentaires

Votre réponse pourrait être améliorée avec des informations de support supplémentaires. Veuillez modifier pour ajouter plus de détails, tels que des citations ou de la documentation, afin que d'autres puissent confirmer que votre réponse est correcte. Vous pouvez trouver plus d'informations sur la façon d'écrire de bonnes réponses dans le centre d'aide .