Effective Dart
在过去的几年里,我们编写了大量的 Dart 代码,并学习了很多关于什么有效,什么无效的知识。我们与您分享这些,以便您也可以编写一致、健壮、快速的代码。这里有两个最重要的主题
保持一致性。 当涉及到格式化和大小写等问题时,关于哪个更好的争论是主观的,并且不可能解决。我们所知道的是,保持一致性在客观上有帮助。
如果两段代码看起来不同,那应该是由于它们在某些有意义的方面是不同的。当一段代码突出并引起您的注意时,它应该出于有用的原因这样做。
保持简洁。 Dart 被设计成易于理解的,因此它继承了 C、Java、JavaScript 和其他语言的许多相同语句和表达式。但我们创建 Dart 是因为这些语言提供的功能还有很大的改进空间。我们添加了许多功能,从字符串插值到初始化形式参数,以帮助您更简单、更轻松地表达您的意图。
如果有很多种方式来说明某件事,您通常应该选择最简洁的一种。这并不是说您应该代码高尔夫,将整个程序塞进一行代码中。目标是经济而非密集的代码。
指南
#我们将指南分成几个单独的页面,以便于消化
风格指南 – 这定义了布局和组织代码的规则,或者至少是
dart format
没有为您处理的部分。风格指南还指定了标识符的格式:camelCase
、using_underscores
等。文档指南 – 这告诉您关于注释内部内容的所有知识。包括文档注释和普通的、日常的代码注释。
用法指南 – 这教您如何最好地利用语言特性来实现行为。如果它在语句或表达式中,则此处涵盖。
设计指南 – 这是最柔性的指南,但范围最广。它涵盖了我们学到的关于为库设计一致、可用的 API 的知识。如果它在类型签名或声明中,则此处会介绍。
有关所有指南的链接,请参阅摘要。
如何阅读指南
#每个指南都分为几个部分。部分包含指南列表。每个指南都以以下词语之一开头
DO 指南描述了应始终遵循的做法。几乎永远没有正当理由偏离它们。
DON'T 指南是相反的:几乎永远不是好主意的事情。希望我们没有其他语言那么多这些,因为我们的历史包袱较少。
PREFER 指南是您应该遵循的做法。但是,在某些情况下,这样做可能更有意义。只需确保您在忽略指南时充分理解其全部含义。
AVOID 指南是“prefer”的双重:您不应该做的事情,但在极少数情况下可能有充分的理由这样做。
CONSIDER 指南是您可能希望或可能不希望遵循的做法,具体取决于情况、先例和您自己的偏好。
一些指南描述了规则不适用的例外情况。列出时,例外情况可能并不详尽 — 您可能仍然需要在其他情况下使用您的判断。
这听起来像是如果您没有正确系好鞋带,警察就会破门而入。事情并没有那么糟糕。这里的大多数指南都是常识,我们都是理性的人。一如既往的目标是编写良好、可读且可维护的代码。
Dart 分析器提供了一个 linter,以帮助您编写良好、一致的代码,这些代码遵循这些和其他指南。如果存在一个或多个可以帮助您遵循指南的 linter 规则,则该指南会链接到这些规则。链接使用以下格式
Linter 规则:unnecessary_getters_setters
要了解如何使用 linter,请参阅启用 linter 规则和 linter 规则列表。
词汇表
#为了保持指南的简洁,我们使用一些简写术语来指代不同的 Dart 构造。
库成员是顶层字段、getter、setter 或函数。基本上,顶层任何不是类型的东西。
类成员是在类内部声明的构造函数、字段、getter、setter、函数或运算符。类成员可以是实例或静态,抽象或具体。
成员是库成员或类成员。
变量,当一般使用时,指的是顶层变量、参数和局部变量。它不包括静态或实例字段。
类型是任何命名的类型声明:类、typedef 或枚举。
属性是顶层变量、getter(在类内部或顶层,实例或静态)、setter(相同)或字段(实例或静态)。大致任何“类似字段”的命名构造。
所有规则摘要
#风格
#标识符
- DO 使用
UpperCamelCase
命名类型。 - DO 使用
UpperCamelCase
命名扩展。 - DO 使用
lowercase_with_underscores
命名包、目录和源文件。 - DO 使用
lowercase_with_underscores
命名导入前缀。 - DO 使用
lowerCamelCase
命名其他标识符。 - PREFER 对常量名称使用
lowerCamelCase
。 - DO 像单词一样大写超过两个字母的首字母缩略词和缩写词。
- PREFER 对未使用的回调参数使用通配符。
- DON'T 对非私有标识符使用前导下划线。
- DON'T 使用前缀字母。
- DON'T 显式命名库。
排序
格式化
文档
#注释
文档注释
- DO 使用
///
文档注释来记录成员和类型。 - PREFER 为公共 API 编写文档注释。
- CONSIDER 编写库级别的文档注释。
- CONSIDER 为私有 API 编写文档注释。
- DO 以单句摘要开始文档注释。
- DO 将文档注释的第一句话分成自己的段落。
- AVOID 与周围上下文的冗余。
- PREFER 以第三人称动词开始函数或方法注释。
- PREFER 以名词短语开始非布尔变量或属性注释。
- PREFER 以 "Whether" 开头,后跟名词或动名词短语来开始布尔变量或属性注释。
- DON'T 为属性的 getter 和 setter 都编写文档。
- PREFER 以名词短语开始库或类型注释。
- CONSIDER 在文档注释中包含代码示例。
- DO 在文档注释中使用方括号来引用作用域内的标识符。
- DO 使用散文来解释参数、返回值和异常。
- DO 将文档注释放在元数据注释之前。
Markdown
写作
用法
#库
Null
- DON'T 显式将变量初始化为
null
。 - DON'T 使用
null
的显式默认值。 - DON'T 在相等运算中使用
true
或false
。 - AVOID 如果您需要检查
late
变量是否已初始化,则避免使用它。 - CONSIDER 类型提升或空检查模式来使用可空类型。
字符串
集合
- DO 在可能的情况下使用集合字面量。
- DON'T 使用
.length
来查看集合是否为空。 - AVOID 将
Iterable.forEach()
与函数字面量一起使用。 - DON'T 使用
List.from()
,除非您打算更改结果的类型。 - DO 使用
whereType()
按类型过滤集合。 - DON'T 在附近的运算可以完成时使用
cast()
。 - AVOID 使用
cast()
。
函数
变量
成员
- DON'T 不必要地将字段包装在 getter 和 setter 中。
- PREFER 使用
final
字段来创建只读属性。 - CONSIDER 对简单成员使用
=>
。 - DON'T 使用
this.
,除非重定向到命名构造函数或避免阴影。 - DO 在声明时初始化字段(如果可能)。
构造函数
- DO 在可能的情况下使用初始化形式参数。
- DON'T 在构造函数初始化列表可以完成时使用
late
。 - DO 对空构造函数体使用
;
而不是{}
。 - DON'T 使用
new
。 - DON'T 冗余地使用
const
。
错误处理
- AVOID 没有
on
子句的 catch 子句。 - DON'T 丢弃来自没有
on
子句的 catch 子句的错误。 - DO 仅对程序错误抛出实现
Error
的对象。 - DON'T 显式捕获
Error
或实现它的类型。 - DO 使用
rethrow
重新抛出捕获的异常。
异步
设计
#名称
- DO 一致地使用术语。
- AVOID 缩写。
- PREFER 将最具描述性的名词放在最后。
- CONSIDER 使代码读起来像一个句子。
- PREFER 为非布尔属性或变量使用名词短语。
- PREFER 为布尔属性或变量使用非祈使动词短语。
- CONSIDER 为命名的布尔参数省略动词。
- PREFER 为布尔属性或变量使用“肯定”名称。
- PREFER 为主要目的是副作用的函数或方法使用祈使动词短语。
- PREFER 如果返回值是函数或方法的主要目的,则使用名词短语或非祈使动词短语。
- CONSIDER 如果您想引起对函数或方法执行的工作的注意,则使用祈使动词短语。
- AVOID 以
get
开头的方法名称。 - PREFER 如果方法将其对象的状态复制到新对象,则将其命名为
to___()
。 - PREFER 如果方法返回由原始对象支持的不同表示形式,则将其命名为
as___()
。 - AVOID 在函数或方法的名称中描述参数。
- DO 在命名类型参数时遵循现有的助记约定。
库
类和混入
- AVOID 在简单的函数可以完成时定义单成员抽象类。
- AVOID 定义仅包含静态成员的类。
- AVOID 扩展不打算被子类化的类。
- DO 使用类修饰符来控制您的类是否可以被扩展。
- AVOID 实现不打算作为接口的类。
- DO 使用类修饰符来控制您的类是否可以作为接口。
- PREFER 为
mixin class
定义纯mixin
或纯class
。
构造函数
成员
- PREFER 使字段和顶层变量为
final
。 - DO 对概念上访问属性的操作使用 getter。
- DO 对概念上更改属性的操作使用 setter。
- DON'T 在没有相应 getter 的情况下定义 setter。
- AVOID 使用运行时类型测试来伪造重载。
- AVOID 没有初始值设定项的公共
late final
字段。 - AVOID 返回可空的
Future
、Stream
和集合类型。 - AVOID 从方法返回
this
只是为了启用流畅的接口。
类型
- DO 类型注释没有初始值设定项的变量。
- DO 如果类型不明显,则类型注释字段和顶层变量。
- DON'T 冗余地类型注释初始化的局部变量。
- DO 注释函数声明的返回类型。
- DO 注释函数声明的参数类型。
- DON'T 注释函数表达式上推断的参数类型。
- DON'T 类型注释初始化形式参数。
- DO 在未推断的泛型调用上编写类型参数。
- DON'T 在推断的泛型调用上编写类型参数。
- AVOID 编写不完整的泛型类型。
- DO 使用
dynamic
注释而不是让推断失败。 - PREFER 函数类型注释中的签名。
- DON'T 为 setter 指定返回类型。
- DON'T 使用旧式的 typedef 语法。
- PREFER 内联函数类型而不是 typedef。
- PREFER 对参数使用函数类型语法。
- AVOID 使用
dynamic
,除非您想禁用静态检查。 - DO 使用
Future<void>
作为不生成值的异步成员的返回类型。 - AVOID 使用
FutureOr<T>
作为返回类型。
参数
相等性
除非另有说明,否则本网站上的文档反映的是 Dart 3.7.1 版本。页面上次更新于 2024-05-06。 查看源代码 或 报告问题。