Dart 编码风格指南

Dart 编码风格指南

类型注解

对于公有 API,最好提供类型注解。

类型注解是非常重要的文档,它说明了相应的库应当如何使用。为参数以及公有方法的返回类型注解有利于使用者了解 API 需要什么参数以及它能提供什么功能。

但是,如果有个 API 可以接收任何参数,或者是 Dart 中无法表示的值,那么该 APi 可以不用添加注解。

对于库内部的代码(即便是私有的,或者是嵌套的函数),请再你认为有帮助的地方添加注解,但是不要认为你必须提供这些注解。

install(id, destPath) {    // bad
  // ...
}

在上面的代码中,我们就不清楚 id 到底是什么。字符串?那么 destPath 又是什么呢?字符串还是文件对象?这个函数是同步的还是异步的?

Future<bool> install(PackageId id, String destPath) {     // good
  // ...
}

当你加上类型之后,这个函数的相关信息也就说明白了。

对于局部变量,最好是使用 var 而不是类型注解来声明。

现在,我们更加倾向于让函数体的代码尽可能的简洁,并且局部变量的类型在初始化表达式中是可以推测出来的,在这种情况下显示声明其类型是没有必要的。好点的编辑器可以推测出局部变量的类型,所以自动补全功能仍然是可用的,并且你所期望的其他功能也可以正常使用。

Map<int, List<Person>> groupByZip(Iterable<Person> people) {    // good 
  var peopleByZip = new Map<int, List<Person>>();
  for (var person in people) {
    peopleByZip.putIfAbsent(person.zip, () => <Person>[]);
    peopleByZip[person.zip].add(person);
  }
  return peopleByZip;
}
Map<int, List<Person>> groupByZip(Iterable<Person> people) {    // bad
  Map<int, List<Person>> peopleByZip = new Map<int, List<Person>>();
  for (Person person in people) {
    peopleByZip.putIfAbsent(person.zip, () => <Person>[]);
    peopleByZip[person.zip].add(person);
  }
  return peopleByZip;
}

如果对代码的运行性能有要求,那么在传递参数的时候,类型注解最好是用 double 或者 int 来代替 num

单一调用点(有着稳定输入类型)在优化时要比多态调用点(其输入类型可能会发生变化)更加容易。

对于数字类型,在添加类型注解时你应该指明具体的数字类型。明确地声明 double 或者 int 有助于使用你方法的人为之传递相应的参数。

在正式初始化的时候不要做类型注解。

如果一个构造函数的参数使用了 this. 来初始化字段,那么该参数的类型必然和这个字段相同,故而不必使用类型注解。

class Point {    // good
  int x, y;
  Point(this.x, this.y);
}
class Point {    // bad
  int x, y;
  Point(int this.x, int this.y);
}

你应该避免在函数表达式中使用类型注解。

函数表达式的一大优点就是它的简洁性。如果一个函数复杂到需要注解类型来理解它,它就应该是一个函数语句或者是一个方法。相应的,如果一个函数简洁到可以作为一个表达式,那么它就不需要类型注解。

var names = people.map((person) => person.name);    // good
var names = people.map((Person person) {    // bad
  return person.name;
});

应当避免在不需要的时候使用 dynamic 来添加类型注解。

在 Dart 中,多数情况下类型注解都是可以忽略的,因为它们会被自动当做 dynamic 类型。所以,不添加类型注解在语义上是完全没问题的,并且代码会显得更简洁。

lookUpOrDefault(String name, Map map, defaultValue) {    // good
  var value = map[name];
  if (value != null) return value;
  return defaultValue;
}
dynamic lookUpOrDefault(String name, Map map, dynamic defaultValue) {    // bad
  var value = map[name];
  if (value != null) return value;
  return defaultValue;
}

对于接收的任何对象,应该使用 Object 代替 dynamic 来表明参数是对象。

有些操作可能对于任何对象都适用。比如,toString() 用于输出信息,并且可以用于任何对象。在 Dart 中有两种类型可以匹配所有对象:Object 以及 dynamic。但是,它们表示的是两种不同的东西。

Object 注解表明 “我可以接收任何对象,只要它含有相关对象自身定义的方法。”

dynamic 类型注解表明没有注解可以说明你实际允许的对象是什么类型。(也可能有,但是你不在意是否要声明)

//good
// Accepts any object.
void log(Object object) {
  print(object.toString());
}

// Only accepts bool or String, which can't be expressed in a type annotation.
bool convertToBool(arg) {
  if (arg is bool) return arg;
  if (arg is String) return arg == 'true';
  throw new ArgumentError('Cannot convert $arg to a bool.');
}
07-10 10:39