Dart 级联运算符

级联语法包含两种:..?..

对同一个对象连续执行多次属性赋值、方法调用,省去临时变量,代码更连贯流畅。

级联语法不是运算符,属于 Dart 专属语法糖。

1. 普通级联 ..

确定对象一定不为 null

所有 .. 操作都作用于最开头的原始对象,忽略每一步方法返回值。

官方例子

import 'dart:ui';

void main() {
  // 级联写法
  var paint = Paint()
    ..color = Colors.black
    ..strokeCap = StrokeCap.round
    ..strokeWidth = 5.0;

  // 等价拆分写法(无级联,需重复变量名)
  var paint2 = Paint();
  paint2.color = Colors.black;
  paint2.strokeCap = StrokeCap.round;
  paint2.strokeWidth = 5.0;
}Code language: JavaScript (javascript)

这个例子引入了一个dart:ui 的库,它属于flutter的库,只能在flutter中使用。由于我们这里是dart的。所以读者理解上面语法就可以。下面这个例子,我们使用另外一个纯dart的内置类来掩饰:StringBuffer

这是一个字符串操作类:StringBuffer

void main() {
  // 级联语法
  var sb = StringBuffer()
    ..write("Hello ")
    ..write("FoxDevelop.com ")
    ..writeln("Cascade");

  print(sb.toString());

  // 等价下面,不使用级联
  var sb2 = StringBuffer();
  sb2.write("Hello ");
  sb2.write("FoxDevelop.com ");
  sb2.writeln("Cascade");
  print(sb2.toString());
}Code language: PHP (php)

执行结果

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Hello FoxDevelop.com Cascade

Hello FoxDevelop.com CascadeCode language: CSS (css)

以上我,读者确认我们的对象paint sb sb2 这些对象,绝对不为null的情况下使用,可以快速赋值,不需要重复使用对象名调用对象属性。但是如果对象是空的情况下:会直接报错

void main() {

  var sb = null
    ..write("Hello ")
    ..write("FoxDevelop.com ")
    ..writeln("Cascade");

  print(sb.toString());
}Code language: JavaScript (javascript)

所以,就出现了下面这种第二种用法

2. 空安全级联 ?..

只要对象是 null,整条级联链全部不执行,避免空指针报错;仅第一个级联用 ?..,后面直接接 ..

void main() {

  var sb = null
    ?..write("Hello ")
    ..write("FoxDevelop.com ")
    ..writeln("Cascade");

  print(sb.toString());
}Code language: Dart (dart)

认真看,上面的 ?..write(“Hello “) 前面有个问号

再次编译,正常

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
nullCode language: CSS (css)

不会报错

如下是官方例子:读者了解语法写法就可以

// ?.. 开头,后续统一用 ..
document.querySelector('#confirm')
  ?..textContent = 'Confirm'
  ..classList.add('important')
  ..onClick.listen((e) => window.alert('Confirmed!'))
  ..scrollIntoView();

// 等价空安全拆分写法
final button = document.querySelector('#confirm');
button?.textContent = 'Confirm';
button?.classList.add('important');
button?.onClick.listen((e) => window.alert('Confirmed!'));
button?.scrollIntoView();Code language: JavaScript (javascript)

嵌套级联

final addressBook = (AddressBookBuilder()
      ..name = 'jenny'
      ..email = 'jenny@example.com'
      // 内层 PhoneNumberBuilder 单独级联
      ..phone = (PhoneNumberBuilder()
            ..number = '415-555-0100'
            ..label = 'home')
          .build())
    .build();Code language: PHP (php)

phone 是一个调用PhoneNumberBuilder() 这个对象 并且赋值的对象。

注意

级联必须作用于对象实例,不能作用于 void 返回值,否则编译报错。

var sb = StringBuffer();
sb.write('foo') // write 返回 void
  ..write('bar'); // 报错:void 没有 write 方法Code language: Dart (dart)

这里前面创建了一个StringBuffer对象,sb

但是它是直接用分号结束了; 然后再用sb 对象去调用函数write 但是这个write返回是void的,这是 下面就不能再用..write了 除非这个write返回是是对象实例。

正确的写法

var sb = StringBuffer()
  ..write('foo')
  ..write('bar');Code language: Dart (dart)

总结

  1. .. 每次操作都回归最顶层原始对象,不接收方法返回值。所以它可以连续调用.. 相当于,我们其他编程语言的“链式编程”,例如js的
  2. ?.. 空安全级联,防止对象为 null 时报错,仅链首使用,因为只要链首能执行,那就证明这个对象绝对不为null了,所以后面就不需要再用?..
  3. 支持嵌套级联
  4. 不能在返回 void 的方法后接级联
  5. .. 不属于运算符,是 Dart 语法糖
所谓语法糖

语法糖是编程语言在语法解析阶段,对同一份底层逻辑提供的轻量化简写语法;它不会新增任何语言能力,编译器 / 解释器会在生成中间代码前,自动把简写语法还原为等价的基础标准代码。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注