Dart语言入门四
Posted MobileDev小园地
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dart语言入门四相关的知识,希望对你有一定的参考价值。
库和可见性
import和library指令可以用来创建模块和可复用的代码库。库不仅能够提供API,也同时是一个私有单元:以下划线_开头的变量只有在库中是可见的。每一个Dart的应用都是一个库,即使没有使用library指令。
库可以用使用包(package) 来进行分发。参照<Pub Package and Asset Manager>来获取关于pub (SDK中的一个包管理器)的知识。
使用库
使用import关键字来指定一个库的命名空间如何在另一个库的范围内使用。
例如,Dart web应用经常使用dart:html库,它可以通过这种方式来导入:
import 'dart:html';
唯一需要指定给import的参数就是这个库的URI。对于内建库,这个URI有一个固定的前缀写法:dart:。对于其他库,你可以使用一个文件系统的路径或者package:语法。这种package:写法是指由包管理器(如pub工具)指定的库。例如:
import 'package:test/test.dart';
注意:URI是指统一资源标识符。URLs(统一资源定位符)是一种常用的URI。
指定一个库前缀
如果导入的两个库中有名称冲突,可以为其中一个库或者两个库指定前缀。例如,库1和库2都有一个Elemenet类,你可以这么来写代码:
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// Uses Element from lib1
Element element1 = Element();
// Uses Elemenet from lib2
Element element2 = lib2.Element();
只导入库的部分内容
如果只想使用库的部分内容,可以选择性地导入库。例如:
// Import only foo.
import 'package:lib1/lib1.dart' show foo;
// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;
懒加载一个库
延迟加载(或者叫做懒加载)允许程序在需要时才加载一个类。下面是可能使用懒加载的一些情况:
减少一个应用的启动时间
执行A/B测试 - 例如尝试一个算法的两种不同实现
加载一些不经常使用的功能,例如可选的屏幕和对话框类
要懒加载一个库,首先必须通过在导入库时使用deffered as。
import 'package:greetings/hello.dart' defferred as hello;
当需要使用这个库时,通过库的标识符来调用loadLibrary():
Future greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
在上面的代码中,await会暂停当前程序的执行直到库加载完。参考下面的<异步支持>节来获取更多关于async和await的知识。
loadLibrary()方法可以被多次调用也不会发生问题,无论调用几次,它只加载一次。
当使用延迟加载时,要记住以下要点:
延迟加载的库里的常量在当前导入文件里不是常量。记住,这些常量在库加载完毕之前并不存在。
在当前导入文件里不能使用延迟加载库里的类型。如果出现这种情况,可以考虑把这个类型放到一个第三方库,在当前文件和延迟加载库里都导入这个第三方库。
Dart在定义使用deferred as namespace的命名空间里隐式插入loadLibrary()。loadLibrary()方法会返回一个Future。
虚拟机差异:Dart虚拟机允许访问延迟加载库里的成员即使在调用loadLibrary()之前。这种行为可能会改变,所以不能依赖当前这种虚拟机行为。
实现库
参考’创建库包‘文档https://www.dartlang.org/guides/libraries/create-library-packages(后面会有相关文章)来学习如果实现一个库,包括:
如何组织库里的代码
如何使用export指令
何时使用part指令
何时使用library指令
异步支持
Dart的库里有非常多的返回Future或者Stream对象的函数。这些函数是异步的:它们在调用一个可能耗时的操作(如I/O)后会直接返回而不用等着这个耗时操作完成。
await和async关键字用来支持异步编程,使得写异步代码和写同步代码一样方便。
处理Future
有两种方式来获取一个完整的Future的结果:
使用async和await
使用Future的API,在库相关的文档有介绍
使用async和await的代码是异步的,但是它看起来却像是同步代码。例如,下面的代码使用await来等待一个异步方法的返回结果:
await lookUpVersion();
使用await的代码必须写在异步函数里-一个标记为async的函数:
Future checkVersion() async {
var version = await lookupVersion();
// Do something with version
}
注意:尽管一个异步函数可能执行一些超时的操作,代码并不会等待这些超时的操作。这个异步方法只执行到它遇到第一个await表达式。然后它直接返回,只有await表达式执行完毕时才恢复执行。
使用try,catch和finally来处理使用await代码里的异常和代码的清理:
try {
version = await lookUpVersion();
} catch(e) {
// React to inability to look up the version.
}
await可以在异步函数里多次使用。例如,下面的代码有三次等待异步函数的结果:
var entrypoint = await findEntryPoint();
var exitCode = await runExecutable(entrypoint, args);
await flushThenExit(exitCode);
在await表达式里,这个表达式通常是一个Future,如果不是,它也会被自动包装成一个Future。Future对象是一个返回对象的承诺,await表达式的值就是这个返回对象,await表达式会暂停执行直到这个对象可用。
如果使用await时遇到编译时的错误,确保await是用在一个异步函数里。例如,在程序的main()函数里使用await,main()函数体必须被标记为async:
Future main() async {
var version = await checkVersion();
print('In main: version is ${await lookupVersion()}');
}
声明异步函数
异步函数的函数体被标识有async修饰符。
为一个函数添加async关键字使得它返回一个Future。例如,下面的同步代码会返回一个字符串:
String lookUpVersion() => "1.0.0";
如果把它改成一个异步函数 - 例如,因为未来的实现可能是非常耗时的 - 返回值就是一个Future。
Future<String> lookUpVersion() async => "1.0.0";
注意这个函数体并不需要使用Future的API。Dart可以在需要的时候创建Future对象。
如果你的异步函数并不返回一个有用的值,把它的返回值声明为Future<void>。
处理Streams
有两种方式来从一个Stream获取结果:
使用async和一个异步for循环(await for)
使用Stream的API,在库相关的文档有介绍
注意:在使用await for之前,确保它确实能使代码变得更清晰、你确实要等待所有流的结果。例如,通常情况下不应该在UI事件的监听器里使用await for,因为UI框架会一直发送流事件。
一个异步循环有着下面的形式:
await for (varOrType identifier in expression) {
// Executes each time the stream emits a value
}
表达式的值必须是Stream类型。执行顺序如下:
等待,直到这个流发送一个值
执行for循环的循环体,循环体内变量被赋值为这个发送的值
重复执行步骤1和2直到流关闭
可以使用break和return语句来停止监听流,它会中断这个循环并且从流上注销。
如果在实现一个异步for循环的时候遇到一个编译错误,确保await for是写在一个异步方法中。例如,如果要在main函数里使用异步for循环,那么main函数需要标记为async:
Future main() async {
// ...
await for (var request in requestServer) {
handleRequest(request);
}
// ...
}
更多的关于异步编程的信息,可以参考库介绍文档的dart:async部分。也可以参考文章:Dart语言异步支持:第一部分和Dart语言异步支持:第二部分,还有Dart语言规范。
生成器
如果要延迟生成一个值的序列,可以使用生成器函数。Dart有两种内置的生成器函数:
同步生成器:返回一个Iterable对象
异步生成器:返回一个Stream(流)对象
要实现一个同步生成器函数,把函数标记为sync*,并且使用 yield语句来产生值:
Iterable<int> naturalTo(int n) sync* {
int k = 0;
while (k < n) yield k++;
}
要实现一个异步生成器函数,把函数标记为async*,并且同样使用yield语句来产生值:
Stream<int> asynchronousNaturalsTo(int n) async* {
int k = 0;
while (k < n) yield k++;
}
如果生成器是递归执行的,可以使用yield*来改善它的性能:
Iterable<int> naturalsDownFrom(int n) sync* {
if (n > 0) {
yield n;
yield* naturalsDownFrom(n - 1);
}
}
要获取更多的关于生成器的知识,可以参考Dart语言异步支持:第二部分。
可调用的类
要使得Dart中类可以像函数一样被调用,在类中实现一个call()方法。
在下面的例子里,WannableFunction类定义了一个call()方法,这个方法拼接三个字符串参数,用空格分割,并且结尾添加一个叹号。
class WannableFunction {
call(String a, String b, String c) => '$a $b $c!';
}
main() {
var wf = new WannableFunction();
var out = wf("Hi", "there", "gang");
print("$out");
}
更多关于把类当做函数的信息,可以参考Emulating Functions in Dart(在Dart里模拟函数)。
Isolates
大多数计算机,甚至移动平台,都有多核的CPU。为了充分利用这些内核,开发者经常使用共享内存的并发线程。但是,共享内存的并发是会经常引发错误,甚至一些复杂的错误。
相比较于线程,所有的Dart代码运行在isolate里。每一个isolate有它自己的存储堆,保证了没有任何一个状态可以被其他的isolate访问。
关于更多isolate的知识,请参考dart:isolate库文档。
Typedefs
在Dart里,函数是对象,就像字符串和数字也是对象。一个typedef,或者称为函数类型别名,给一个函数类型起了一个名字,这个名字可以在声明成员和返回类型时使用。当一个函数类型被赋值给一个变量时,一个typedef会保持着这个类型信息。
考虑下面的代码,没有使用typedef:
class SortedCollection {
Function compare;
SortedCollection(int f(Object a, Object b)) {
compare = f;
}
}
// Intial, broken implementation
int sort(Object a, Object b) => 0;
void main() {
SortedCollection coll = SortedCollection(sort);
// 这里知道compare是一个函数,但是是什么类型的函数?
assert(coll.compare is Function);
}
类型信息在把f赋值给compare的时候会丢失。f的类型是(Object, Object) -> int (这里->意思为返回),但是compare的类型是Function。如果我们改变代码使用明确的名字并保持类型信息,开发者和工具都可以使用这个信息。
typedef Compare = int Function(Object a, Object b);
class SortedCollection {
Compare compare;
SortedCollection(this.compare);
}
// Intial, broken implementation
int sort(Object a, Object b) => 0;
void main() {
SortedCollection coll = SortedCollection(sort);
assert(coll.compare is Function);
assert(coll.compare is Compare);
}
注意:当前typedef只能用于函数类型,希望在以后能有所改变。
因为typedefs只是简单的重命名,它们提供了检查任何函数类型的一种方式。例如:
typedef Compare<T> = int Function(T a, T b);
int sort(int a, int b) => a - b;
void main() {
assert(sort is Compare<int>); // True!
}
元数据(Metadata)
元数据可以用来给你的代码添加更多信息。元数据注释以一个@字符开始,跟着一个编译期的常量引用(例如deprecated)或者一个常量构造函数的调用。
有两个所有Dart代码都可以使用的注释:@deprecated和@override。例如使用@override可以参考类一章()的继承一个类这节。下面是一个使用@deprecated注释的例子:
class Television {
// _Deprecated: use [turnOn] instead._
@deprecated
void activate() {
}
// Turns the TV's power on.
void turnOn() {...}
}
你也可以定义自己的元数据注释。下面是定义一个@todo注释的例子,有两个参数。
library todo;
class Todo {
final String who;
final String what;
const Todo(this.who, this.what);
}
下面是一个使用这个@todo注释的例子:
import 'todo.dart'
@Todo('Seth', 'make this do something')
void doSomething() {
print('do something!');
}
元数据可以出现在库、类、typedef、类型参数、构造函数、工厂、函数、属性、参数或者变量声明之前,也可以出现在import或者export指令之前。可以在运行中通过反射来获取元数据信息。
注释
Dart语言支持单行注释、多行注释和文档注释。
单行注释
单行注释以//开始,所有//和本行结尾标记之间的内容都是注释,会被Dart编译器忽略。
void main() {
// TODO: refactor into an AbstractLlamaGreetingFactory?
print('Welcome to my Llama farm!');
}
多行注释
多行注释以/*开始,以*/结束。所有/*和*/之间的内容会被编译期忽略(除非这个注释是一个文档注释;参考下一节)。多行注释可以嵌套使用。
void main() {
/*
* This ia a lot of work. Consider raising chickens.
Llama larry = Llama();
larry.feed();
larry.exercise();
larry.clean();
*/
}
文档注释
文档注释是///或者/**开始的多行或者单行注释。在连续的行上使用///和使用多行文档注释有一样的效果。
在文档注释里,Dart编译期会忽略所有内容除非它在中括号里。使用中括号,可以引用一个类、方法、成员属性、全局变量,函数和参数。中括号里的名字会在文档注释所在代码的词法作用域里识别。
下面是一个引用其他类和参数的文档注释例子:
/// A domesticated South American camelid (Lama glama).
///
/// Andean cultures have used llamas as meat and pack
/// animals since pre-Hispanic times.
class Llama {
String name;
/// Feeds your llama [Food].
///
/// The typical llama eats one bale of hay per week.
void feed(Food food) {
// ...
}
/// Exercises your llama with an [activity] for
/// [timeLimit] minutes.
void exercise(Activity activity, int timeLimit) {
// ...
}
}
在生成的文档里,[Food]会变成一个指向Food类API文档的链接。
要解析Dart代码并生成HTML文档,可以使用SDK里的文档生成工具。要查看生成文档的例子,可以参考Dart API文档。要获取更多的关于结构化注释的信息,请参考Dart文档注释引导文档。
总结
Dart入门简介文章总结了Dart语言中经常使用的特性。Dart语言正在发展,更多的特性正在被实现,希望有更多的特性出现但不会对现有的代码有影响。更多的信息,可以参考Dart语言规范 (https://www.dartlang.org/guides/language/spec)和Effective Dart (https://www.dartlang.org/guides/language/effective-dart)。
关于Dart语言中更多的关于核心库的知识,参考Dart库文档(https://www.dartlang.org/guides/libraries/library-tour)。
本文主要内容翻译自Dart官方英文文档:www.dartlang.org/guides/language/language-tour
其他相关文章:
以上是关于Dart语言入门四的主要内容,如果未能解决你的问题,请参考以下文章