- 0.1:扩展方法是静态的
- 0.2:扩展可以具有泛型
- 0.3:扩展成员
- 0.4:来自社区的例子
在去年12月份,Dart发布了2.7版本。Dart 2.7添加了对扩展方法的支持,以及一个用于处理带有特殊字符的字符串的新程序包。 在DartPad中更新了null安全性(类型安全可为null和不可为null的类型)和全新的null安全体验。 在生态系统级别,pub.dev具有新的点赞功能,可为欣赏的软件包提供反馈。 Dart 2.7现已从dart.dev上以SDK下载的形式提供,并且已内置在Flutter 1.12版本中。
扩展方法
要使用扩展方法很简单:
extension 名字 on 需要扩展的类
让我们看一个例子:增加对从字符串解析整数和双精度的支持。 作为应用开发人员,无法更改String类,因为它是在dart:core库中定义的,但是通过扩展方法,可以对其进行扩展! 定义此扩展名后,可以在String上调用新的parseInt方法,就好像该方法是在String类本身上定义的一样:
extension ParseNumbers on String {
int parseInt() {
return int.parse(this);
}
double parseDouble() {
return double.parse(this);
}
}
main() {
int i = '42'.parseInt();
print(i);
}
扩展方法是静态的
扩展方法是静态的,这意味着无法在类型是dynamic的值上调用它们。 这里的调用在运行时抛出异常:
dynamic d = '2';
d.parseInt();
→ Runtime exception: NoSuchMethodError
扩展方法可以很好地配合Dart的类型推断,因此在下面的变量v中可以推断为具有String类型,并且可以使用String的扩展名:
var v = '1';
v.parseInt(); // 可行
由于扩展方法是静态的,因此它们的调用速度与调用静态函数一样快,调用语法更加友好。
扩展可以具有泛型
如果想在List上定义一个扩展,以使元素在偶数索引处希望此扩展方法可用于任何类型的List,并返回与输入List相同类型的新List。 我们可以通过使扩展名通用并将扩展名的类型参数应用于扩展名和扩展方法来做到这一点:
extension FancyList<T> on List<T> {
List<T> get evenElements {
return <T>[for (int i = 0; i < this.length; i += 2) this[i]];
}
}
扩展成员
功能扩展方法,因为如果在其他编程语言中使用了相应的语言功能,那将是熟悉的术语。 但是在Dart中,此功能更为通用:它还支持使用新的getter,setter和operator扩展。 在的FancyList示例中:
extension ShiftString on String {
String operator <<(int shift) {
return this.substring(shift, this.length) + this.substring(0, shift);
}
}
来自社区的例子
Jeremiah Ogbomo创建了time包,该包使用num扩展(整数和双精度的基类)来轻松构造Duration对象:
// 创建一个10分钟的Duration
Duration tenMinutes = 10.minutes;
//创建一个1.5小时的Duration
Duration oneHourThirtyMinutes = 1.5.hours;
//创建一个DateTime的使用操作符+的扩展
final DateTime afterTenMinutes = DateTime.now() + 10.minutes;
Simon Leier创建了dartx程序包,其中包含了许多核心Dart类型的扩展。 一些例子:
var allButFirstAndLast = list.slice(1, -2); // [1, 2, 3, 4]
var notBlank = ' .'.isBlank; // false
var file = File('some/path/testFile.dart');
print(file.name); // testFile.dart
print(file.nameWithoutExtension); // testFile
Brian Egan使用扩展方法更新流行的RxDart包,以以重新定义用于处理流的API。
更安全的子字符串处理
Dart的标准String类使用UTF-16编码。 这是编程语言中的常见的,尤其是那些支持在设备和Web上运行的语言。
UTF-16字符串通常可以很好地工作。 但是,在处理字符串时,尤其是在处理用户输入的字符串时,可能会遇到用户理解为字符的字符与UTF-16中编码为代码单元的字符之间的差异。 例如,提取用户输入的字符串的前三个字符:
var input = ['Resume'];
input.forEach((s) => print(s.substring(0, 3)));
$ dart main.dart
Res
到目前为止没有问题; 在输入列表中打印了字符串的前三个字符,结果为Res。 现在,考虑来自不同地区的用户,他们可能输入包含重音符号,韩文(韩文脚本)甚至是表情符号组合的字符串:
// New longer input list:
var input = ['Resume', 'Résumé', '이력서', '💼📃', 'Currículo'];
$ dart main.dart
Res
Ré
이력서
💼�
Cur
嗯,其中一些有效。 对于Re′sumé,得到一个“两个字符”的字符串, 对于💼📃,问号是怎么回事? 这里的问题在于Unicode。 实际上,Résume中的重音符号e是两个字符:e和组合的重音符号。 curl是单个字符,碰巧是用一对替代的U+d83d U+dcc3编码的。
通常不需要担心字符。 如果要做的只是接收,传递并移交整个字符串,则内部编码是透明的。 但是,如果需要遍历字符串的字符或操纵字符串的内容,则可能会遇到麻烦。 好消息是Dart 2.7引入了一个新的程序包:characters,用于处理这些情况。 该程序包支持将字符串视为用户感知的字符序列,也称为Unicode字形簇。 使用字符包,可以通过对缩短文本的代码进行少量更改来修复代码:
```
// Before:
input.forEach((s) => print(s.substring(0, 3)));
// After, using the characters package:
input.forEach((s) => print(s.characters.take(3)));
```
首先,从s中的字符串创建一个新的Characters实例(使用.characters扩展方法)。 然后,使用take()方法提取最初的3个字符。
空安全预览
几个月前,Dart团队宣布了在Dart中支持空安全性的意图,增加了对安全访问对象引用而不触发空引用异常的支持。 现在,为我们提供了一种预览空安全静态分析的方法:
void main() {
Person('Larry', birthday: DateTime(1973, 03, 26)).describe();
Person('Sergey').describe();
}
class Person {
String firstName;
DateTime birthday;
Person(this.firstName, {this.birthday});
void describe() {
print(firstName);
int birthyear = birthday?.year;
print('Born ${DateTime.now().year - birthyear} years ago');
}
}
如果运行代码,则在运行到第二个人时,它会以空指针异常崩溃。 犯了一个编码错误:虽然我们确实通过使构造函数中的Birthday字段为可选,并通过测试Birthday?.year中的生日来预测某些生日不明的人,但却忘记了生日为null的情况。
当尝试将此代码粘贴到新的空安全版本中,这是DartPad的特殊版本,其中包含空安全功能的静态分析部分的技术预览。 甚至没有运行代码,可以看到三个问题:
通过修复这些分析的错误:
- 要声明可能为空:DateTime birthday 改成 DateTime? birthday
- 要声明birthday为null时,birthyear也可能为null:int birthyear 改为 int? birthyear
- 判断是否为null:if (birthyear != null) {…}
Dart团队希望该示例可以很好地说明开发者想要的零空安全体验。 如前所述,这个例子只是正在开发中的部分安全技术的早期技术预览。 并且正在努力在Dart SDK中完成空安全的第一个beta版本。
在pub.dev上的软件包的👍功能
在pub.dev上的添加的是包的点赞功能。