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、函数或运算符。类成员可以是实例或静态的,抽象的或具体的。
成员 是库成员或类成员。
变量 在一般使用时,指的是顶级变量、参数和局部变量。它不包括静态或实例字段。
类型 是任何命名的类型声明:类、类型定义或枚举。
属性 是顶级变量、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 在文档注释中使用方括号来引用作用域内的标识符。
- 应该使用散文来解释参数、返回值和异常。
- 应该将文档注释放在元数据注解之前。
Markdown
写作
用法
#库
Null
- 不要将变量显式初始化为
null
。 - 不要使用显式的
null
默认值。 - 不要在相等操作中使用
true
或false
。 - 如果需要检查
late
变量是否已初始化,则应避免使用late
变量。 - 考虑使用类型提升或空值检查模式来使用可空类型。
字符串
集合
- 应该尽可能使用集合字面量。
- 不要使用
.length
来检查集合是否为空。 - 避免对函数字面量使用
Iterable.forEach()
。 - 除非你打算更改结果的类型,否则不要使用
List.from()
。 - 应该使用
whereType()
按类型过滤集合。 - 当附近的运算可以实现时,不要使用
cast()
。 - 避免使用
cast()
。
函数
变量
成员
- 不要不必要地将字段包装在 getter 和 setter 中。
- 优先使用
final
字段来创建只读属性。 - 对于简单的成员,考虑使用
=>
。 - 除非重定向到命名构造函数或避免阴影化,否则不要使用
this.
。 - 应该尽可能在其声明时初始化字段。
构造函数
错误处理
- 避免使用没有
on
子句的 catch 块。 - 不要丢弃没有
on
子句的 catch 块中的错误。 - 应该仅为程序性错误抛出实现
Error
的对象。 - 不要显式捕获
Error
或实现它的类型。 - 应该使用
rethrow
来重新抛出捕获的异常。
异步
设计
#名称
- 应该一致地使用术语。
- 避免缩写。
- 优先将最具描述性的名词放在最后。
- 考虑使代码读起来像一个句子。
- 对于非布尔属性或变量,优先使用名词短语。
- 对于布尔属性或变量,优先使用非祈使动词短语。
- 对于命名的布尔参数,考虑省略动词。
- 对于布尔属性或变量,优先使用“肯定”的名称。
- 对于主要目的是副作用的函数或方法,优先使用祈使动词短语。
- 如果返回值是函数或方法的主要目的,则优先使用名词短语或非祈使动词短语。
- 如果你想引起人们对函数或方法执行的工作的注意,则可以考虑使用祈使动词短语。
- 避免以
get
开头的方法名称。 - 如果方法将对象的状态复制到新对象,则优先将其命名为
to___()
。 - 如果方法返回由原始对象支持的不同表示形式,则优先将其命名为
as___()
。 - 避免在函数或方法名称中描述参数。
- 在命名类型参数时,应该遵循现有的助记约定。
库
类和混入
- 当使用一个简单的函数可以实现时,避免定义只有一个成员的抽象类。
- 避免定义仅包含静态成员的类。
- 避免扩展不打算被子类化的类。
- 如果你的类支持扩展,请进行文档说明。
- 避免实现不打算作为接口的类。
- 如果你的类支持用作接口,请进行文档说明。
- 优先使用纯
mixin
或纯class
而不是mixin class
。
构造函数
成员
- 优先使字段和顶级变量为
final
。 - 应该对概念上访问属性的操作使用 getter。
- 应该对概念上更改属性的操作使用 setter。
- 不要定义没有相应 getter 的 setter。
- 避免使用运行时类型测试来伪造重载。
- 避免使用没有初始化程序的公共
late final
字段。 - 避免返回可空的
Future
、Stream
和集合类型。 - 避免从方法返回
this
只是为了启用流畅的接口。
类型
- 应该对没有初始化程序的变量进行类型注解。
- 如果类型不明显,则应该对字段和顶级变量进行类型注解。
- 不要冗余地对初始化的局部变量进行类型注解。
- 应该在函数声明上注解返回类型。
- 应该在函数声明上注解参数类型。
- 不要在函数表达式上注解推断的参数类型。
- 不要对初始化形参进行类型注解。
- 应该在未推断的泛型调用上编写类型参数。
- 不要在已推断的泛型调用上编写类型参数。
- 避免编写不完整的泛型类型。
- 应该使用
dynamic
进行注解,而不是让推断失败。 - 优先在函数类型注解中使用签名。
- 不要为 setter 指定返回类型。
- 不要使用旧的 typedef 语法。
- 优先使用内联函数类型而不是 typedef。
- 优先使用函数类型语法表示参数。
- 除非你想禁用静态检查,否则避免使用
dynamic
。 - 对于不产生值的异步成员,应该使用
Future<void>
作为返回类型。 - 避免使用
FutureOr<T>
作为返回类型。
参数
相等
除非另有说明,本网站上的文档反映了 Dart 3.6.0。页面最后更新于 2024-05-06。 查看源代码 或 报告问题。