Dart:如何正确处理空值安全?

Dart: How to properly handle null-safety?

我已经将我的项目升级到 null-safety,这有点令人困惑,因为如果我包装成

if(someObject.field != null) { 
    doSomething(someObject.field);  // error, can't assign String? to String
}

需要不可为空的方法调用 属性 并且我尝试传递的变量可以为空,然后我收到类型错误,无法将可为空的值分配给不可为空的类型。

但是当我这样做的时候

String? someObjectField = someObject!.field;
if(someObjectField != null) { 
    doSometing(someObjectField); // Can assign
}

它按预期工作。

doSomething = (String foo) {}

例如:

class Person {
  final String name;

  Person(this.name);
  
}

Function test = () {
  Map<String, String?> pers = {
    'name': 'John',
  };

  if(pers['name'] != null) {
    Person(pers['name']); // Error, even if it will be never executed
    Person(pers['name'] as String); // no error
    Person(pers['name']!); // no error
  }
  
};

如果我这样做:

if (widget.event != null && widget.event.featuredImage != null)

然后它在第二个语句中抱怨接收器 (widget.event) 可以为 null,我需要使用 !,但是第二个语句不应该执行并且它不应该导致运行时异常。

所以需要修改为:

if (widget.event != null && widget.event!.featuredImage != null)

但是当我尝试在 Flutter 中使用嵌套小部件时,即使我使用 if 作为包装器,我仍然需要在所有地方添加 !

Stack(
  children: [
    // Add Container with image only when event and futured image are not null
    if (widget.event != null && widget.event!.featuredImage != null) ...[
      Container(
        height: 250,
        decoration: BoxDecoration(
          color: Colors.transparent,
          image: DecorationImage(
            fit: BoxFit.cover,
            image: NetworkImage(widget.event!.featuredImage!), // here i Need to use ! to satisfy the compiler
          ),
        ),
      ),
    ],
  ],
)

或者我可以将变量提取到另一个 String? image = widget.event!.featuredImage,然后将 if 语句修改为 if(image != null) 并使用像 NetworkImage(image) 这样的小部件,它按预期工作。

与检测我是否在上述条件下检查 null 的 TypeScript 相比,这对我来说毫无意义。

长话短说,即使我检查 render/not-render 组件的空值,我仍然需要使用 !.

有什么明显的我遗漏的东西吗?

提前致谢

既然你将它与 TypeScript 进行了比较,是的,你错过了一些东西。

Typescript 是一团糟,只能在非常有限的环境中工作,“工作”被严重夸大了。例如,您可以在打字稿中编写一个方法,该方法接受一个字符串,然后在运行时发现它实际上不是一个字符串,令人惊讶的是,它是一个完全不同的类型。 JavaScript 的欢乐。与 JS 相比,称 TypeScript 为“类型安全”是正确的,与实际编译语言相比是荒谬的。

所以您忽略了一个事实,即 Dart 编译器可以保证 一旦您检查某项内容就不会为空。为此,它需要额外的约束。例如,您可以让 getter 每次调用它们时都不会 return 相同的值。例如,根据 getter 的代码,您的调用很容易在第一次和第二次调用之间 return 不同 值。或者您可以使用继承和多态性来构建一些问题更多的结构。有关有趣的示例,请参见 。所以你需要有一个局部变量,保证具有相同的值,除非明确更改。

您的 if (widget.event != null && widget.event.featuredImage != null) 示例可以轻松修改为:

final image = widget?.event?.featuredImage;

Stack(
  children: [
    // Add Container with image only when event and futured image are not null
    if (image != null) ...[
      Container(
        height: 250,
        decoration: BoxDecoration(
          color: Colors.transparent,
          image: DecorationImage(
            fit: BoxFit.cover,
            image: NetworkImage(image), 
          ),
        ),
      ),
    ],
  ],
)

是的,您必须实现一些微小的逻辑,您不能只在代码上添加 ?! 就可以像以前一样运行。但是一旦你理解了逻辑上的小变化是什么,它就很容易了。