Flutter学习之旅,Dart高效代码编写方式-空值、字符串、集合、函数

一,空值

不要将变量明确初始化为null

如果变量具有不可为null的类型,则如果在未明确初始化变量之前尝试使用它,则Dart会报告编译错误。如果该变量可为空,则将null为您隐式初始化为。Dart中没有“未初始化的内存”的概念,也不需要显式地将变量初始化null为“安全”。

好的写法

Item? bestItem;

差的写法

Item? bestItem = null;

 请勿使用明确的默认值null

如果将可为空的参数设置为可选参数,但未给其提供默认值,则该语言将隐式null用作默认值,因此无需编写该参数。

考虑将可为空的字段复制到局部变量以启用类型提升。

检查可为空的变量不等于将其null提升为不可为空的类型。这样,您就可以访问变量中的成员,并将其传递给需要非空类型的函数。不幸的是,提升仅适用于局部变量和参数,因此不会提升字段和顶级变量。

解决此问题的一种模式是将字段的值复制到局部变量。对该变量的Null检查确实会提升,因此您可以放心地将其视为不可为空。

好的写法:

class UploadException {
  final Response? response;

  UploadException([this.response]);

  @override
  String toString() {
    var response = this.response;
    if (response != null) {
      return "Could not complete upload to ${response.url} "
          "(error code ${response.errorCode}): ${response.reason}.";
    }

    return "Could not upload (no response).";
  }
}

与使用!字段或顶级变量的每个位置相比,复制到局部变量可以更清洁,更安全:

差的写法:

class UploadException {
  final Response? response;

  UploadException([this.response]);

  @override
  String toString() {
    if (response != null) {
      return "Could not complete upload to ${response!.url} "
          "(error code ${response!.errorCode}): ${response!.reason}.";
    }

    return "Could not upload (no response).";
  }
}

 二,字符串
不要使用相邻的字符串来连接字符串文字。

如果您有两个字符串文字(不是值,而是实际引用的文字形式),则无需使用+它们来串联它们。就像在C和C ++中一样,只需将它们彼此相邻放置即可。这是制作单个一行不适合的长字符串的好方法。

好的写法

raiseAlarm(
    'ERROR: Parts of the spaceship are on fire. Other '
    'parts are overrun by martians. Unclear which are which.');

 差的写法

raiseAlarm('ERROR: Parts of the spaceship are on fire. Other ' +
    'parts are overrun by martians. Unclear which are which.');

优选使用插值来组成字符串和值。

好的写法

'Hello, $name! You are ${year - birth} years old.';

差的写法

'Hello, ' + name + '! You are ' + (year - birth).toString() + ' y...';

避免在不需要时在插值中使用花括号。

如果要插入一个简单标识符,而不是紧随其后的是更多字母数字文本,{}则应省略。

好的写法

var greeting = 'Hi, $name! I love your ${decade}s costume.';

差的写法

var greeting = 'Hi, ${name}! I love your ${decade}s costume.';

三、集合类型

尽可能使用集合字面量。

Dart具有三种核心集合类型:列表,地图和集合。像大多数类一样,Map和Set类具有未命名的构造函数。但是,由于这些集合的使用频率很高,因此Dart具有更好的内置语法来创建它们:

好的写法

var points = <Point>[];
var addresses = <String, Address>{};
var counts = <int>{};

差的写法

var addresses = Map<String, Address>();
var counts = Set<int>();

集合字面量在Dart中特别强大,因为它们使您能够访问散布运算符 (...),以包括其他集合的内容,以及是否以及在构建内容时执行控制流:

好的写法

var arguments = [
  ...options,
  command,
  ...?modeFlags,
  for (var path in filePaths)
    if (path.endsWith('.dart'))
      path.replaceAll('.dart', '.js')
];

差的写法

var arguments = <String>[];
arguments.addAll(options);
arguments.add(command);
if (modeFlags != null) arguments.addAll(modeFlags);
arguments.addAll(filePaths
    .where((path) => path.endsWith('.dart'))
    .map((path) => path.replaceAll('.dart', '.js')));

不要用.length看集合是否为空。

可迭代合同不要求集合知道其长度或能够在恒定时间内提供它。 仅查看集合中是否包含任何内容而调用.length可能会非常缓慢。

而是有更快,更易读的getter:.isEmpty和.isNotEmpty。 使用不需要您否定结果的结果。

好的写法

if (lunchBox.isEmpty) return 'so hungry...';
if (words.isNotEmpty) return words.join(' ');

差的写法

if (lunchBox.length == 0) return 'so hungry...';
if (!words.isEmpty) return words.join(' ');

避免Iterable.forEach()在函数遍历

forEach()函数在JavaScript中得到了广泛使用,因为内置 for-in循环无法实现您通常想要的功能。在Dart中,如果要遍历序列,惯用的方法是使用循环。

好的写法

for (var person in people) {
  ...
}

差的写法

people.forEach((person) {
  ...
});

另请注意,始终可以使用Map.forEach()。不是可迭代的,因此该指南不适用。

除非您打算更改结果的类型,否则请勿使用List.from()

给定一个List,有两种显而易见的方法来产生一个包含相同元素的新List:

var copy1 = iterable.toList();
var copy2 = List.from(iterable);

明显的区别是第一个较短。的重要 区别在于,第一个保留原始对象的类型的参数:

好的写法:

// Creates a List<int>:
var iterable = [1, 2, 3];

// Prints "List<int>":
print(iterable.toList().runtimeType);

差的写法

// Creates a List<int>:
var iterable = [1, 2, 3];

// Prints "List<dynamic>":
print(List.from(iterable).runtimeType);

如果更改类型,则调用List.from()非常有用:

var numbers = [1, 2.3, 4]; // List<num>.
numbers.removeAt(1); // Now it only contains integers.
var ints = List<int>.from(numbers);

但是,如果您的目标只是复制可迭代对象并保留其原始类型,或者您不关心该类型,请使用toList()

使用whereType()按类型过滤集合。

假设您有一个包含对象混合的列表,而您只想从中获取整数。您可以这样使用where()

差的写法

var objects = [1, "a", 2, "b", 3];
var ints = objects.where((e) => e is int);

这很冗长,但更糟糕的是,它返回一个可能不是您想要的类型的Iterable。在此处的示例中,Iterable<Object>即使您可能想要一个,它也会返回一个,Iterable<int>因为这是您要对其进行过滤的类型。

有时您会看到通过添加cast()以下代码来“纠正”上述错误的代码:

差的写法

var objects = [1, "a", 2, "b", 3];
var ints = objects.where((e) => e is int).cast<int>();

这很冗长,并导致创建两个包装器,并具有两层间接和冗余运行时检查。幸运的是,核心库具有whereType()用于此确切用例的方法:

好的写法

var objects = [1, "a", 2, "b", 3];
var ints = objects.whereType<int>();

当附近的操作可以使用时,请勿使用cast()

通常,当您处理可迭代项或流时,会对它进行多次转换。 最后,您想产生一个带有特定类型参数的对象。 而不是附加对cast()的调用,请查看现有转换之一是否可以更改类型。

如果您已经在调用toList(),则将其替换为对List <T> .from()的调用,其中T是您想要的结果列表的类型。

好的写法

var stuff = <dynamic>[1, 2];
var ints = List<int>.from(stuff);

差的写法

var stuff = <dynamic>[1, 2];
var ints = stuff.toList().cast<int>();

如果要调用map(),请为其提供一个显式类型参数,以便它生成所需类型的可迭代对象。 类型推断通常基于传递给map()的函数为您选择正确的类型,但有时您需要明确。

好的写法

var stuff = <dynamic>[1, 2];
var reciprocals = stuff.map<double>((n) => 1 / n);

差的写法

var stuff = <dynamic>[1, 2];
var reciprocals = stuff.map((n) => 1 / n).cast<double>();

避免使用cast()。

这是先前规则的较简单的概括。有时没有附近的操作可用于固定某些对象的类型。即使这样,在可能的情况下,也请避免使用cast()“更改”集合的类型。

最好选择以下任一选项:

用正确的类型创建它。更改首次创建集合的代码,以使其具有正确的类型。

在访问时投射元素。如果您立即遍历集合,请在迭代中强制转换每个元素。

使用List.from()进行强制转换。如果您最终将访问集合中的大多数元素,并且不需要原始活动对象支持该对象,请使用List.from()对其进行转换。

cast()方法返回一个惰性集合,该集合检查每个操作上的元素类型。如果仅对几个元素执行几个操作,那么懒惰就可以了。但是在许多情况下,延迟验证和包装的开销超过了好处。

这是使用正确类型创建它的示例:

好的写法

List<int> singletonList(int value) {
  var list = <int>[];
  list.add(value);
  return list;
}

差的写法

List<int> singletonList(int value) {
  var list = []; // List<dynamic>.
  list.add(value);
  return list.cast<int>();
}

这是在访问时强制转换每个元素:

好的写法

void printEvens(List<Object> objects) {
  // We happen to know the list only contains ints.
  for (var n in objects) {
    if ((n as int).isEven) print(n);
  }
}

差的写法

void printEvens(List<Object> objects) {
  // We happen to know the list only contains ints.
  for (var n in objects.cast<int>()) {
    if (n.isEven) print(n);
  }
}

这里需要使用List.from()

好的写法

int median(List<Object> objects) {
  // We happen to know the list only contains ints.
  var ints = List<int>.from(objects);
  ints.sort();
  return ints[ints.length ~/ 2];
}

差的写法

int median(List<Object> objects) {
  // We happen to know the list only contains ints.
  var ints = objects.cast<int>();
  ints.sort();
  return ints[ints.length ~/ 2];
}

四、函数

在Dart中,甚至函数都是对象

tear-off时不要创建匿名函数。

如果您引用对象上的方法但省略括号,则Dart会给您“撕除”(tear-off),该闭包采用与该方法相同的参数,并在调用该方法时调用它。

如果您有一个函数使用与传递给它的参数相同的参数来调用方法,则无需手动将调用包装在匿名函数中。

使用=将命名参数与其默认值分开。

由于遗留原因,Dart允许:和都=将命名参数用作默认值分隔符。为了与可选的位置参数保持一致,请使用 =

好的写法

void insert(Object item, {int at = 0}) { ... }

差的写法

void insert(Object item, {int at: 0}) { ... }

 

 

 

 

 

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值