跳转到正文
YGJoon 技术博客

Dart 运算符和表达式详解

发布于:
·1.4k 字 · 约 4 分钟·作者:
三元、??=、as/is 类型测试都是常规操作,重点讲 Dart 独有的级联运算符与空安全 ?.、??,写出更短的链式代码。
Dart 运算符和表达式详解文章封面
目录

算术运算符

Dart 提供了常见的算术运算符:

int a = 10;
int b = 3;

print(a + b);  // 加法:13
print(a - b);  // 减法:7
print(a * b);  // 乘法:30
print(a / b);  // 除法:3.3333333333333335
print(a ~/ b); // 整除:3
print(a % b);  // 取余:1

关系运算符

用于比较两个值之间的关系:

int x = 5;
int y = 8;

print(x == y); // 等于:false
print(x != y); // 不等于:true
print(x > y);  // 大于:false
print(x < y);  // 小于:true
print(x >= y); // 大于等于:false
print(x <= y); // 小于等于:true

逻辑运算符

用于组合或修改逻辑条件:

bool a = true;
bool b = false;

print(a && b); // 与:false
print(a || b); // 或:true
print(!a);     // 非:false

赋值运算符

Dart 提供了多种赋值运算符:

int a = 10;    // 基本赋值
a += 5;        // 等同于 a = a + 5
a -= 3;        // 等同于 a = a - 3
a *= 2;        // 等同于 a = a * 2
a ~/= 4;       // 等同于 a = a ~/ 4

条件运算符

三元运算符

int age = 20;
String status = age >= 18 ? "成年" : "未成年";

??= 运算符

如果变量为 null,则赋值:

String? name;
name ??= "默认名称"; // 如果 name 为 null,则赋值

类型测试运算符

var str = "Hello";
print(str is String);     // true
print(str is! int);      // true

级联运算符

允许对同一个对象进行一系列操作:

class Person {
  String? name;
  int? age;
  
  void printInfo() {
    print("$name, $age");
  }
}

void main() {
  Person()
    ..name = "张三"
    ..age = 25
    ..printInfo();
}

空安全运算符

?. 运算符

安全地访问可能为 null 的对象的属性:

String? name;
print(name?.length); // 如果 name 为 null,返回 null

?? 运算符

提供默认值:

String? name;
print(name ?? "默认名称"); // 如果 name 为 null,返回默认值

位运算符

int a = 5;  // 二进制:0101
int b = 3;  // 二进制:0011

print(a & b);  // 与:1 (0001)
print(a | b);  // 或:7 (0111)
print(a ^ b);  // 异或:6 (0110)
print(~a);     // 取反:-6
print(a << 1); // 左移:10 (1010)
print(a >> 1); // 右移:2 (0010)

表达式

算术表达式

double result = (10 + 5) * 2 / 3; // 注意:/ 永远返回 double,这里是 10.0

字符串表达式

String name = "张三";
int age = 25;
String info = "我叫$name,今年${age + 1}岁";

函数表达式

var sum = (int a, int b) => a + b;
print(sum(5, 3)); // 8

运算符优先级

Dart 运算符优先级从高到低:

  1. 一元后缀运算符 (expr++, expr—)
  2. 一元前缀运算符 (-expr, !expr, ++expr, —expr)
  3. 乘除运算符 (*, /, %, ~/)
  4. 加减运算符 (+, -)
  5. 移位运算符 (<<, >>)
  6. 按位与 (&)
  7. 按位异或 (^)
  8. 按位或 (|)
  9. 关系运算符 (>=, >, <=, <)
  10. 类型测试运算符 (as, is, is!)
  11. 相等运算符 (==, !=)
  12. 逻辑与 (&&)
  13. 逻辑或 (||)
  14. 空值合并运算符 (??)
  15. 条件运算符 (?:)
  16. 级联运算符 (..)
  17. 赋值运算符 (=, +=, 等)

最佳实践

  1. 优先使用括号明确运算优先级,提高代码可读性
  2. 合理使用空安全运算符,避免空指针异常
  3. 使用级联运算符简化连续操作
  4. 注意运算符的副作用,特别是在使用自增自减运算符时

实战案例:我的代码里出镜率最高的 5 个运算符

翻 JsonConverter 和 SerialService 的源码,Dart 运算符里真正高频的就这几个——而且每个都对应一个具体决策:

  1. ??(判空默认值):_controller?.stream ?? const Stream.empty()。串口没连接时返回空流而不是 null,调用方永远不用判空。?? 的本质是”给 null 一个体面的默认值”,我代码里 ?? 出现的地方,几乎都是”这里可能没值,但调用方不该操心”。

  2. ?.(空安全调用):Dart 里 ?. 后面即使跟赋值,对象为 null 时整个表达式直接跳过、不抛异常。这比 if (x != null) 省三行,意图也更清楚。(我的串口代码里用的是 ! 断言——_port!.config = config,因为前面已经 openReadWrite() 判过空了;! 和 ?. 的选择标准是:前面判过空就用 !,没判过就用 ?.,别混。)

  3. is(类型判断+提升):jsonData is Map<String, dynamic>。Dart 的 is 不只是判断,还自动类型提升——判断完直接当那个类型用,不用 as 强转。这是我从 TS 转过来最喜欢的特性之一。

  4. 字符串插值 $:buffer.writeln(' $type? $key;')。生成器里几十处插值。注意 ${key.capitalize()} 这种带表达式的要加大括号——裸 $key.capitalize() 会被解析成 $key 后面跟 .capitalize(),这是新手必踩的坑。

  5. as(强转):我的代码里几乎不用。as 是”我比编译器更懂”,用一次就是一次信任透支。is 判断 + 提升能覆盖 95% 的场景,剩下的 5% 往往意味着类型设计有问题——该回去改类型,而不是加 as。

总结

运算符这东西,文档里能列十几个,实际天天写的就 ??、?.、is 那几个——上面实战案例里数的 5 个,就是从我自己代码里扒出来的出镜率排名。先把这几个用熟,剩下的等真遇到了再查文档,完全来得及。


分享这篇文章:


相关文章

上一篇
Dart 控制流语句详解
下一篇
Dart 并发编程进阶