Dart - 语法糖(持续更新)

文章目录

  • 前言
  • 开发环境
  • 中间表示
  • 语法糖
    • 1. 操作符/运算符(?./??/??=/../?../.../...?)
    • 2. 循环(for-in)
    • 3. 函数/方法(=>)
    • 4. 关键字(await for)
  • 最后


前言

通过将dill文件序列化为可读文本查看Dart语法糖的中间表示(IR),并尝试反推大致的等价源码,便于进一步理解和使用这些语法糖。

开发环境

  • macOS: 13.4
  • Dart: 3.0.5

中间表示

中间表示序列化文本的本质是解析抽象语法树(AST)的各个节点并打印拼接成文本,节点打印以及内容拼接格式请参考ast_to_text.dart。以下列出一些常见的格式方便后面阅读理解中间表示序列化为文本后的内容:

注意,这些不是官方说明,只是通过阅读和调试源码得到的个人经验总结,仅供参考。如果需要调试源码,请参考这篇文章Dart - dill文件序列化为可读文本。

  • ::

用于指定库或类的成员。例如core::String表示dart.core库中的String类型,Dart常见类型都属于这个核心库,详见core.dart。

如果你遇到奇怪的库名称,那一般是因为成员找不到所属的具体库,比如你自己自定义的类或者顶级函数等,这时的库名称是成员所在dart文件的文件名(有所缩减)。例如下文中经常出现的syn,刚开始我也以为是Dart中的某个库,结果搜遍了Dart SDK都没找到定义的源码,没办法直接开启调试,结果原来是文件名缩写。

screenshot1

关键源码(位于ast_to_text.dart):

class NameSystem {...String nameCanonicalNameAsLibraryPrefix(Reference? node, CanonicalName? name,{String? proposedName}) {return prefixes.disambiguate(node, name, () {if (proposedName != null) return proposedName;CanonicalName? canonicalName = name ?? node?.canonicalName;if (canonicalName?.name != null) {String path = canonicalName!.name;int slash = path.lastIndexOf(pathSeparator);if (slash >= 0) {path = path.substring(slash + 1);}if (path.endsWith('.dart')) {path = path.substring(0, path.length - '.dart'.length);}return abbreviateName(path);}return 'L';});}final RegExp punctuation = new RegExp('[.:]');String abbreviateName(String name) {int dot = name.lastIndexOf(punctuation);if (dot != -1) {name = name.substring(dot + 1);}if (name.length > 4) {return name.substring(0, 3);}return name;}
}

文件名为syntactic_sugar.dart时,会先截掉文件扩展名,然后截取前三个字符,得到syn。如果文件名是aa.bb.dart,会得到bb

  • let表达式

let表达式通过定义一个局部变量避免一些重复计算,一般长这样(来源Let节点的文档注释):

let v = x in y

let表达式的返回值取决于in子句中表达式y的值。举个下文中的例子🌰:

let final core::String? #t1 = syn::a in #t1 == null ?{core::int?} null : #t1{core::String}.{core::String::length}{core::int}
  • vfinal core::String? #t1
  • xsyn::a
  • y#t1 == null ?{core::int?} null : #t1{core::String}.{core::String::length}{core::int}

#t1是一个局部变量,命名规则是#t加上索引值,参考NameSystem类。

  • @vm.call-site-attributes.metadata=receiverType:library

这是由Dart VM使用的一种特殊的元数据,用于存储调用接收者的静态类型,以便优化后端编译。以上是个人理解,可能有偏差。其实这个不了解也不太影响阅读序列化的文本。

语法糖

1. 操作符/运算符(?./??/??=/…/?../…/…?)

  • ?.(条件访问成员)

使用示例:

String? a;
var b = a?.length;

中间表示:

static field core::String? a;
static field core::int? b = let final core::String? #t1 = syn::a in #t1 == null ?{core::int?} null : #t1{core::String}.{core::String::length}{core::int};

?.语法糖通过条件表达式(条件 ? 表达式 1 : 表达式 2)做了一次判空操作,当对象为空时返回null,不为空时继续访问成员。

  • ??(空感知运算符)

使用示例:

String? a;
var b = a ?? 'c';

中间表示:

static field core::String? a;
static field core::String b = let final core::String? #t1 = syn::a in #t1 == null ?{core::String} "c" : #t1{core::String};

??语法糖和?.语法糖类似,也是通过条件表达式做了一次判空操作,当对象为空时返回提供的默认值,不为空时返回对象。

  • ??=(空感知赋值运算符)

使用示例:

void test() {String? a;// 作用域有限制,需要在方法中使用a ??= 'b';// 大致等价源码a == null ? a = 'b' : null;
}

中间表示:

static method test() → void {core::String? a;a == null ?{core::String} a = "b" : null;a{core::String} == null ?{core::String?} a = "b" : null;
}

??=语法糖通过判断对象是否为空决定是否要给对象赋值。

  • ..(级联运算符)

使用示例:

void test() {List<String> l = [];var l1 = l..add('a')..[0] = 'b'..length;// 大致等价源码l.add('a');l[0] = 'b';l.length;var l2 = l;
}

中间表示:

static method test() → void {core::List<core::String> l = core::_GrowableList::•<core::String>(0);core::List<core::String> l1 = let final core::List<core::String> #t1 = l in block {[@vm.call-site-attributes.metadata=receiverType:library dart:core::List<library dart:core::String>] #t1.{core::List::add}("a"){(core::String) → void};[@vm.call-site-attributes.metadata=receiverType:library dart:core::List<library dart:core::String>] #t1.{core::List::[]=}(0, "b"){(core::int, core::String) → void};#t1.{core::List::length}{core::int};} =>#t1;[@vm.call-site-attributes.metadata=receiverType:library dart:core::List<library dart:core::String>] l.{core::List::add}("a"){(core::String) → void};[@vm.call-site-attributes.metadata=receiverType:library dart:core::List<library dart:core::String>] l.{core::List::[]=}(0, "b"){(core::int, core::String) → void};l.{core::List::length}{core::int};core::List<core::String> l2 = l;
}

..语法糖通过let表达式创建一个临时变量并赋值为,然后在代码块中依次调用方法,最后返回临时变量。

  • ?..(空感知级联运算符)

使用示例:

void test() {List<String>? l;var l1 = l?..add('a')..[0] = 'b'..length;
}

中间表示:

static method test() → void {core::List<core::String>? l;core::List<core::String>? l1 = let final core::List<core::String>? #t1 = l in #t1 == null ?{core::List<core::String>?} null : block {[@vm.call-site-attributes.metadata=receiverType:library dart:core::List<library dart:core::String>] #t1{core::List<core::String>}.{core::List::add}("a"){(core::String) → void};[@vm.call-site-attributes.metadata=receiverType:library dart:core::List<library dart:core::String>] #t1{core::List<core::String>}.{core::List::[]=}(0, "b"){(core::int, core::String) → void};#t1{core::List<core::String>}.{core::List::length}{core::int};} =>#t1;
}

?..语法糖和..语法糖相比,主要区别在于执行代码块之前通过条件表达式做了一次判空操作。

  • ...(扩展操作符)

使用示例:

void test() {List<String> l = ['a', 'b'];var l1 = [...l, 'c'];// 大致等价源码var l2 = List.of(l);l2.add('c');
}

中间表示:

static method test() → void {core::List<core::String> l = core::_GrowableList::_literal2<core::String>("a", "b");core::List<core::String> l1 = block {final core::List<core::String> #t1 = core::List::of<core::String>(l);[@vm.call-site-attributes.metadata=receiverType:library dart:core::List<library dart:core::String>] #t1.{core::List::add}{Invariant}("c"){(core::String) → void};} =>#t1;core::List<core::String> l2 = core::List::of<core::String>(l);[@vm.call-site-attributes.metadata=receiverType:library dart:core::List<library dart:core::String>] l2.{core::List::add}("c"){(core::String) → void};
}

...语法糖通过List.of方法从已有数组中创建一个新的数组,然后调用add方法添加其他元素。如果语法糖用于Map,和用于数组时所做的操作类似。

  • ...?(空感知扩展操作符)

使用示例:

void test() {List<String>? l;var l1 = [...?l, 'c'];
}

中间表示:

static method test() → void {core::List<core::String>? l;core::List<core::String> l1 = block {final core::List<core::String> #t1 = core::_GrowableList::•<core::String>(0);final core::Iterable<core::String>? #t2 = l;if(!(#t2 == null))[@vm.call-site-attributes.metadata=receiverType:library dart:core::List<library dart:core::String>] #t1.{core::List::addAll}{Invariant}(#t2{core::Iterable<core::String>}){(core::Iterable<core::String>) → void};[@vm.call-site-attributes.metadata=receiverType:library dart:core::List<library dart:core::String>] #t1.{core::List::add}{Invariant}("c"){(core::String) → void};} =>#t1;
}

...?语法糖和...语法糖相比,主要区别在于增加了临时变量用于判断已有数组是否为空,如果为空则不处理已有数组,不为空则通过addAll方法添加已有数组。

2. 循环(for-in)

  • for-in

使用示例:

void test() {List<String> params = [];for (var param in params) {}// 大致等价源码Iterator<String> iterator = params.iterator;for (; iterator.moveNext();) {String param = iterator.current;}
}

中间表示:

static method test() → void {core::List<core::String> params = core::_GrowableList::•<core::String>(0);{synthesized core::Iterator<core::String> :sync-for-iterator = params.{core::Iterable::iterator}{core::Iterator<core::String>};for (; :sync-for-iterator.{core::Iterator::moveNext}(){() → core::bool}; ) {core::String param = :sync-for-iterator.{core::Iterator::current}{core::String};{}}}core::Iterator<core::String> iterator = params.{core::Iterable::iterator}{core::Iterator<core::String>};for (; iterator.{core::Iterator::moveNext}(){() → core::bool}; ) {core::String param = iterator.{core::Iterator::current}{core::String};}
}

for-in语法糖是由标准的for循环Iterator实现的。每次循环调用moveNext方法会迭代到下一个元素,通过current方法获取当前迭代的元素。如果已经迭代结束,moveNext方法会返回false结束循环。

3. 函数/方法(=>)

  • =>(箭头函数)

使用示例:

bool test() => true;
// 大致等价源码
bool test1() {return true;
}

中间表示:

static method test() → core::boolreturn true;
static method test1() → core::bool {return true;
}

=>语法糖补全了return,同时因为是单行函数,所以不加{}

4. 关键字(await for)

  • await for

使用示例:

void test() async {var server = await HttpServer.bind(InternetAddress.anyIPv4,8080,);await for (HttpRequest request in server) {}// 大致等价源码var stream = server;StreamIterator<HttpRequest>? iterator = StreamIterator<HttpRequest>(stream);try {while (await iterator.moveNext()) {var request = iterator.current;}} finally {if (iterator != null) {await iterator.cancel();}}
}

中间表示:

static method test() → void async /* futureValueType= void */ {_ht::HttpServer server = await _ht::HttpServer::bind(io::InternetAddress::anyIPv4, 8080);{synthesized _ht::HttpServer :stream = server;synthesized asy::_StreamIterator<_ht::HttpRequest>? :for-iterator = new asy::_StreamIterator::•<_ht::HttpRequest>(:stream);trywhile (let dynamic #t1 = asy::_asyncStarMoveNextHelper(:stream) in await :for-iterator.{asy::_StreamIterator::moveNext}(){() → asy::Future<core::bool>}) {_ht::HttpRequest request = :for-iterator.{asy::_StreamIterator::current}{_ht::HttpRequest};{}}finallyif(!(:for-iterator.{asy::_StreamIterator::_subscription}{asy::StreamSubscription<_ht::HttpRequest>?} == null))await :for-iterator.{asy::_StreamIterator::cancel}(){() → asy::Future<dynamic>};}_ht::HttpServer stream = server;asy::StreamIterator<_ht::HttpRequest>? iterator = asy::StreamIterator::•<_ht::HttpRequest>(stream);try {while (await iterator{asy::StreamIterator<_ht::HttpRequest>}.{asy::StreamIterator::moveNext}(){() → asy::Future<core::bool>}) {_ht::HttpRequest request = iterator{asy::StreamIterator<_ht::HttpRequest>}.{asy::StreamIterator::current}{_ht::HttpRequest};}}finally {if(!(iterator{asy::StreamIterator<_ht::HttpRequest>} == null)) {await iterator{asy::StreamIterator<_ht::HttpRequest>}.{asy::StreamIterator::cancel}(){() → asy::Future<dynamic>};}}
}

await for语法糖用于处理流(Stream),在服务端开发中比较常见,以上使用示例是对HTTP请求进行处理。从中间表示可见,该语法糖先用HttpServer对象创建流迭代器StreamIterator,然后开启一个while循环迭代流的值。

这里简单讲讲流的迭代原理,如果不感兴趣可以略过。如果一个Future对象有await关键字修饰但又一直不调用complete方法,那么Future对象就会一直处于未完成的状态,程序会被阻塞无法继续往下执行。while循环中的判断语句就是利用这个实现对流的迭代,具体实现源码位于_StreamIterator类。

_StreamIterator类中的moveNext方法:

Future<bool> moveNext() {var subscription = _subscription;if (subscription != null) {if (_hasValue) {var future = new _Future<bool>();_stateData = future;_hasValue = false;subscription.resume();return future;}throw new StateError("Already waiting for next.");}return _initializeOrDone();
}

StreamIterator对象首次调用moveNext方法时,因为还未订阅流,所以会执行_initializeOrDone方法。如果已经订阅且当前有值(不是在等待下一个值)则恢复被暂停的订阅并返回一个新的Future对象,这时因为while循环中的判断语句有await关键字修饰,程序将被阻塞,直到下一个值的到来(在_onData方法中接收处理)。

_StreamIterator类中的_initializeOrDone方法:

Future<bool> _initializeOrDone() {assert(_subscription == null);var stateData = _stateData;if (stateData != null) {Stream<T> stream = stateData as dynamic;var future = new _Future<bool>();_stateData = future;// The `listen` call may invoke user code, and it might try to emit// events.// We ignore data events during `listen`, but error or done events// are used to asynchronously complete the future and set `_stateData`// to null.// This ensures that we do no other user-code callbacks during `listen`// than the `onListen` itself. If that code manages to call `moveNext`// again on this iterator, then we will get here and fail when the// `_stateData` is a future instead of a stream.var subscription = stream.listen(_onData,onError: _onError, onDone: _onDone, cancelOnError: true);if (_stateData != null) {_subscription = subscription;}return future;}return Future._falseFuture;
}

_initializeOrDone方法的作用是订阅流,监听流的各个事件,当有新的值时会调用_onData方法。返回Future对象的作用和moveNext方法一样,是为了阻塞程序。

_StreamIterator类中的_onData方法:

void _onData(T data) {// Ignore events sent during the `listen` call// (which can happen if misusing synchronous broadcast stream controllers),// or after `cancel` or `done` (for *really* misbehaving streams).if (_subscription == null) return;_Future<bool> moveNextFuture = _stateData as dynamic;_stateData = data;_hasValue = true;moveNextFuture._complete(true);if (_hasValue) _subscription?.pause();
}

_stateData变量指向的是moveNext方法或initializeOrDone方法返回的Future对象,新的值到来后_stateData变量改为指向新的值,后续可以通过current方法获取。Future对象调用complete方法结束while循环中的判断语句阻塞,同时订阅对象调用pause方法暂停订阅直到循环体执行结束再次调用moveNext方法恢复订阅,如此往复循环,只要流不关闭,可以一直迭代处理。

从以上流的迭代原理可知,如果有并发要求则不能在循环体内阻塞程序,不然会导致流的迭代被阻塞,实测确实如此,所以在处理HTTP请求时请尽量使用异步。

最后

如果这篇文章对你有所帮助,请不要吝啬你的点赞👍加星🌟,谢谢~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/10847.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HTML快速学习

目录 一、网页元素属性 1.全局属性 2.标签 2.1其他标签 2.2表单标签 2.3图像标签 2.4列表标签 2.5表格标签 2.6文本标签 二、编码 1.字符的数字表示法 2.字符的实体表示法 三、实践一下 一、网页元素属性 1.全局属性 id属性是元素在网页内的唯一标识符。 class…

【使用深度学习的城市声音分类】使用从提取音频特征(频谱图)中提取的深度学习进行声音分类研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

SpringMVC-mybatis中可以返回查询的个数,但是都为null。。。

通过postman测试请求时&#xff0c;显示查询成功&#xff0c;返回一个json数组&#xff0c;里面也有数据&#xff0c;但是数据都是null。 说明&#xff1a;确实是sql执行成功了&#xff0c;只不过是没有将sql中的字段的值给注入到对象的属性中去。。。 Select("SELECT * …

cv2抛出异常 “install libgtk2.0-dev and pkg-config, then re-run cmake or configure”

背景&#xff1a; linux中使用cv2显示图片的时候&#xff0c;运行提示异常&#xff1a; 处理方式&#xff1a; 网友的推荐操作&#xff1a; 切换至root模式安装 apt-get install libgtk2.0-dev进入OpenCV下载目录&#xff0c;重新编译 cd /home/XXX/opencv mkdir release …

项目2 | 负载均衡式在线OJ

啊我摔倒了..有没有人扶我起来学习.... &#x1f471;个人主页&#xff1a; 《 C G o d 的个人主页》 \color{Darkorange}{《CGod的个人主页》} 《CGod的个人主页》交个朋友叭~ &#x1f492;个人社区&#xff1a; 《编程成神技术交流社区》 \color{Darkorange}{《编程成神技术…

pytorch2.x 官方quickstart测试

文章目录 1.本地环境2.[安装pytorch](https://pytorch.org/get-started/locally/) (Windows GPU版本&#xff09;3. [官方quickstart](https://pytorch.org/tutorials/beginner/basics/quickstart_tutorial.html) 1.本地环境 D:\python2023>nvidia-smi Thu Jul 27 23:27:45…

数据库字段变更监控平台设计开发

序&#xff1a; 在开发过程中&#xff0c;在值班解决客服问题时&#xff0c;在分析定位别人写的业务代码问题时&#xff0c;重点是不是自己写的代码&#xff0c;只看到了数据库中落库最终数据&#xff0c;并不知道业务逻辑问题发生时数据库表中当时数据情况&#xff1f;如果能知…

【开源项目】低代码数据可视化开发平台go-view

数据可视化开发平台go-view 基本介绍 GoView 是一个Vue3搭建的低代码数据可视化开发平台&#xff0c;将图表或页面元素封装为基础组件&#xff0c;无需编写代码即可完成业务需求。 它的技术栈为&#xff1a;Vue3 TypeScript4 Vite2 NaiveUI ECharts5 Axios Pinia2 Plop…

NLP实验案例100个(6-10)

实验六 数据类型 一、实验目的及要求 熟悉数据的数据类型二、实验设备&#xff08;环境&#xff09;及要求 开发环境&#xff1a;jupyter notebook 开发语言以及相关的库&#xff1a;python开发语言 numpy库 三、实验内容与步骤 1.创建一个array类型的数据&#xff0c;设置…

【踩坑】三种方式解决 Homebrew failing to install - fatal: not in a git directory

问题描述 解决方法一 添加安全目录&#xff0c;没有测试。 git config --global --add safe.directory /opt/homebrew/Library/Taps/homebrew/homebrew- git config --global --add safe.directory /opt/homebrew/Library/Taps/homebrew/homebrew-cask 解决方法二 取消挂载这…

【python工具】html中表格转化为excel

背景 大家在实际的工作中可能会遇到这样的场景,查看某个统计的页面数据,其中一些数据是表格形式展示的,比如这是国家统计局关于人口统计的数据: 你想将表格内容下载下来根据自己的需要进行二次加工,但是页面没有提供下载功能或者需要你登陆才能下载。那么重点来了~~ 操…

Rabbitmq的安装与使用(Linux版)

目录 Rabbitmq安装 1.在Ubuntu上安装RabbitMQ&#xff1a; 打开终端&#xff0c;运行以下命令以更新软件包列表&#xff1a; 安装RabbitMQ&#xff1a; 安装完成后&#xff0c;RabbitMQ服务会自动启动。你可以使用以下命令来检查RabbitMQ服务状态&#xff1a; 2.在CentOS…

【前端知识】React 基础巩固(三十五)——ReduxToolKit (RTK)

React 基础巩固(三十五)——ReduxToolKit (RTK) 一、RTK介绍 Redux Tool Kit &#xff08;RTK&#xff09;是官方推荐的编写Redux逻辑的方法&#xff0c;旨在成为编写Redux逻辑的标准方式&#xff0c;从而解决上面提到的问题。 RTK的核心API主要有如下几个&#xff1a; confi…

【Hive实战】Hive的压缩池与锁

文章目录 Hive的压缩池池的分配策略自动分配手动分配隐式分配 池的等待超时Labeled worker pools 标记的工作线程&#xff08;自定义线程池&#xff09;Default pool 默认池Worker allocation 工作线程的分配 锁Turn Off ConcurrencyDebuggingConfigurationhive.support.concur…

QTDAY3

闹钟 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTimerEvent> //定时器事件处理函数 #include <QTime> //时间类 #include <QString> #include <QPushButton> #include <QTextToSpeech> #include …

C++模拟实现queue

1.前言 queue 遵循的原则是先进先出&#xff0c;那到底是用list 还是 vector呢&#xff1f;其实都可以&#xff0c;但是严格来讲vector是不可以的&#xff0c;因为他头删的效率太低了。所以vs官方是不允许用vector的&#xff1a; 因为底层的pop用的是pop_front(), vector是没有…

ssh2-sftp-client实现前端项目自动部署

首先要npm安装插件 npm i ssh2-sftp-client 项目中新建一个js文件 npm run build 之后在终端中 执行这个js文件就可以直接将文件上传到 服务器 import Client from ssh2-sftp-client; import { join } from path;const sftp new Client();const deploy async () > {try…

【iOS】iOS持久化

1 持久化目的 快速展示&#xff0c;提升体验 已经加载过的数据&#xff0c;用户下次查看时&#xff0c;不需要再次从网络&#xff08;磁盘&#xff09;加载&#xff0c;直接展示给用户 节省用户流量&#xff08;节省服务器资源&#xff09; 对于较大的资源数据进行缓存&#x…

小创业公司死亡剧本

感觉蛮真实的&#xff1b;很多小创业公司没有阿里华为的命&#xff0c;却得了阿里华为的病。小的创业公司要想活无非以下几点&#xff1a; 1 现金流&#xff0c;现金流&#xff0c;现金流&#xff1b; 2 产品&#xff0c;找痛点&#xff0c;不要搞伪需求&#xff1b; 3 根据公司…

【学习笔记】视频检测方法调研

目录 1 引言2 方法2.1 视频目标跟踪2.1.1 生成式模型方法2.1.2 判别式模型方法2.1.2.1 基于相关滤波跟踪2.1.2.2 基于深度学习跟踪 2.2 视频异常检测2.2.1 基于重构方法2.2.2 基于预测方法2.2.3 基于分类方法2.2.4 基于回归方法 2.3 深度伪造人脸视频检测2.3.1 基于RNN时空融合…