「这是我参与2022首次更文挑战的第11天,活动详情查看:2022首次更文挑战」。
本文翻译自pub: equatable | Dart Package (flutter-io.cn)
译时版本: equatable: 2.0.3
概览
在 Dart 中使比较对象可用经常会涉及到覆写 == 操作符和 hashCode 。
这样不只是啰嗦冗长,比较失败的话,还会导致代码性能低下,出现期望之外的行为。
默认情况下,如果两个对象是同一个实例,则 == 返回 true 。
比方说有下面这样的类:
class Person {
const Person(this.name);
final String name;
}
可以如下创建实例:
void main() {
final Person bob = Person("Bob");
}
之后试着比较 Person 的两个实例,不管在是生产环境代码中,还是在测试代码中,都会出现问题:
print(bob == Person("Bob")); // false
关于这一点的更多信息,可参考官方 Dart 文档 。
为了能比较 Person 的两个实例,我们需要修改类,覆写 == 和 hashCode 如下:
class Person {
const Person(this.name);
final String name;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Person &&
runtimeType == other.runtimeType &&
name == other.name;
@override
int get hashCode => name.hashCode;
}
现在如果再次运行以下代码:
print(bob == Person("Bob")); // true
它就能够对 Person 的不同实例进行比较了。
可以看到当处理复杂的类时,这会如何快速地变得很麻烦。这就是 Equatable 的用武之地!
Equatable 做什么?
Equatable 已为你覆写 == 和 hashCode ,所以你不需要浪费时间写大量的样板代码。
也有其它的包确实会为你生成样板代码,尽管这样,你仍然需要运行代码生成的步骤,这并不是理想的。
使用 Equatable , 不会有代码生成,我们可以更多地聚焦于写惊艳的应用和更少地世俗工作。
使用
首先,我们需要在 pubspec.yaml 文件中添加 equatable 依赖:
dependencies:
equatable: ^2.0.0
接下来,我们需要安装它:
# Dart
pub get
# Flutter
flutter packages get
最后,我们需要继承 Equatable :
import 'package:equatable/equatable.dart';
class Person extends Equatable {
const Person(this.name);
final String name;
@override
List<Object> get props => [name];
}
同时使用 json :
import 'package:equatable/equatable.dart';
class Person extends Equatable {
const Person(this.name);
final String name;
@override
List<Object> get props => [name];
factory Person.fromJson(Map<String, dynamic> json) {
return Person(json['name']);
}
}
我们现在可以只像以前一样比较 Person 的实例,没有写所有样板代码的痛苦。
注意: Equatable 设计成只用于不可改变的对象,所以所有的成员必须是 final (这不只是 Equatable 的特性 - 使用可改变的值覆写 hashCode 会破坏基于 hash 的集合)。
Equatable 也支持 const 构造方法:
import 'package:equatable/equatable.dart';
class Person extends Equatable {
const Person(this.name);
final String name;
@override
List<Object> get props => [name];
}
toString 实现
Equatable 可以实现含有所有赋予的 props 的 toString 方法。如果想要为指定的 Equatable 对象这样处理,只需要包含如下代码:
@override
bool get stringify => true;
例:
import 'package:equatable/equatable.dart';
class Person extends Equatable {
const Person(this.name);
final String name;
@override
List<Object> get props => [name];
@override
bool get stringify => true;
}
对于 name Bob , 输出会如下:
Person(Bob)
这个标志默认是 false , toString 只会返回类型:
Person
EquatableConfig
stringify 可以通过 EquatableConfig 为所有的 Equatable 实例进行全局配置:
EquatableConfig.stringify = true;
如果 stringify 被指定的 Equatable 类覆写,则 EquatableConfig.stringify 的值会被忽略。换句话说,本地的配置优先于全局的配置。
注意: EquatableConfig.stringify 在调试模式默认为 true ,在发布模式默认为 false 。
扼要重述
不使用 Equatable
class Person {
const Person(this.name);
final String name;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Person &&
runtimeType == other.runtimeType &&
name == other.name;
@override
int get hashCode => name.hashCode;
}
使用 Equatable
import 'package:equatable/equatable.dart';
class Person extends Equatable {
const Person(this.name);
final String name;
@override
List<Object> get props => [name];
}
EquatableMixin
有的时候,由于类已经继承了一个父类,所以不能再继承 Equatable 。这种情况下, 你仍然可以通过使用 EquatableMixin 来获得 Equatable 的便利:
使用
比方说我们想创建一个 EquatableDateTime 类,我们可以如下使用 EquatableMixin :
class EquatableDateTime extends DateTime with EquatableMixin {
EquatableDateTime(
int year, [
int month = 1,
int day = 1,
int hour = 0,
int minute = 0,
int second = 0,
int millisecond = 0,
int microsecond = 0,
]) : super(year, month, day, hour, minute, second, millisecond, microsecond);
@override
List<Object> get props {
return [year, month, day, hour, minute, second, millisecond, microsecond];
}
}
现在我们想要创建 EquatableDateTime 的一个子类,我们只需要覆写 props :
class EquatableDateTimeSubclass extends EquatableDateTime {
final int century;
EquatableDateTimeSubclass(
this.century,
int year,[
int month = 1,
int day = 1,
int hour = 0,
int minute = 0,
int second = 0,
int millisecond = 0,
int microsecond = 0,
]) : super(year, month, day, hour, minute, second, millisecond, microsecond);
@override
List<Object> get props => super.props..addAll([century]);
}
性能
你可能想知道如果你使用了 Equatable ,性能会有何影响。
结果(平均运行超过10次)
相等比较 A == A
| 类 | 运行时间 (μs) |
|---|---|
| 手动 | 0.193 |
| 空的Equatable | 0.191 |
| 含有Equatable | 0.190 |
实例化 A()
| 类 | 运行时间 (μs) |
|---|---|
| 手动 | 0.165 |
| 空的Equatable | 0.181 |
| 含有Equatable | 0.182 |
*性能测试运行于:Dart VM version: 2.4.0