如何修复 "The operator ‘{0}’ can’t be unconditionally invoked because the receiver can be ‘null’"

How to fix "The operator ‘{0}’ can’t be unconditionally invoked because the receiver can be ‘null’"

在尝试 DartSound Null Safety 时,我遇到了一个问题:


一些上下文

创建一个新的 Flutter 项目我发现了以下(非常熟悉的)片段代码

int _counter = 0;

void _incrementCounter() {
  setState(() {
    // This call to setState tells the Flutter framework that something has
    // changed in this State, which causes it to rerun the build method below
    // so that the display can reflect the updated values. If we changed
    // _counter without calling setState(), then the build method would not be
    // called again, and so nothing would appear to happen.
    _counter++;
  });
}

现在,我将变量 _counter 更改为 nullable 并取消了初始化:

int? _counter;

void _incrementCounter() {
  setState(() {
    _counter++;
  });
}

正如预期的那样,我在编辑器中收到以下错误:

The operator ‘+’ can’t be unconditionally invoked because the receiver can be 'null'

问题

the documentation 之后,我添加了所需的检查:

if (_counter!=null)
        _counter++;

但令我惊讶的是,错误一直在显示和提示

Try making the call conditional (using '?' or adding a null check to the target ('!'))

即使我明确地有条件地进行调用...所以有什么问题吗?

更新

正如 , in a further thread in Github 中的建议,Erik Ernst 说:

Type promotion is only applicable to local variables... Promotion of an instance variable is not sound, because it could be overridden by a getter that runs a computation and returns a different object each time it is invoked. Cf. dart-lang/language#1188 for discussions about a mechanism which is similar to type promotion but based on dynamic checks, with some links to related discussions.

所以,有了这个解释,现在我看到只有局部变量可以(到目前为止?)是 promoted,因此我的问题可以通过写

来解决
int? _counter;

void _incrementCounter() {
  setState(() {
    if (_counter!=null)
        _counter = _counter! + 1;
  });
}

对于替代修复,请参阅下面我的原始答案。


其他修正

我终于通过捕获方法内部实例变量的值解决了这个问题,如下所示:

int? _counter;

void _incrementCounter() {
  setState(() {
    var c = _counter;

    if (c!=null)
      c++;

    _counter = c;
      
  });
}

为什么需要捕获变量?

嗯,整个问题是

Type promotion is only applicable to local variables... Promotion of an instance variable is not sound, because it could be overridden by a getter that runs a computation and returns a different object each time it is invoked

所以在我的方法中我们

  • 捕获实例变量的值
  • 然后我们检查 that value 是否为空。
  • 如果结果证明该值不为空,那么我们将对通过空检查正确提升的局部变量进行操作。
  • 最后我们将新值应用到实例变量。

最终观察

我是 Dart 的新手,因此我不是 100% 确定,但对我来说似乎 一般 ,在工作时对于可为空的实例变量,我的方法比使用 bang 运算符 cast away nullity:

更好

通过放弃无效性,您忽略了提升实例变量的主要问题,即

it could be overridden by a getter that runs a computation and returns a different object each time it is invoked ...

通过 捕获 实例变量的值并使用 that 本地捕获的值可以避免这个问题...

如果我错了请告诉我...