跳转到正文
YGJoon 技术博客

Dart 函数和方法详解

发布于:
·1.3k 字 · 约 3 分钟·作者:
从基本语法到必需、可选位置、可选命名、默认值四种参数写法,再到函数作为一等公民的赋值与传递,打牢函数基本功。
Dart 函数和方法详解文章封面
目录

函数定义

基本语法

返回类型 函数名(参数列表) {
  函数体
  return 返回值;
}

简单示例

int add(int a, int b) {
  return a + b;
}

// 箭头语法(单行函数)
int multiply(int a, int b) => a * b;

参数类型

必需参数

String getFullName(String firstName, String lastName) {
  return '$firstName $lastName';
}

可选位置参数

String sayHello(String name, [String? title]) {
  if (title != null) {
    return 'Hello, $title $name';
  }
  return 'Hello, $name';
}

可选命名参数

void printPersonInfo({
  required String name,
  int? age,
  String? address,
}) {
  print('Name: $name');
  if (age != null) print('Age: $age');
  if (address != null) print('Address: $address');
}

默认参数值

void greet(String name, {String greeting = 'Hello'}) {
  print('$greeting, $name!');
}

函数作为一等公民

函数赋值给变量

var sayHi = (String name) => 'Hi, $name!';
Function multiply = (int a, int b) => a * b;

函数作为参数

void executeFunction(Function callback) {
  callback();
}

// 使用示例
executeFunction(() => print('Hello'));

函数作为返回值

Function makeAdder(int addBy) {
  return (int i) => i + addBy;
}

// 使用示例
var add2 = makeAdder(2);
print(add2(3)); // 输出: 5

匿名函数

var list = ['apple', 'banana', 'orange'];

// 使用匿名函数
list.forEach((item) {
  print(item);
});

// 箭头语法
list.forEach((item) => print(item));

闭包

Function counter() {
  int count = 0;
  return () {
    count++;
    return count;
  };
}

// 使用示例
var increment = counter();
print(increment()); // 1
print(increment()); // 2

异步函数

Future

Future<String> fetchUserData() async {
  // 模拟网络请求
  await Future.delayed(Duration(seconds: 2));
  return 'User Data';
}

Stream

Stream<int> countStream(int max) async* {
  for (int i = 1; i <= max; i++) {
    await Future.delayed(Duration(seconds: 1));
    yield i;
  }
}

生成器函数

同步生成器

Iterable<int> naturalsTo(int n) sync* {
  int k = 0;
  while (k < n) yield k++;
}

异步生成器

Stream<int> asynchronousNaturalsTo(int n) async* {
  int k = 0;
  while (k < n) {
    await Future.delayed(Duration(seconds: 1));
    yield k++;
  }
}

完整示例

class Calculator {
  // 基本运算方法
  int add(int a, int b) => a + b;
  int subtract(int a, int b) => a - b;
  int multiply(int a, int b) => a * b;
  double divide(int a, int b) => a / b;

  // 可选参数示例
  double calculateArea({
    required double length,
    double? width,
  }) {
    if (width == null) {
      // 计算正方形面积
      return length * length;
    }
    // 计算矩形面积
    return length * width;
  }

  // 函数作为参数
  void executeOperation(int a, int b, Function(int, int) operation) {
    print('Result: ${operation(a, b)}');
  }

  // 异步方法示例
  Future<List<int>> generateNumbers(int count) async {
    List<int> numbers = [];
    for (int i = 0; i < count; i++) {
      await Future.delayed(Duration(milliseconds: 100));
      numbers.add(i);
    }
    return numbers;
  }
}

void main() async {
  var calc = Calculator();
  
  // 基本方法调用
  print('Addition: ${calc.add(5, 3)}');
  print('Subtraction: ${calc.subtract(5, 3)}');
  
  // 可选参数
  print('Square area: ${calc.calculateArea(length: 5)}');
  print('Rectangle area: ${calc.calculateArea(length: 5, width: 3)}');
  
  // 函数作为参数
  calc.executeOperation(5, 3, (a, b) => a * b);
  
  // 异步方法
  var numbers = await calc.generateNumbers(5);
  print('Generated numbers: $numbers');
}

注意事项

  1. 优先使用命名参数提高代码可读性
  2. 合理使用可选参数和默认值
  3. 注意异步函数的使用和错误处理
  4. 避免过度使用闭包导致内存泄漏
  5. 合理组织函数的长度和复杂度

最佳实践

  1. 函数命名使用动词开头
  2. 一个函数只做一件事
  3. 参数数量不要太多
  4. 适当添加函数文档注释
  5. 保持函数的纯粹性

实战案例:一个 143 行工具里的方法设计

我在 ygking_json_to_dart_model 里手写过一个 JSON 转 Dart 工具(lib/services/json_converter.dart,143 行),它的方法拆分是我刻意设计的:

String convertJsonToDart(String jsonStr, String className) { ... }  // 唯一的公开入口

void _generateClass(Map<String, dynamic> map, String className,
    StringBuffer buffer, String parentClassName) { ... }           // 私有,下划线开头

String _getFieldType(dynamic value, String key) { ... }            // 私有类型推导

三个决定都是有原因的:

  1. 只暴露一个公开方法。convertJsonToDart 是唯一入口,_generateClass 这些以下划线开头设为库私有——调用方不需要知道我是递归生成的还是字符串模板拼的。Dart 的”私有”就是以下划线开头,没有 private 关键字,新手常在这儿愣一下。
  2. StringBuffer 当参数传进去,而不是让方法返回 String。Dart 里对象是按引用传递的,_generateClass 直接往同一个 buffer 里 writeln,递归多少层都不产生中间字符串。如果写成”每个方法返回一段 String 再拼接”,嵌套深的 JSON 能拼出几百个临时对象。
  3. 内部方法全用位置参数,不用命名参数。_generateClass 有 4 个参数,按理说该用命名参数提高可读性——但它只在一个地方被调用(递归调用自己),调用方就是我自己,位置参数更短。命名参数是给”别人调用的 API”准备的,内部递归用位置参数是务实的选择。

总结

Dart 的函数系统非常灵活,支持多种参数类型和函数特性。合理使用这些特性可以写出更清晰、更易维护的代码。把参数写法和闭包用熟,写 Flutter 回调和工具函数时会顺手很多。


分享这篇文章:


相关文章

上一篇
Dart 变量和数据类型详解
下一篇
Dart 类和对象详解