Silentdoer

导航

Dart随记

【注意,运行dart命令不要在git bash里,很有问题,特别是执行dart pub get这种命令】

dart里Function.apply(Function f, ...)方法似乎是没有意义的(可以不需要的),因为完全可以直接f(...)来执行,好吧是有意义的,比如参数List<dynamic>是外部传过来的,所以我们无法事先知道有多少个参数,因此我们没法写f(list[0], list[1]...)这样的写法(因为size不固定,而f(...)是编译时就要确认有多少个参数),而Function.apply则可以直接Function.apply(f, list)即可,不需要知道list的长度是多少

 

dart 2.x和3.x都是满足了某个case就不会去执行另外一个case,不同的是dart 2要求每个case都加上break(除了default),而dart 3可以不用(所以dart 3里switch的break其实没有用了);这点和java不一样,java如果case 1和case 2,case 1没有加break然后满足了,则case 1和2都会执行(这个确实不合理),而dart2要求必须加break所以不会出现这个问题,而dart3则执行默认不加break也只匹配一个case,哪怕switch的值满足两个case;【由于dart2要求除了default必须有break,所以dart3的改变没有break change(但是break在switch里没有用了),dart3里的switch更像是if else的语法糖】

 

var kk4 = Symbol("ff");
  var kk5 = Symbol("ff");
kk4 == kk5 // true

在dart里Symbol哪怕两个都是new的,kk4 == kk5也会是true【自动享元模式】

Dart里命名构造方法,factory方法和static方法的区别:

1.命名构造方法只能返回自己的类型;

2.factory方法可以返回子类类型;【这两个也是一个“静态方法”(因为实例对象无法调用它),但是很特殊且可以用到类模板声明里的泛型参数】

3.static方法可以返回任意类型;【static方法里不能有泛型参数T,而上面两个可以】

 

当一个方法即是构造方法,又是factory方法时(其实就只是factory方法,只不过被java局限了和类名一样的方法就是构造方法,在dart里不是),它优先是factory方法(具有factory的特性),如external factory List([int? length]);因此List()可以返回其子类(null safety不可调用,因为需要提供fill的值,即有长度时的元素默认值)

 

lambda表达式分为两种,一种是有=>的,这种最好不要加{},而它的返回值就是表达式的返回值(注意赋值语句返回null,但是注意set比较特别,它不会去对返回类型去求值,因此set mapX(y) => {y}这种写法也是可以的),而没有=>的则要用{}包裹,且里面是语句(和Java一样);

因为set不会去计算返回类型,所以最好不要写void set mapY(y) ...;而是直接写set mapY(y)...;比较合适;

 

dart里对于SplayTreeSet允许是dynamic是有原因的,比如:

var set = SplayTreeSet((a, b) => a.toString().compareTo(b.toString()));
  set.add("ss");
  set.add(8);

 这种情况下就允许key是不同类型;

对于extension里的static方法,可以这么理解,没有加static的方法其实隐式的是

 
extension IntExtension on int {
(extension) int doubleInt() {  // 注意,实际代码里用的时候是没有(extension)的
return this * 2;
}
}

而加了static的则那个隐式的(extension)就没了,因此是针对IntExtension的一个静态方法;【因此doubleInt虽然是编译时转换为一个方法,但是不能看成是静态方法,就像factory方法不能看成static方法一样,虽然用法很像

,3.doubleInt() 甚至编译的时候都不是类似静态方法一样调用,即IntExtension.doubleInt(3),而就是直接变成(3 * 2)】

而且注意,extension是没有构造方法的(也不能显式定义),因此不能初始化一个extension类型的对象;

还有就是extension类型不能反射(它可以是Type,但不是Class);

 

void test2<T>() {
  // test2<int>();时为true,说明T的值是T实际类型对象的runtimeType值(实际类型对象即int型的3)
  print(T == 3.runtimeType);
}

 

dart 的pub get的包是放在dart或flutter解压目录(如果是zip解压的sdk)的.pub-cache里的;

依赖默认放在:C:\Users\silentdoer\AppData\Local\Pub\Cache\hosted目录下(Windows)

 

dart里extension是可以没有名字的,即extension on Clazz是可以的,这个时候默认extension的名字是null;

 

然后说说扩展静态方法,dart里的扩展方法如果和Clazz中已经存在的方法同名如都叫test,那么扩展方法不生效,即obj.test调用后还是调用Clazz对象的test方法;

而两个扩展都是扩展到Clazz,但是它们都有同一个方法如foo,那么会报错【毕竟无法确认应该用哪个的扩展方法,用后导入的生效这种显然不好,格式化指不定就把顺序弄没了】;

所以这里引入静态扩展方法的概念,假设Clazz里有静态方法fuck,而我们想要扩展Clazz的静态方法fuck,显然调用Clazz.fuck()的时候是调用的Clazz里的而非扩展里的,因此这里就做成了static方法归属于扩展【但是其实很牵强啊,还是希望能做成如果扩展方法和类里方法有同名的就报错而不是默认用类里的,这样静态方法也是扩展到类上面比较好】【这个假设支持extension的静态方法不是弄到extension里而是Clazz的时候再提个issue问下为什么extension里如果有和Clazz的同名方法不干脆直接报错,因为这个同名方法没有任何意义了】

 

dart里可以配置pub cache目录的位置,通过添加环境变量:PUB_CACHE,然后可以设置其值为:O:\Apps\flutter\.pub-cache【这个目录是下载flutter自带的一个cache 目录,如果只是安装了dart也可以看下dart安装目录里有没有类似的pub cache目录,有的话最好复用】

这个也配置一下吧:export PUB_HOSTED_URL=https://pub.flutter-io.cnexport FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn

 

在dart语言里Null func() {return;}和Null func() {}是可以的,即dart里return;和没有return可以被认为会自动返回null【当然如果函数返回值声明为void,那么这个时候return;和没有return则被认为返回void】

而void test() {return null;}是可以的,但是Null test() {return voidRetFunc();}是错误的,所以说Null是void的subtype,void可以承载null值,而Null不能承载void值,这个关系就像是int可以赋值给int?,而int?不能直接赋值给int,所以int是int?的subtype,因此() {return 3;};的returnType是int而非int?,按这个理推() {return;}的returnType是subtype类型Null,因此推断为Null Function(),

就像var list = [8]被认为是List<int>而不是List<num>一样,会从可能的类型里挑选那个最精确的;

 

dart 里通过json_serializable来生成toJson和fromJson代码在离线环境下的配置,除了添加build_runner等pub依赖包外,还需要有flutter环境(也就是说纯dart还没法用这个,fuck);

而且实体类必须有个默认构造方法;把依赖包弄好,把实体类弄好(默认构造方法,json_annotation的注解等)弄好后执行flutter pub get --offline;然后执行flutter packages pub run build_runner build就可以生成文件了;

【好吧,不需要flutter环境,只有dart环境也可以,即弄好pub依赖后,然后弄好实体类,接着dart pub get --offline(这个其实创建好项目添加了pubspec.yaml依赖后就可以执行了),然后再执行dart run build_runner build即可(注意nnbd以后@JsonKey的nullable已经没用了)】【注意json_serializable的依赖都可以放到dev_dependencies里(如果是打包为一个dart文件集合[类似java打包为一个可执行jar包],则json_annotation可以放dependencies里【注意如果是dart2native则全都放dev_dependencies里即可】)】

 

dart里..操作符只能用于最外层的对象,如果最外层对象的属性也是一个对象也要用到..则必须用括号括起来,如:

var foo = Foo()
              ..age=8
              ..stud = (Stud()..aaa = "sjfl");  // 如果Stud()外面没有括号括起来则..是无法..到Stud的属性的而是Foo的属性

 

dart也可以当脚本语言,即dart可以执行一个单独的dart文件,且该文件最上面也可以有#!/bin/dart;且这个文件的后缀甚至可以不叫.dart叫.txt也是可以的

 

dart create proj_name默认是会创建完毕后执行dart pub get的【以该项目目录为工作目录】,如果不需要【比如离线环境offline情况】可以用dart create --no-pub prop_name

【然后cd到项目目录自己手动执行dart pub get --offline(如果不离线去掉--offline),然后再dart run来运行pub项目】

 

dart运行项目可以进入到项目根目录执行dart run(如果是flutter项目用flutter run),但是我用dart run执行好慢不知道为什么(直接dart ./bin/demo_01.dart就很快,可能dart run还要检查啥的?)

而编译成native则貌似不能以项目为单位,而是以文件?可以用dart2native ./bin/demo_01.dart;

 

windows发布dart包步骤:【不要用powershell发布,但是curl要用powershell】

1.前提,需要能够FQ;

2.有gmall账号【最好先在浏览器上登录一次,好授权的时候能快速点击授权账号】

3.PUB_HOSTED_URL和FLUTTER_STORAGE_BASE_URL先去掉【去掉后需要重开cmd,注意用cmd发布,powershell有问题,但是需要用powershell curl google.com是否成功】

【当然也可以在pubspec.yaml里添加:publish_to: "https://pub.dartlang.org"】(如果是flutter包,地址是:"https://pub.flutter-io.cn"

【新的是去掉publish_to就会发布到flutter/dart中央仓库,如果是加上且值为none貌似是发布到环境变量里的仓库,然后发布的语句要改成:flutter packages pub publish --server=https://pub.dartlang.org

3.具体步骤:

3.1.进入到待发布项目,执行dart pub publish --dry-run,检查是否能出现0 warnings【不过能出现结果应该就行,有warnings貌似也不会影响发布】

3.2.(linux里如果FQ了不需要这一步)在cmd里执行set http_proxy=http://127.0.0.1:1080

set https_proxy=https://127.0.0.1:1080【这两步set很重要很重要,否则一直卡住(可以在步骤3.1之前执行);还有就是这个端口貌似和FQ工具有关,不同的FQ工具用的这个端口是可能不一样的】

3.3 dart pub publish -f【dart用这个:dart pub publish -f --server=https://pub.dartlang.org;flutter用这个:flutter packages pub publish --server=https://pub.dartlang.org

3.4复制控制台上的url到浏览器上打开,勾选自己的gmall账号授权成功后会跳转到pub.dev里(要手动登录一下pub.dev);(silentdoer数值@gmail.com

3.5此时如果cmd出现了Uploading则说明即将成功,过一会就会提示success了;【Successfully uploaded package表示发布成功】

【注意,上面的步骤建议都在FQ状态下执行】

 

dart里命名构造方法和工厂方法和静态方法的区别:

1.命名构造方法和工厂方法的写法几乎一致,都是类名.方法名,如Student.good() {...},区别在于构造方法前面要加一个factory,且方法体里面要有return语句,而且return的对象必须是Student的subtype【可以直接赋值给Student变量】(不要翻译成子集或子类都不是很合适)类型(即返回的对象也可以是Student的子类);而命名构造方法则返回的只是Student类型,且没有return语句(因为它本身就是创建对象的构造方法)【在查构造方法怎么复用另一个构造方法】;

而且注意构造方法和工厂方法都是“实例方法”,因此Student<T>的T是在构造方法参数和工厂方法参数上是生效的,也因此构造方法和工厂方法里不能有其他自定义的泛型,类似void test<M>()这样是不允许的;

而静态方法则不允许用到类定义里的泛型,即Student<T>那么void staticMethod(T s)是错误的,Student<int>.staticMethod也是错误的,只能Student.staticMethod;【也算可以理解吧,因为一开始是没有扩展的,而静态方法是完全不依赖类型,因此只需要List.staticMethod()即可,List<int>.staticMethod()完全没必要,当然后面出了extension后这种设计其实不太合理了,也算合理吧否则List<int>?是不是也得加方法,这样一搞同一个静态方法在N多个相似的类上都有就会很乱,因为他们的功能是完全一样的因此只允许一种调用方式是OK的】【这也是为什么扩展里的静态方法没有注册到类型上的原因就是因为存在泛型类,而泛型类实例不能调用静态方法】

大佬的说法是:

There is no plan to make shadowing type parameters an error in the language. If you want to protect yourself against doing it by accident, you can use the lint avoid_shadowing_type_parameters.

The current Dart type inference treats "raw types" (a type like List) as specified, and therefore not subject to inference. Since the type is generic, and there is no inference for it, it's instantiated to bounds, giving you List<dynamic>.
One of the reasons for this choice is that that was the behavior in Dart 1, and it mad the Dart 2.0 migration easier to preserve the old behavior. There were also considerations about the inference algorithm needed to do something else.

You cannot invoke static methods on instantiated generic types because it has no effect. There was no reason to allow it, so the syntax doesn't allow you to write it.

You actually can invoke static methods on instantiated type aliases now:

typedef IntFoo = Foo<int>;
... IntList.staticMethodOnFoo(...) ...

It doesn't make any difference to the call, static methods cannot access type parameters of the class, only instance and constructors can (because the type parameters are treated as properties of instances, not the class declaration itself, and static methods are properties of the class declaration, the declaration being treated as a namespace).

So, it's not correct that List.copyRange(...) is equivalent to List<dynamic>.copyRange(...). It really is treating the List declaration as a namespace, not as a class.

Allowing List<int>.staticMethod is confusing because it really would just mean the same thing as List.staticMethod. It's unnecessary and confusing, and therefore it's not allowed. There are no plans to allow it.

 大概意思是类型定义的List如List a是类模板List(或说是List<T>)的具体化模板类(即List<dynamic>),而通过泛型具体化类名调用方法是不可行的即List<dynamic>/List<int> .staticMethod是错误的,只允许通过类模板来调用即List.staticMethod(此时List实际上的List<T> 它不属于抽象类【即如果一开始要求List<dynaic>也必须写全,那么这里List a是错误的写法,因为List还并不是一个class,它是一种raw type(即是type而非class不能承载对象)】,也因此允许扩展调用静态方法也是可以接受的(匿名extension里写静态方法就是程序员自己的问题,就像java里匿名类写静态方法也是沙雕,而null可以有多个含义是正常的,因此它可以表示匿名的意思,即extension on AClass,null可以表示不存在的,匿名的,没有的,nil的等类似的意思),因为扩展也是一种raw type无法实例化也不是class,和类模板List一样,而类模板可以调用静态方法,自然扩展也应该可以,所以目前来看似乎没有了需要去排斥的语言特性,大多数自己不太喜欢的语法其实都有一定的理由去那么做)

【这里也不去苛求变量声明的List<dynamic>必须写全了,迁移是一方面,动态性也是一方面,毕竟自己也是很长时间很排斥var的会导致没有ide的情况下看代码很蛋疼,而没有IDE又不想写var的情况下来写零碎快速的脚本(也需要可读性只不过性能啥的没有要求)如果要求必须写全List<dynamic>确实不太符合动态语言的特征,所以这个“不一致”也可以保留吧(当然如果可以当然是希望不保留)】

 

类型推断如果依赖返回值,比如 T test<T>()那么左边的变量类型是可以作为推断依据的如List<int> b = test();如果是有参数作为推断依据如List<T> test<T>(T t),那么还是优先以左边来做推断的如List<num> b = test(9),b的对象类型还是List<num>,因此b.add(3.9)是可以的;(如果是var b = test(9)则b的变量类型和对象类型都是List<int>因此分析阶段就不允许添加double值);而如果是List<num> b = test<in>(9),则b的变量类型是List<num>但是对象类型是List<int>因此不能添加3.9否则运行时报错)【总结就是如果右值显式指定类型则其类型就是指定的类型,否则会根据左边的内容看能不能推断,不能再根据右边的参数来推断,赋值给左值要求右值是左值的subtype,即类似List<int>对象可以赋值给List<num>对象】

 

dart构造方法a可以调用构造方法b,方式为:【注意调用另一个构造方法的构造方法是不能有方法体的,所以Student.named() : this("sfff") {}是错误的要把{}去掉】

class Chipmunk {
  String name;
  int fame;

  Chipmunk.named(this.name, [this.fame]);

  Chipmunk.famous1() : this.named('Chip', 1000);
  factory Chipmunk.famous2() {
    var result = new Chipmunk.named('Chip');
    result.fame = 1000;
    return result;
  }
}

 

注意List<dynamic>严格意义上来说不是List类型的具体化,因为它还是“泛型”

 

在dart里目前发现一处不太一致的地方(虽然情有可原),就是List a = [],那么这个List其实是List<dynamic>,但是在泛型类静态方法调用上则不允许写成Stud<dynamic>.staticMethod()而允许Stud.staticMethod();其实这里的Stud.staticMethod()本质上是Stud<T>.staticMethod();要解决这个不一致也很简单,就是要求所有的必须写清楚类型,即List<dynamic> list而不允许List list;

但是基于方便的折中考虑【dart也可以当成快餐语言(动态语言快速写脚本)】最后允许了这种写法;【List.staticMethod的List是raw type而非class,无法承载对象】

 

变量类型和对象类型是两个概念,runtimeType是输出对象类型;

 

dart pubspec.yaml里的版本如果想直接用最新的版本,可以写any,如path: any,默认会获取最新的版本。

 

dart path包是可以处理schema是package的(默认只能处理file的),可以final Context context = Context(style: Style.url);然后用context来处理package的Uri即可(类似path默认的处理方式如dirname(..)等函数);

也可以用Isolate里resolvePackageUri(Uri packageUri);可以将package的Uri转换为file的Uri;

 

Isolate对象的handler的message参数类型不能是ReceivePort,而如果是其他类型比如自定义Student类型,则dart会自动复制一个副本给isolate,而不是共享同一个对象;只有SendPort这个特殊类型dart才允许多个线程使用同一个对象;

而且一个SendPort对象可以在多个线程中共享,而不只是两个;【而且注意,send发送出去的对象如果不是SendPort也是一样是深复制一个数据给其他线程而非send的就是两个线程是同一个对象了】

如果isolate1发消息m给isolate2,isolate2收到m后(m是经过了深拷贝的)直接再发送回给isolate1,那么注意如果m是函数对象,则isolate1收到的m和它发的那个是完全一样的,如果是class对象则不是一样了;

而如果class对象里面有Function,则Function这部分字段也是完全一样的【即Function对象在isolate里是单例模式】

SendPort也可以在Class消息的字段里,也是可以直接传递给其他isolate的【而Isolate对象作为消息的一部分时和class没什么区别是会变得(尽管我不清楚像这类功能性的类对象它深拷贝拷贝完整没有)】

 

main() {

await xxx;  // 这里要注意,await并不是一直让main线程等待,而是await时 main线程是能够接收其他的消息的;(当然下面的print('bb')是会保证一定是await成功后才会执行的)

print('bb');

}

 

dart里类如果显式写了构造方法或者factory方法,则不会默认提供无参构造方法;

 

dart里async方法的返回类型必须是Future或者FutureOr类型【除了void,所以从这点上void和Null还是不一样的】,而返回类型是void的async方法不能被await

 

dart里int正数最大值是9223372036854775807,然后令其++后会变成最小负数即-9223372036854775808

 

数据应该用组合(即A类里可以有B和C等类型数据构成A类数据),而能力应该用继承;

 

dart 里 if (a is Future)的Future是Future<dynamic>,List<Future>的Future是Future<dynamic>, Future meth(){}的Future也是Future<dynamic>目前只发现Future.staticMethod的Future是raw type。

 

dart里不能像C#一样只允许List<Sub>赋值给Iterable<Base>而不能赋值给List<Base>,否则covariant关键字就没有用了

 

dart里最好不要用kill来关闭isolate,它会令isolate当前执行的future task在await后就立刻退出,所以isolate事件循环队列里的其他future task就直接不执行了,很容易导致业务数据异常;最好的方法是往isolate里发送一个关闭消息,然后由isolate handler代码里主动清理后关闭receivePort并退出相关的逻辑;(java里interrupt线程也是靠用户去判断是否已经interrupt了,然后主动退出)

 

dart里stream.listen(handler)会返回StreamSubscription类型对象记做subscription,subscription.cancel()只是表示这个对stream数据的监听器关闭了(流是没有关闭的,流里面还能接收数据);

但是如果stream是单监听器的,则虽然subscription.cancel()不等价于receivePort.close(),但是由于stream不可能再被其他监听器消费,所以dart会默认认为stream已经“死了”,所以不会占用isolate的事件循环的检测(即main方法执行完其他代码会自动退出而不会等到stream来数据,因此类似close了receivePort(stream是没有close方法的)【事实上不是】);

但是如果stream是multipart的,则由于关闭了某一个subscription(stream数据监听器),stream是可以再次添加新的监听器的,所以stream还是会继续占用事件循环的检测(其他代码执行完了会isolate也不会结束而是等待stream数据),这个时候关闭了subscription只是说其对应的handler不会再被调用,因此后面stream接收到的数据只会由其他的监听器来消费(有点像rocket mq);

multipart的stream可以同时拥有n个listener,并不是只能顺序等第一个cancel后再添加第二个,且stream来了数据会给每个在监听中且没有cancel的listener消费;

而如果receivePort.close()的话,则bstream不管里面是否还有没有分发的数据/事件/消息,也会直接关闭,即所有的handler都不会再接收到消息,stream里的数据也会丢失(所以close receivePort之前最好判断下bstream是否是空了【好吧没法实现,receivePort不能用于判断isEmpty因为receivePort不是广播的,而bstream.isEmpty会hung住(因为数据可以源源不断)除非receivePort先close,但是close了会清空未被消费的元素,所以也没有数据了;不过也还好,因为可以发送结束消息,receivePort的bstream收到后再close receivePort,如果client端发了结束消息却又继续发消息可以不管(但如果是多个client则还是可能有消息漏掉的可能,不过这个不可避免,毕竟就算可以判断isEmpty,那也是那个瞬间是空了,但是后续执行清理工作的时候是可能又来数据的)】);

注意receivePort.close()后会清空里面的数据,所以close后再判断isEmpty会是true,length是0;

receivePort.asBroadcastStream()得到的bstream和receivePort是同一根管子,只是对外接口有不同而已;

 

判断泛型T是否是void可以用:

bool isVoidType<T>() {
    var list = <T>[];
    var s = <void>[];
    if (list.runtimeType == s.runtimeType) {
      return true;
    } else {
      return false;
    }
  }

 

dart命名构造方法也是可以提供泛型参数的:Foo<int>.name()【和普通方法的泛型参数提供方式不一样,如Foo.staticMethod<int>();】

 

dart 里面lambda表达式如果用=>则右侧一定整体是返回值,所以 () => {9}其实是返回了一个Set,而不是一个函数块;所以不能() => {return 9;}这样的写法;同理() => {'aa': 9}是返回了一个Map

 

dart 里子类实现基类的方法,参数名可以和基类定义时的不一样,同样,typedef Foo = void Function(int aaa); Foo变量可以接收参数名不是叫aaa的函数实例;

 

dart和java对包的反射的区别之一是,dart支持反射和导入一体,即"package:..."然后dart自己就支持去仓库里找相关的package然后反射其文件到内存里;而java则需要先手动导入jar文件(也很复杂),然后再反射jar文件里的类;【dart最好是以绝对路径的方式来反射比较好,因为可以获取package所在目录,用绝对路径可以指定版本;测试的时候用pub get一下就能把本项目发布到本地仓库里测试了,虽然多了一步,但是整体比java好处大于坏处;java必须事先load到内存,而且java的目录必须和包名路径一直不方便做脚本】【待测】

【经过测试,dart里如果是以package作为Uri来load的话,要求这个package必须已经添加到pubspec.yaml里,且对应的package有某些文件import了,所以以package来动态加载类文件意义不大,因此需要用Isolate.resolvePackageUri(packageUri)来实现这种方式可以不需要import,但是这种仍然需要package有添加到package_config.json里(类似java的--classpath)】

 

【Good,dart里也支持将自己import为一个package(前提是自己是lib库项目且已经pub get生成了package_config.json【项目自己的lib路径就是本地项目路径而非仓库路径,因此一直是最新的不需要先复制到仓库里】),这样就不用担心自己本地测试和发布到仓库里由别人调用的行为是不一致的了(之前自己本地测试是用相对路径../uu.dart这种方式,但是别人用可能是package:aaa/src/kk/uu.dart,现在自己本地测试也可以这么用)】【bin项目给它创建个lib目录也一样是可以像上面一样工作的(因此bin项目如果一些通用的工具类最好是放在lib目录里,可以同时有bin和lib目录)】

 

import 'package/kkk/src...'的kkk是先从package_config.json里找到之后,根据其对应的路径再去找这个package的具体文件,而不是import package就是直接去本地仓库里找相关的库;

【如果不会涉及package,那么package_config.json是没有意义的(比如导入库文件是以绝对或相对路径导入而非以package导入)】

 

dart里面如果是AClass.method(),则AClass是表示一种约束而非对象;而如果var t = AClass;则此时AClass是作为一种对象赋值给t的;java里需要显式获取AClass的类型对象,即AClass.class

dart if (dart.library.io) 的用法只能用于import而不能用于条件判断;

 

dart里除了不能发送ReceivePort对象,Future对象最好也不要发送【Completer也是,因为得不到想要的结果】,因为Future对象发送到另一个isolate进行await可能导致一直获取不到值,比如Future.value(33)对象发送到另一个isolate里await,它会一直获取不到值,原因就是这个Future对象在该isolate里没有办法触发更新此Future对象的事件,所以这个Future对象一直是等待状态(Future对象内部有维护当前是什么状态);而创建这个Future对象的地方由于Future.value(33)给该isolate提交了信号,所以await时该isolate能够捕获到然后更新这个Future对象,所以能取消等待获取到值;【如果Future对象是以await成功后再发送,则接收方isolate await也能获取到值】;因此并发的task如果返回值是Future,则要求在worker里await掉【因为这个Future对象是在worker里产生的(执行task内部产生)】,而不能发送回master里再去await;

 

可以有局部变量void kk;且kk = 'sss';是合法的,但是不能使用kk,也不能dynamic us = kk;因此await 一个Future<void>是可以的,且可以将它赋值给void类型变量,也可以将void s变量赋值给void b变量;只是不能当对象使用(包括打印,void不是对象所以也没有runtimeType之类的属性)【类似Rust的Never】【也不能void kk = 'ss'; if (kk is xxx)】【void变量之所以可以承载任意值是因为它对任意值都忽略掉无效了不会去计算这个值(即不会对该对象做啥操作,所以void ss = 'k'等价于任意的void ss, void ss = 9 ...)】

 

dart 允许await 一个具体对象(void不是对象所以不能await),比如 await 3;await test();【test返回对象类型】

 

【注意】dart也不允许将void变量作为消息发送到isolate里,但是如果void变量是对象属性则可以,此时它的值是null;如果说void test(){} var ss = test();ss是void变量无法print;但是如果我们hack一下用Function m = test;var ss = m();这个时候ss的值就是null了是可以打印了;

 

StackTrace对象也不能send,但是可以toString()后以字符串发送;

 

dart没有静态方法和静态块的说法,而且类的静态字段的初始化也不是在使用了类就会执行,而是要具体使用了这个静态字段的时候才会执行,当然是执行一次【即A.b;A.b只第一次初始化了b,所以dart在没有late关键字之前其实就已经支持late初始化了】

 

late最好少用,因为它本质上和可空类型一样需要每次判断是否是null;

 

dart的isolate的kill()方法是很危险的,最好不要直接kill,而是处理完善后逻辑后再kill,它会令isolate事件循环执行完当前的事件后就不再执行后面的了,导致业务逻辑出现混乱,如代码:

void handler(void v) {
  Future.microtask(() async {
    print('aaaa4');
    await Future.delayed(Duration(seconds: 3));
    print('bbbb4');
  });

  Future.delayed(Duration(seconds: 1), () {
    Isolate.current.kill();
  });
}

这里bbb4不会输出,因为1秒钟以后发送了kill消息给isolate,而bbb4因为上面的await产生了一个需要3秒以后才激活的事件,kill时事件没有被激活,因此被直接丢弃掉了;

 

dart里BroadcastStream如果有多个listener,则消息来了会给这些listener都发送【类似kafka有多个消费者group】

 

completer只能完成一次,完成后必须可以通过isCompleted判断是否完成;【正常完成和异常完成都会变成isCompleted为true】

 

dart里var point = Point(1, 2);和var point = const Point(1, 2);没有任何区别,即右侧的构造方法如果是const的则可以省略const【因为有const构造方法的类要求所有字段都必须是final,那Point对象是一定无法修改的了】,不过如果右侧显式用了const则要求Point必须是const构造方法;而如果左侧是const(var改成const)则也是要求右侧的构造方法必须是const的但是同样可以省略const;

 

dart aot命令dart compile exe ./bin/xxxx.dart

 

dart的extension不是Type,因为它不能作为左值类型,以及返回值类型和参数类型等,即AType a = ATypeSubClass();,AType不能是extension;扩展某种情况下类似dart package命名空间概念(import sss as kk的kk),kk也是拥有成员,但是无法作为类型【稍微平抚了一下extension有静态方法却不是type的心】

还有就是import '...' as uu;然后uu.aaa();但是uu也不是Type;而且uu(文件包)里只能有“实例方法”不能有“静态方法”,extension里只有“静态方法”没有属于extension的“实例方法”,所以这么一对比倒也没有很不一致;

后续Type会改造为Type<T>,比如String是Type<String>的实例(而String即是类型也是“实例”),所以可以extension StringStaticExt on Type<String> { void kk() {}}来实现对String的静态方法扩展;

 

dart离线activate的方式,其实就是在一台有外网的机器,执行dart pub global activate interactive(注意配置PUB_CACHE,使得activate的package的路径和离线机器里的路径是一致的),执行完毕后会在PUB_CACHE的bin里有interactive.bat(类似可执行程序。。),然后把这个bin目录加入环境变量Path,PUB_CACHE下还有个global_packages目录,里面是具体activate的应用,这个应用是固定了activate时的路径的,所以要放在和有线环境机器相同的路径下

 

dart:io里的sleep(duration)是将整个Isolate都sleep掉,如果只是希望当前的task暂停duraion,可以用await Future.delayed(duration);

 

dart的switch和java的switch不一样,java的switch是如果匹配上了一个case,这个case里没有break的话则会继续执行下一个case,哪怕这个case的值不被匹配:

        // 这里case 0和case 1都会输出,但是这个其实是不符合直觉的,你可以说case 0不break,然后能继续去匹配,但是前提是下一个case是能被匹配上
        int a = 0;
        switch (a) {
            case 0:
                System.out.println("接口是否");
            case 1:
                System.out.println("检控方而");
                break;
            default:
                System.out.println("捷克斯拉夫的");
                break;
        }

而Dart则不break,下一个case要能匹配才会输出下一个case:(测试了下case 0和case int _在case 0不break的情况也不会输出case int _,break在dart的switch里有什么作用?经过仔细,当case 0里是空的逻辑时,然后case 0里同时没有break则会延续到case int _里【注意这种情况不会继续判断case,比如case 0下面是case 1,switch判断匹配上的case 0后里面是空的处理逻辑则会自动fallthrough而不会自己判断下一个case 1是否匹配;注意只会fallthrough一次,如果下一个case 1有处理逻辑则不会继续fallthrough,没有的化则会继续fallthrough】)

// 这里只输出case 0
int a = 0;
  switch(a) {
    case 0:
    print("减肥的顺口溜");
    case 1:
    print("fsfsfe");
    default:
    print("士大夫接口");
  }

// 这里也只输出case 0,说明default如果有匹配上,则一定不会输出,哪怕它的上一个case是被匹配上的且没有break
int a = 0;
  switch(a) {
    case 0:
    print("减肥的顺口溜");
    default:
    print("士大夫接口");
  }

 

 

要防止dart访问github,可以改hosts文件将github.com域名访问127.0.0.1

posted on 2020-08-18 15:47  Silentdoer  阅读(422)  评论(0编辑  收藏  举报