目录

宏(实验性)

Dart 宏系统 是一个主要的新的语言特性,* 目前正在开发中 *,它为 Dart 语言增加了对 静态元编程 的支持。

Dart 宏是用户可定义的一段代码,它接收其他代码作为参数,并实时操作它以创建、修改或添加声明。

您可以将宏系统分成两部分:使用宏和编写宏。此页面(处于较高层次,因为* 此特性仍处于预览阶段 *)在以下部分中涵盖了每一部分:

  • JsonCodable :一个现成的宏,您可以立即尝试(在实验性标志后面),它为 Dart 中冗长的 JSON 序列化和反序列化这一常见问题提供了一个无缝的解决方案。

  • 宏特性概述 :我们为什么将宏添加到 Dart 中,相关的用例,与现有代码生成解决方案相比的优势,以及在该特性完成后编写宏的未来工作方式的粗略概述。

JsonCodable

#

JsonCodable 宏将用户定义的 Dart 类编码和解码为 Map<String, Object?> 类型的 JSON 映射。它生成两个成员,一个 toJson 序列化方法和一个 fromJson 反序列化构造函数。

设置实验

#
  1. 切换到 Dart dev 渠道Flutter master 渠道

  2. 运行 dart --version 并确保您拥有 Dart 版本 3.5.0-152 或更高版本。

  3. 编辑 pubspec 中的 SDK 约束 以要求 Dart 版本: sdk: ^3.5.0-152

  4. 添加包 jsondependenciesdart pub add json

  5. 在项目根目录的 analysis_options.yaml 文件中 启用实验

    yaml
    analyzer:
     enable-experiment:
       - macros
  6. 在您计划使用它的文件中导入包:

    dart
    import 'package:json/json.dart';
  7. 使用实验性标志运行您的项目:

    dart run --enable-experiment=macros bin/my_app.dart

使用宏

#

要使用 JsonCodable 宏,请将注解附加到您要序列化的类:

dart
import 'package:json/json.dart';

@JsonCodable() // 宏注解。
class User {
 final int? age;
 final String name;
 final String username;
}

宏会检查 User 类并使用 User 类的字段导出 fromJsontoJson 的实现。

因此,无需自己定义它们, toJsonfromJson 现在可用于带注解类的对象:

dart
void main() {
 // 给定一些任意的 JSON:
 var userJson = {
   'age': 5,
   'name': 'Roger',
   'username': 'roger1337'
 };

 // 使用生成的成员:
 var user = User.fromJson(userJson);
 print(user);
 print(user.toJson());
}

查看生成的代码

#

有时查看生成的代码以更好地了解宏的工作原理或检查其提供的细节可能很有用。

单击 IDE 中注解下出现的“ 转到增强 ”链接(在 VSCode 中受支持)以查看宏如何生成 toJsonfromJson

如果您更改了带注解的类中的任何内容,您可以观察生成的增强与您的应用程序代码一起实时调整:

生成的增强随着其增强代码的更新而更新的并排 gif 图像

触发自定义诊断

#

JsonCodable 宏具有内置的诊断信息,这些诊断信息像语言本身的诊断信息一样发出。例如,如果您尝试手动声明应用了宏的 toJson 方法,分析器将发出错误:

dart
@JsonCodable()
class HasToJson {
 void toJson() {}
 // 错误:由于此现有方法,无法生成 toJson 方法。
}

您可以在 JsonCodable 的定义 中搜索“ DiagnosticMessage ”以查找宏将抛出的其他错误。例如,扩展未进行序列化的类,或者字段名称与给定 JSON 中的键名不完全匹配。

宏语言特性

#

Dart 宏是一种 静态 元编程或代码生成解决方案。与 运行时 代码生成解决方案(如 build_runner )不同,宏完全集成到 Dart 语言中,并由 Dart 工具在后台自动执行。这使得宏比依赖于辅助工具效率更高:

  • 无需额外运行 ;宏在您编写代码时实时构建。
  • 没有重复的工作 或持续的重新编译会影响性能;所有构建和代码生成都直接在编译器中自动进行。
  • 不写入磁盘 ,因此没有部分文件或指向生成的引用的指针;宏直接增强 现有 类。
  • 没有令人困惑/模糊的测试 ;自定义诊断信息像分析器发出的任何其他消息一样,直接在 IDE 中发出。

并且效率也更高,并且比手动编写这些类型问题的解决方案的错误率也低得多。

用例

#

宏提供了可重用的机制来解决以冗长的样板代码为特征的模式,并且通常需要迭代类的字段。我们希望将来使用宏来解决的一些常见示例包括:

  • JSON 序列化。 序列化 JSON 所需的额外工具,例如 json_serializable 包,效率并不像它应该的那样高。 JsonCodable 宏提供了一种更清晰的方法来生成序列化代码; 立即尝试

  • 数据类。 Dart 中 最受请求的 功能是用于数据类,这些数据类会自动提供构造函数以及每个字段的 ==hashCodecopyWith() 方法的实现。使用宏实现该解决方案意味着用户可以根据需要自定义其数据类。

  • 冗长的 Flutter 模式。 一个例子是将复杂的 build 方法分解成较小的 widget 类的集合。这对于性能更好,并使代码更易于维护。不幸的是,编写所有这些较小的类需要大量的样板代码,这会阻止用户。宏可能会提供一个解决方案,该解决方案会迭代复杂的 build 方法以生成较小的 widget 类,从而极大地提高 Flutter 代码的生产力和质量。您可以查看 Flutter 团队在此 提案 中对该主题的探讨。

宏的工作原理

#

要创建宏,您可以使用 macro 关键字编写类似于类的宏声明。宏声明还必须包含 implements 子句以定义宏可以应用于哪个接口。

例如,适用于类并将新声明添加到类的宏将实现 ClassDeclarationsMacro 接口:

dart
macro class MyMacro implements ClassDeclarationsMacro {
   const MyMacro();

   // ...
}

虽然该特性仍在开发中,但您可以在 源代码 中找到宏接口的完整列表。

上述示例中的 MyMacro 构造函数对应于您将用于将宏应用于声明的注解。语法与 Dart 现有的元数据注解语法相同:

dart
@MyMacro()
class A {}

在宏声明的主体中,您可以定义您希望宏 生成 的代码,以及您希望宏发出的任何 诊断信息

在非常高的层次上,编写宏本质上是通过使用构建器方法将声明的 属性 与这些属性上的 标识符 组合在一起。宏通过对程序的深入 自省 来收集此信息。

宏仍在开发中,因此目前只能介绍这么多细节。如果您好奇,或者想在实验性标志后面自己尝试,最好的指导是查看现有宏的实现:

时间表

#

宏的稳定发布日期目前未知。这是由于其实现的复杂性。

宏的工作原理是深入 自省 应用它们的程序。宏最终可能会遍历程序的远处部分,以收集其正在增强的声明的属性和类型注释的必要信息。

考虑到它们在大代码库中的应用,在这些库中,多个宏可以在不同位置连续自省和增强基础代码, 执行顺序阶段 的设计尤其具有挑战性,需要仔细考虑。

我们计划在今年(2024 年)晚些时候发布 JsonCodable 宏的稳定版本,并在明年(2025 年)初发布完整语言特性的稳定版本(即编写您自己的宏)。