【问题标题】:"The operator can’t be unconditionally invoked because the receiver can be null" error after migrating to Dart null-safety迁移到 Dart 空安全后出现“无法无条件调用运算符,因为接收器可以为空”错误
【发布时间】:2022-01-25 08:00:33
【问题描述】:

我正在升级一个基于 Flutter 框架的个人包。我注意到 Flutter Text 小部件源代码中的here 有一个空检查:

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

但是,textSpan! 仍在使用! 运算符。不应该将textSpan 提升为不可为空的类型而不必使用! 运算符吗?但是,尝试删除操作符会出现以下错误:

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.

这是一个独立的例子:

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

我得到一个编译时错误:

错误:“字符串?”类型的值无法从函数“myString”返回,因为它的返回类型为“String”。

或者,如果我尝试获取 _mySting.length,则会收到以下错误:

无法无条件访问属性“length”,因为接收者可以为“null”。

我认为进行空检查会将_myString 提升为不可为空的类型。为什么不呢?

My question 已在 GitHub 上解决,所以我在下面发布答案。

【问题讨论】:

标签: flutter dart type-promotion dart-null-safety


【解决方案1】:

飞镖工程师 Erik Ernst says on GitHub:

类型提升仅适用于局部变量。 ... 实例变量的提升并不合理,因为它可能被运行计算并在每次调用时返回不同对象的 getter 覆盖。参照。 dart-lang/language#1188 用于讨论类似于类型提升但基于动态检查的机制,并提供一些相关讨论的链接。

所以本地类型提升有效:

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

但实例变量不会提升。为此,您需要使用 ! 运算符手动告诉 Dart 您确定在这种情况下实例变量不为空:

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

【讨论】:

  • myMethod可以改写成String myMethod() { return _myString ?? '' }吗?
  • @gegobyte,是的(如果您添加分号)。
【解决方案2】:

错误:

假设这是您的代码,您正在对实例变量进行空值检查,但仍然看到错误:

class Foo {
  int? i = 0;

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

不能无条件调用“toDouble”方法,因为接收者可以为“null”。

您在这样的代码中看到的错误是因为Getter 没有提升为它们的不可为空的对应物。说说原因吧。


错误原因:

假设有一个类 Bar 扩展 Foo 并覆盖 i 变量并且不为其分配任何值(保留它 null):

class Bar extends Foo {
  @override
  int? i;
}

如果可以的话

print(Bar().func() * 2);

您会遇到运行时 null 错误,这就是禁止 getter 类型提升的原因。


解决方案:

我们需要抛弃int? 的可空性。一般有3种方式(更多方式包括使用asis等)

  • 使用局部变量(推荐)

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

    double bar() {
      return i?.toDouble() ?? -1; // Provide some default value.
    }
    
  • 使用 Bang 运算符 (!)

    只有当您 100% 确定变量 (i) 永远不会是 null 时,才应该使用此解决方案。

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

【讨论】:

  • 局部变量解决方案看起来是最好的选择,尤其是当您需要使用 += 或 *= 运算符时
  • 使用局部变量的解决方案是您个人推荐的,还是飞镖团队对此有任何说法?
  • @HannesHultergård 它来自 Dart 团队的一位成员所写的帖子。
【解决方案3】:

风格:Theme.of(context).textTheme.headline5!.copyWith(

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

尝试使用 ?或空安全检查器 - !

【讨论】:

  • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
猜你喜欢
  • 2021-08-07
  • 2021-09-18
  • 1970-01-01
  • 2021-11-21
  • 1970-01-01
相关资源
最近更新 更多