运算符

优质
小牛编辑
139浏览
2023-12-01

Dart 定义了下面表格中的这些运算符。你可以重写其中大部分的运算符,在 重载运算符 部分有更详细的描述。

描述运算符
一元后缀expr++ expr-- () [] . ?.
一元前缀-expr !expr ~expr ++expr --expr
乘除* / % ~/
加减+ -
移位<< >>
按位与&
按位异或^
按位或|
关系和类型检查>= > <= < as is is!
相等性== !=
逻辑与&&
逻辑或||
如果为空??
条件expr1 ? expr2 : expr3
级联..
赋值= *= /= ~/= %= += -= <<= >>= &= ^= |= ??=

使用运算符创建的是表达式。下面是一些表达式的例子:

a++
a + b
a = b
a == b
c ? a : b
a is T

在 // 括号可提高可读性 if ((n % i == 0) && (d % i == 0)) ... // 可读性差,但是效果等价 if (n % i == 0 && d % i == 0) ...

警告:对于作用于两个操作数的操作符,最左边的操作数决定了哪个版本的运算符会被使用。比如,如果你有一个 Vector 对象和 Point 对象,aVector + aPoint 使用 Vector 版本的 +。

算术运算符

Dart 支持通常的算术运算符,如下表所示。

运算符含义
+
-
-expr取反(改变一个表达式的正负号)
*
/
~/整数除,返回整数结果
%获取整数除的余数(取模)

例子:

assert(2 + 3 == 5);
assert(2 - 3 == -1);
assert(2 * 3 == 6);
assert(5 / 2 == 2.5); // 结果是 double
assert(5 ~/ 2 == 2); // 结果是 int
assert(5 % 2 == 1); // 取余

assert('5/2 = ${5 ~/ 2} r ${5 % 2}' == '5/2 = 2 r 1');

Dart 也支持前缀和后缀的自增、自减运算符:

运算符含义
++varvar = var + 1(表达式的值是 var + 1)
var--var = var + 1(表达式的值是 var)
--varvar = var -1(表达式的值是 var - 1)
var--var = var - 1(表达式的值是 var)

例子:

var a, b;

a = 0;
b = ++a; // 在过取值之前自增
assert(a == b); // 1 == 1

a = 0;
b = a++; // 在取值之后自增
assert(a != b); // 1 != 0

a = 0;
b = --a; // 在取值之前自减
assert(a == b); // -1 == -1

a = 0;
b = a--; // 在取值之后自减
assert(a != b); // -1 != 0

相等和关系运算符

下表列出了相等和关系运算符的含义。

运算符含义
==相等;请参阅下面的讨论
!=不等
>大于
<小于
>=大于等于
<=小于等于

要判断两个对象 x 和 y 是否代表相同的东西,使用 == 运算符。(在少数情况下,当你想知道两个对象是否是同一个对象,使用 identical()。)下面是 == 运算符的工作原理:

  1. 如果 x 或者 y 是空,仅当两者都是空时返回 true,否则返回 false。
  2. 返回方法 x.==(y) 的调用结果。(是的,像 == 这样的运算符就是在它第一个操作数上调用的方法。你甚至可以重载许多运算符,包括 ==,详情请参阅 重载运算符。

这里是一些使用相等和关系运算符的例子:

assert(2 == 2);
assert(2 != 3);
assert(3 > 2);
assert(2 < 3);
assert(3 >= 3);
assert(2 <= 3);

类型检查运算符

运算符 asisis! 可以在运行期方便地进行类型检查。

运算符含义
as类型转换
is如果对象拥有指定类型返回 true
is!如果对象拥有指定类型返回 false

如果 obj 实现了接口 T ,那么表达式 obj is T 的结果是 true。比如,obj is Object 总是返回 true。

使用 as 运算符将对象转换成指定类型。通常的,你应该使用它作为跟随在 is 后面使用该对象作为表达式这种情况的简写。比如,考虑以下代码:

if (emp is Person) {
  // 类型检查
  emp.firstName = 'Bob';
}

你可以使用 as 运算符使代码更简洁:

(emp as Person).firstName = 'Bob';

说明:上面的代码并不是完全等价的。如果 emp 是空或者不是 Person,第一个例子(使用 is)什么都不做,而第二个(使用 as)会抛出一个异常。

赋值运算符

如你所见,你可以使用 = 运算符进行赋值。要在仅当被赋值的变量为空时才进行赋值,使用 ??= 运算符。

// 赋值到 a
a = value;
// 如果 b 为空才赋值到 b;否则 b 保留原始值
b ??= value;

复合赋值运算符比如 += 组合了一个其他运算符到赋值运算符。

=-=/=%=>>=^=
+=*=~/<<=&=|=

下面解释复合赋值运算符的原理:

复合赋值等价的表达式
对运算符 opa op= ba = a op b
例子:a += ba = a + b

下面的例子同时使用了赋值运算符和复合赋值运算符:

var a = 2; // 使用 = 赋值
a *= 3; // 赋值并进行乘法:a = a * 3
assert(a == 6);

逻辑运算符

你可以使用逻辑运算符反转或者组合布尔表达式。

运算符含义
!expr反转后面表达式的值(false 变 true,true 变 false)
||逻辑或
&&逻辑与

下面是一个使用逻辑运算符的例子:

if (!done && (col == 0 || col == 3)) {
  // ...一些操作...
}

按位和移位运算符

在 Dart 中你可以操作数值中的各个位。通常你需要在整数上使用这些按位和移位运算符。

运算符含义
&按位与
|按位或
^按位异或
~expr一元按位补码(0s 变成 1s;1s 变成 0s)
<<左移位
>>右移位

下面是使用按位和移位操作符的例子:

final value = 0x22;
final bitmask = 0x0f;

assert((value & bitmask) == 0x02); // 按位与
assert((value & ~bitmask) == 0x20); // 按位与、按位补码
assert((value | bitmask) == 0x2f); // 按位或
assert((value ^ bitmask) == 0x2d); // 按位异或
assert((value << 4) == 0x220); // 左移位
assert((value >> 4) == 0x02); // 右移位

条件表达式

Dart 有两个表达式可以让你简明地计算可能需要 if-else 语句的表达式:

condition ? expr1 : expr2

如果 condition 是 true,计算 expr1*(并且返回它的值);否则,计算并返回 *expr2 的值。

expr1 ?? expr 2

如果 expr1 非空,返回它的值;否则,计算并返回 expr2 的值。

当你需要根据一个布尔表达式进行赋值时,考虑使用 ?:

var visibility = isPublic ? 'public' : 'private';

如果布尔表达式进行的是空值判断,考虑使用 ??

String playerName(String name) => name ?? 'Guest';

上一个例子至少可以改写为以下两种形式,但是没有那么简洁:

// 比使用 ?: 运算符的长一点
String playerName(String name) => name != null ? name : 'Guest';

// 使用 if-else 语句的长版本
String playerName(String name) {
  if (name != null) {
    return name;
  } else {
    return 'Guest';
  }
}

级联符号

级联 (..) 允许你在同一个对象上进行一系列操作。除了方法调用,你也可以访问同一个对象的属性。这样通常可以让你免于创建临时变量并且写出更加顺畅的代码。

考虑以下代码:

querySelector('#confirm') // 获取一个对象
  ..text = 'Confirm' // 使用它的成员
  ..classes.add('important')
  ..onClick.listen((e) => window.alert('Confirmed!'));

第一个方法调用,querySelector(),返回一个选择器对象。作用在这个选择器对象上的级联标记后面的代码,会忽略所有随后可能会有的返回值。

前面的代码等同于:

var button = querySelector('#confirm');
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((e) => window.alert('Confirmed!'));

你也可以嵌套级联。比如:

final addressBook = (AddressBookBuilder()
      ..name = 'jenny'
      ..email = 'jenny@example.com'
      ..phone = (PhoneNumberBuilder()
            ..number = '415-555-0100'
            ..label = 'home')
          .build())
    .build();

请小心地在那些返回真实对象的方法上构造级联。比如,下面的调用会失败:

var sb = StringBuffer();
sb.write('foo')
  ..write('bar'); // 错误:void 没有定义 'write' 方法

代码 sb.write() 返回 void,你不能在 void 上构造级联。

说明:严格地说,级联的”两点“标记不是一个运算符。它是 Dart 语法的一部分。

其他运算符

你已经在其他例子中看到过剩下的大部分运算符了:

运算符名称含义
()函数调用代表一个函数调用
[]列表访问引用列表中指定索引处的值
.成员访问引用一个表达式的属性;比如:foo.bar 选取了表达式 foobar 属性
?.有条件的成员访问类似 .,但是左操作数可以为空;比如:foo.bar 选取了表达式 foobar 属性除非 foo 是空(在这种情况下 foo?.bar 的值是空)

要了解更多关于 .?... 的内容,请参阅 类。