运算符 3个月前

编程语言
539
运算符

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 也支持前缀和后缀的自增、自减运算符:

运算符 含义
++var var = var + 1(表达式的值是 var + 1)
var-- var = var + 1(表达式的值是 var)
--var var = 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;

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

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

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

复合赋值 等价的表达式
对运算符 op a op= b a = a op b
例子: a += b a = 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 的值是空)
image
EchoEcho官方
无论前方如何,请不要后悔与我相遇。
1377
发布数
439
关注者
2223661
累计阅读

热门教程文档

C++
73小节
C
14小节
爬虫
6小节
Lua
21小节
Flutter
105小节