本文由 简悦SimpRead 转码,原文地址 www.sandromaglione.com
dart和flutter中的静态元编程即将到来。了解宏原型和如何使用......
Dart和Flutter中的静态元编程:macro_prototype概述
Dart中的宏已经在2021年3月的Flutter Engage上公布。Dart团队开始探索Dart的静态元编程。元编程是 "产生其他代码的代码"。这个功能可以使语言更加强大,允许开发者在语言本身定义新的语法。
事实上,在 2021年1月7日,Github上已经创建了 macro_prototype 存储库。这个项目代表了一个可能的基于build_runner的宏系统。在这篇文章中,我们将探索这个宏的原型,并了解宏将(可能)在dart中如何工作。
宏和元编程仍在讨论中!
在我写这篇文章时,宏和元编程仍在讨论中。最终的版本可能与目前的实现完全不同。
静态元编程
dart团队对静态元编程的定义如下。
元编程指的是对其他代码进行操作的代码,就好像它是数据一样。它可以接受代码作为参数,反映它,检查它,创建它,修改它,并返回它。
静态元编程 指的是在编译时进行这项工作,并且通常在这项工作的基础上修改或添加到程序中。
静态元编程将允许开发者通过在静态时间添加任何种类的代码来增强一个类、一个方法或一个类型的特性。
dart团队在Flutter Engage上介绍了元编程。他们介绍了在dart中引入静态元编程的好处和理由。
静态元编程在Flutter Engage上正式引入,2021年3月。
从那时起,dart社区内部开始了长时间的讨论,以定义dart中元编程的规范。静态元编程是仍在Github上由社区讨论。
macro_prototype
dart团队也开始了宏原型的工作,以测试dart中最终宏实现的一些可能功能。他们使用build_runner来实现宏定义的构建步骤。
我们将在这篇文章中了解这个原型是如何工作的。如果你有兴趣或者你想提出任何新的(有趣的)想法,请在 Github上的静态元编程问题 上留下评论。
安装
由于macro_prototype是在Github上托管的,你需要从那里直接导入软件包。在你的pubspec.yaml文件中加入以下内容。
.yaml
dev_dependencies:
macro_builder:
git:
url: git://github.com/jakemac53/macro_prototype.git
ref: main
build_runner: ^2.1.0
宏定义
在dart中,宏由一个类组成,它可以检查另一个类(或方法,或类型)的代码,并在其内部插入新的代码。
为了设置构建工具来生成宏的代码,我们需要在项目的根部创建一个build.yaml文件。build.yaml文件需要有以下内容。
.yaml
targets:
$default:
builders:
# Change 'prototype' with the name of your project!
prototype:prototype:
enabled: true
builders:
# Change 'prototype' with the name of your project!
prototype:
# Update path below to match the location of your builders.dart file
import: "lib/builders.dart"
builder_factories:
["typesBuilder", "declarationsBuilder", "definitionsBuilder"]
build_extensions:
{ ".gen.dart": [".types.dart.", ".declarations.dart.", ".dart"] }
auto_apply: dependents
build_to: source
然后,在lib文件夹内,创建一个builders.dart文件,内容如下。
import 'package:macro_builder/builder.dart';
import 'package:build/build.dart';
Builder typesBuilder(_) => TypesMacroBuilder([]);
Builder declarationsBuilder(_) => DeclarationsMacroBuilder([]);
Builder definitionsBuilder(_) => DefinitionsMacroBuilder([]);
上述设置允许构建工具了解哪些类需要生成,哪些宏已经在项目中定义。
定义宏
使用 "macro_prototype "来定义宏有多种方法。我建议你 阅读关于macro_prototype包的文档,以了解更多关于dart中宏如何工作的细节。
总之,在宏的生成过程中,我们有三个不同的阶段。
- 类型宏:为应用程序引入全新的类("ClassTypeMacro","FieldTypeMacro",或 "MethodTypeMacro")。
- 声明宏:为类和当前库引入新的公共声明,但不是新的类型(
ClassDeclarationMacro,FieldDeclarationMacro, 或MethodDeclarationMacro)。 - 定义宏:允许实现现有的声明。在这个阶段不能添加新的声明(
ClassDefinitionMacro,FieldDefinitionMacro, 或MethodDefinitionMacro)。
定义一个宏包括创建一个新的类,实现上面介绍的一个或多个宏类。
import 'package:macro_builder/definition.dart';
const exampleClass = _ExampleClass();
class _ExampleClass implements ClassDeclarationMacro {
const _ExampleClass();
@override
void visitClassDeclaration(ClassDeclaration declaration, ClassDeclarationBuilder builder) {
// Implementation!
}
}
创建Macro类
代码的生成是在visitClassDeclaration函数里面管理的。基于你实现的特定宏阶段,你将可以访问不同的输入,用于检查应用宏的类的源代码。
在这个例子中,我们可以访问两个输入。
- ClassDeclaration:它允许检查源类中的方法和属性。
- ClassDeclarationBuilder:它允许输出将被添加到注释类中的代码。
你所需要做的就是调用builder.addToClass来在被注释的类中插入任何定义。下面我报告了一个例子,它在类中创建了一个copyWith方法。
import 'package:macro_builder/definition.dart';
const copyWith = _CopyWith();
class _CopyWith implements ClassDeclarationMacro {
const _CopyWith();
@override
void visitClassDeclaration(
ClassDeclaration declaration,
ClassDeclarationBuilder builder,
) {
Code code = Fragment.fromParts([
Fragment('${declaration.name} copyWith({'),
...declaration.fields.map((e) => Fragment('${e.type.name}? ${e.name},')),
Fragment('}) => ${declaration.name}('),
...declaration.fields
.map((e) => Fragment('${e.name}: ${e.name} ?? this.${e.name},')),
Fragment(');')
]);
builder.addToClass(Declaration('$code'));
}
}
将宏纳入构建中
实际激活宏的最后一步是将其定义添加到builders.dart文件中。注意根据你实现的类型,将宏包含在正确的builder中。
import 'package:macro_builder/builder.dart';
import 'package:build/build.dart';
import 'macros/copy_with.dart';
Builder typesBuilder(_) => TypesMacroBuilder([]);
Builder declarationsBuilder(_) => DeclarationsMacroBuilder([copyWith]);
Builder definitionsBuilder(_) => DefinitionsMacroBuilder([]);
类注释
dart团队决定(至少在这个原型中)使用注释来将宏应用到类中。你需要创建一个使用*.gen.dart扩展名的文件。构建步骤将检查所有的文件,并只将宏应用于后缀为gen的文件。
让我们创建一个union.gen.dart文件,并在其中添加以下代码。
import 'macros/copy_with.dart';
@copyWith
class Union {
final int part1;
final String part2;
const Union({required this.part1, required this.part2});
}
然后启动构建命令来生成代码。
flutter pub run build_runner build
该软件包将生成三个文件。
union.dart: 在你的项目中使用的主要文件。union.declarations.dart:在你的项目中使用的主文件。union.types.dart:在项目中使用的主要文件。
union.dart
// GENERATED FILE - DO NOT EDIT
//
// This file was generated by applying the following macros to the
// `example/union.gen.dart` file:
//
// - Instance of '_ToUnion'
//
// To make changes you should edit the `example/union.gen.dart` file;
import 'macros/copy_with.dart';
@copyWith
class Union {
final int part1;
final String part2;
const Union({required this.part1, required this.part2});
}
union.declarations.dart
// GENERATED FILE - DO NOT EDIT
//
// This file was generated by applying the following macros to the
// `example/union.types.dart` file:
//
// - Instance of '_ExampleClass'
// - Instance of '_Adt'
// - Instance of '_CopyWith'
//
// To make changes you should edit the `example/union.gen.dart` file;
import 'macros/copy_with.dart';
@copyWith
class Union {
Union copyWith({
int? part1,
String? part2,
}) =>
Union(
part1: part1 ?? this.part1,
part2: part2 ?? this.part2,
);
final int part1;
final String part2;
const Union({required this.part1, required this.part2});
}
union.types.dart
// GENERATED FILE - DO NOT EDIT
//
// This file was generated by applying the following macros to the
// `example/union.declarations.dart` file:
//
//
// To make changes you should edit the `example/union.gen.dart` file;
import 'macros/copy_with.dart';
@copyWith
class Union {
final int part1;
final String part2;
Union copyWith({int? part1, String? part2}) =>
Union(part1: part1 ?? this.part1, part2: part2 ?? this.part2);
const Union({required this.part1, required this.part2});
}
注意这个宏是如何在生成的类中添加copyWith方法的。这就是宏的力量!
你现在对dart中的宏和静态元编程有了一些了解。宏可能是dart和flutter的一大进步,我已经迫不及待地想看到未来的发展了!
如果你从文章中学到了新的东西,请在Twitter上关注我@SandroMaglione,并在下面这里订阅我的通讯,以获得更多的更新和很酷的新闻和教程👇。
谢谢你的阅读。