美文网首页Flutter圈子Flutter 入门与实战
Dart 的函数参数怎么传更合理?

Dart 的函数参数怎么传更合理?

作者: 岛上码农 | 来源:发表于2022-05-14 19:48 被阅读0次

前言

函数是我们用得最多的了,通过函数可以将复杂业务拆分为简短精悍的函数,从而分解大业务,提高可维护性和代码复用性。在设计函数的时候,接收什么样的参数最为关键,参数定义了函数的输入。本篇来介绍 Dart 函数的合理使用。

Dart 函数参数简介

在 Dart 中,将参数传递给函数的方式有两种,包括占位方式和命名方式,再加上是否可为空组合起来有4种,如下所示。

// 占位参数形式,第二个参数需要传
void someFunction1(int a,  String? b) {
  // ...
}

// 占位参数形式,第二个参数可不传
void someFunction2(int a,  [String? b]) {
  // ...
}

// 命名参数形式,p 为必传参数
void someFunction4(int a,  {required Person p, Student? s} ) {
  // ...
}


// 命名参数形式,p 和 s 为可选参数
void someFunction4(int a,  {Person? p, Student? s} ) {
  // ...
}

其中可选占位参数使用[...]形式,表示其中的参数可传可不传。命名参数采用{...}形式,如果参数必传则需要声明为 required。注意,可选占位参数和命名参数不可同时使用,例如下面的形式会报错。

// 错误!可选占位参数和命名参数不可共用
void someFunction4(int a, [String? b], {Person? p, Student? s} ) {
  // ...
}

由于 Dart 存在这两种形式,按说是用哪种形式都行,但是实际应用中还是需要遵循一定的规范。

规范1:避免对布尔参数使用占位形式传递

由于布尔值在字面上很难知晓具体的意思,因此,布尔参数若作为占位形式参数传递的话,会降低可读性。比如下面的例子,如果不去看函数定义、文档或内部代码,很难知道布尔值的意义。

// 错误示例
// 实际是创建单次任务
Task(true);
// 循环任务
Task(false);
// 呃,看不懂这是啥?
ListBox(false, true, true);
// 禁用状态的 button
Button(false);

这种情况,对于构造函数,可以使用命名构造函数或命名参数的形式,语义上会清晰很多。下面的代码,即便没有注释也能够看得懂。

// 正确示例
Task.oneShot();
Task.repeating();
ListBox(scroll: true, showScrollbars: true);
Button(ButtonState.enabled);

当然,对于 setter 而言,因为有上下文,使用布尔值是没问题的。

// 正确示例
listBox.canScroll = true;
button.isEnabled = false;

规范2:对于可选占位参数,按频次设置合理的次序

可选占位参数意味着可以不传,如果不怎么用的参数放在前面的话,会导致如果需要传后面的参数就必须给该参数赋值,而如果没有值就得传 null,这样的代码会很难看。

// 错误示例
void someFunction(int a, [int? b, int? c, int d = 0]) {
  //...
}

// 调用示例
someFunction(1, null, null, 12);

上面的例子中,d 可能是经常要传的参数,结果放到了最后,导致调用者很郁闷,每次传 d 的时候都得给 b 和 c 传 null。正确的示例如下:

// 正确示例
void someFunction(int a, [int d = 0, int? b, int? c]) {
  //...
}

// 调用示例
someFunction(1, 12);

当然,这种情况,使用命名参数更合理。

// 正确示例
void someFunction(int a, {int d = 0, int? b, int? c}) {
  //...
}

// 调用示例
someFunction(1, d: 12);

规范3:避免要求传一些无意义的参数

如果我们需要省略参数传递,那么应该就直接省略,而不是传没有意义的参数,例如 null、空字符串等等。省略参数看起来更简洁,也能够避免空值导致的错误 —— 这会让函数以为传入了一个有意义的参数。

// 正确示例
var rest = string.substring(start);

// 错误示例
var rest = string.substring(start, null);

规范4:对于范围参数,遵循起始参数是闭区间,结束参数应该是开区间的约定

我们在获取范围值的时候,会需要传入起止位置,通常是整数下标值,一个起始下标表示从第几个元素开始,一个结束下标表示到哪里为止。由于这类参数通常是占位参数而非命名参数,因此建议是和 Dart的核心库的用法保持一致 —— 起始位置包括在内,而结束位置不包括在内,用数学区间表示就是 [s, e)形式。下面是 Dart 核心库的示例:

// 正确示例
[0, 1, 2, 3].sublist(1, 3) // [1, 2]
'abcd'.substring(1, 3) // 'bc

规范5:对于可选参数,尽量设置默认值

可选参数如果没有默认值,在不传递的时候,会默认为 null。如果不小心使用的话,可能会导致出现 bug。因此,假设编码的时候知道默认状态,那么请尽量设置默认值,例如下面的 DateTimeDuration 的例子:

//正确示例
DateTime(int year,
    [int month = 1,
    int day = 1,
    int hour = 0,
    int minute = 0,
    int second = 0,
    int millisecond = 0,
    int microsecond = 0]);

Duration(
    {int days = 0,
    int hours = 0,
    int minutes = 0,
    int seconds = 0,
    int milliseconds = 0,
    int microseconds = 0});

总结

本篇介绍了 Dart 的函数参数传递形式以及5条规范。个人来说,更倾向于使用命名参数,因为在调用函数的时候会明确参数语义 —— 尤其是参数很多的时候。此外,建议明确参数是否是 required,从而约束调用者的参数传递行为。对于知道默认值的可选参数 ,推荐设置默认值。这样我们可以省略很多 null 判断。

相关文章

  • Dart 的函数参数怎么传更合理?

    前言 函数是我们用得最多的了,通过函数可以将复杂业务拆分为简短精悍的函数,从而分解大业务,提高可维护性和代码复用性...

  • Dart语法(下)

    前文链接: Dart简介 Dart语法(上) 内容: 函数(方法)函数定义及各类函数;函数参数;闭包 面向对象定义...

  • python函数

    python函数 什么是函数 函数语法 调用函数 函数参数形参与实参传参指定参数传参参数默认值可变参数参数解包参数...

  • Dart独特的使用方式(session one)

    1.位置可选参数 Dart中的函数可以包含可选参数。可选参数是函数调用者可以选择不提供的参数。可以在函数内检查可选...

  • python 并行demo

    pool.map()函数,有两个参数可以传,第一个参数传的是函数,第二个参数传的是数据列表。那么怎么在第二个数据列...

  • Python函数参数

    参数类型 必选参数:在给函数传参数时,按照顺序,依次传值。 默认参数:就是在写函数的时候直接给参数传默认的值,调用...

  • 函数(参数,变量),类

    一:函数 1、无参数函数 有参数函数 是有参函数,在调用的时候没有传参如下 2、缺省函数:不传参数,显示默认赋值,...

  • 2.Dart面向对象

    函数 命名可选参数{} 位置可选参数[] 作用域定义:与js的作用域一致,层层向上找。 Dart函数闭包 js函数...

  • Dart基础: 构造函数

    Dart基础: 构造函数 Dart的构造函数比较变化多端, 我们可以在不同的场景下合理声明和调用一个class的构...

  • 函数的定义及参数详解

    如何去定义一个函数: 位置参数:(不传就报错) 带默认参数的函数:(位置参数必传,默认参数不传取默认值) 可变参数...

网友评论

    本文标题:Dart 的函数参数怎么传更合理?

    本文链接:https://www.haomeiwen.com/subject/blemurtx.html