前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Dart 3.0 语法新特性 | switch 匹配加强

Dart 3.0 语法新特性 | switch 匹配加强

作者头像
张风捷特烈
发布2023-07-09 15:56:08
1.4K0
发布2023-07-09 15:56:08
举报
文章被收录于专栏:Android知识点总结

theme: cyanosis

一、 重新审视 switch 关键字

众所周知, switch 关键字用于流程控制: 可以基于一个对象进行匹配,并通过 case 关键字产生分支语句,进行不同的逻辑处理。其中有一个非常值得注意,使用者很容易忽略的一点:

Dart3.0 之前: 分支中的 case 关键字后的对象必须是 常量

1. Dart 3.0 之前的 switch 关键字

在日常开发中,switch 匹配的对象,一般是 int/double/String/enum。 比如下面的 foo1 方法中,对 int 型的变量通过 switch 进行匹配,根据 case 情况,进行不同对应的逻辑处理:

代码语言:javascript
复制
void foo1(int value) {
  switch (value) {
    case 0:
      print("=====零=====");
      break;
    case 1:
      print("=====壹=====");
      break;
    default:
      print("=====無=====");
  }
}

在 Dart3.0 之前,case 后就可以放置任何类型的 常量 ,比如下面定义的 Number 类型。只不过这种写法并不是很常用,Dart 3.0 之前的 switch 语法点也就止步于此:

代码语言:javascript
复制
void foo2(Number value) {
  // switch 在 Dart 3.0.0 之前就一直可以匹配任何对象
  switch (value) {
    case const Number(0):
      print("=====零=====");
      break;
    case const Number(1):
      print("=====壹=====");
      break;
    default:
      print("=====無=====");
  }
}

class Number {
  final int value;

  const Number(this.value);
}

2. Dart 3.0 之后的 switch 关键字

在 Dart 3.0 之后引入了模式匹配 Patterns ,而本质上来说 switch 关键字的作用就是在进行匹配校验。所以 Patterns 的引入,极大加强了 switch 的语法特性。下面通过一个小例子体会一下:

比如今天是 2023 年 6 月 9 日,现在想要拓展一下 DateTime 类型,给一个 describe 方法用于输出 DateTime 对象和今天的天数差值情况。比如 6 月 12 日输出 3 天后 ; 6 月 8 号输出 昨天

代码语言:javascript
复制
void main() {
  DateTime(2023, 6, 5).describe();
  DateTime(2023, 6, 8).describe();
  DateTime(2023, 6, 9).describe();
  DateTime(2023, 6, 12).describe();
}

--->[日志输出]----
2023/6/5 是 4 天前
2023/6/8 是 昨天
2023/6/9 是 今天
2023/6/12 是 3 天后

如下所示,对 DateTime 类进行拓展,添加了一个 describe 方法,用于处理输出逻辑。其中用到了 switch + 模式匹配的特性:

代码语言:javascript
复制
extension DescribeDate on DateTime {
  void describe() {
    DateTime now = DateTime.now();
    Duration diff = this.difference(DateTime(now.year, now.month, now.day));
    String result = switch (diff) {
      Duration(inDays: -1 ) => '昨天',
      Duration(inDays: 0 ) => '今天',
      Duration(inDays: 1 ) => '明天',
      Duration(inDays: int d) => d < 0 ? '${d.abs()} 天前' : '$d 天后',
    };
    print("$year/$month/$day 是 $result");
  }
}

一个很明显的特征是: switch 关键字的分支语句可以作为返回值。而且分支由 模式匹配 Patterns 进行创建,还不了解 Patterns 的朋友,可以转到上一篇 《 Dart 3.0 语法新特性 | 模式匹配 Patterns》 。 下面红框中的 => 左侧的部分是对普通对象的模式匹配,其中 inDays 是 Duration 的 get 方法,所以支持对 inDays 名称的模式匹配:


二、从 switch 来看 Patterns 的种类

在上一篇,我们了解了支持 Patterns 模式匹配的几种类型 (Type)。 但模式匹配并不仅止于此,下面将通过 switch 语句来看一下其他的 Patterns 的种类:


1. 常量与解构变量

在本篇一开始时就介绍了 switch 的分支语句是对 常量 的匹配, 既然可以被 switch 匹配,就可以视为 Patterns 模式, 这就不过多赘述了。

如下所示, foo2 中传入一个 dynamic 类型的值,在 switch 中可以进行匹配:在 case 中解构 value 值,这样就可以根据变量类型匹配,进入不同的分支进行处理:

代码语言:javascript
复制
void main(){
  foo2((1,1)); // 打印 int+int
  foo2((1,"hello")); // 打印 int+String
  foo2(5); // 打印 default
}

// 变量 Patterns
void foo2(dynamic value){
  switch (value) {
    case (int a, String b):
      print("int+String");
      break;
    case (int a, int b):
      print("int+int");
      break;
    default:
      print("default");
  }
}

2. 符号与关键字的 Patterns 模式匹配

除了类型之外,符号和某些关键字也可以对若干个子模式进行连接,形成新的 Patterns 模式。 这很像正则表达式,若干个子正则可以通过符号连接成新正则。 连接符包括:

逻辑运算符: 或 || 、 与 &&

代码语言:javascript
复制
int age = 12;
var isAllow = switch (age) {
  16 || 17 || 18 => true,
  _ => false,
};

关系运算符: > 、>= 、< 、<= 、 == 、!=

代码语言:javascript
复制
int score = 69;
var info = switch (score) {
>=40 &amp;&amp; < 60 => 'D',
== 100 => 'A+',
>= 90 &amp;&amp; < 100 => 'A',
>= 80 &amp;&amp; < 90 => 'B',
>= 70 &amp;&amp; < 80 => 'C',
_ => 'E',
};

注: _ 可以表示其他未匹配的情况,相当于 default 分支的作用。


强制类型转换 asobject?object!

这三者简单了解一下,也可以作为 Patterns 加入匹配规则体系:

代码语言:javascript
复制
(num, Object) record = (1, 's');
var (i as int, s as String) = record;
print("========($i,$s)=========");

switch (value) {
  case var s?:
    print('s 非空类型:$s');
  default:
    print('default');
}

(int?, int?) position = (2, 3);
var (x!, y!) = position;
print("========($x,$y)=========");

三、从 switch 和 Patterns 为我们带来了什么

可能很多人看着 Patterns 匹配感觉很迷茫,这是什么玩意,感觉花里胡哨,感觉挺厉害又没什么大用的样子。这可能是你并没有明白 Patterns 是干什么用的,简单来说 Patterns 是定义了一套语法级的匹配规则。

下面拿出介绍密封类时的那个小案例,理解一些: 登陆界面的认证状态 AuthState,有如下三种子状态

  • 认证中 AuthLoading
  • 认证成功 AuthSuccess
  • 认证失败 AuthFailure
代码语言:javascript
复制
sealed class AuthState{} //创建密封类

class AuthLoading extends AuthState{}

class AuthSuccess extends AuthState{
  final String user;
  final String token;

  AuthSuccess(this.user, this.token);
}

class AuthFailure extends AuthState{
  final String error;
  AuthFailure(this.error);
}

如下是基于 AuthState 对象构建界面的逻辑:其中使用了 switch 进行匹配,并将其作为返回值;每个分支的左侧是 Patterns ,当 state 对象匹配时进入对应分支返回结果。

注意: 这里的 AuthLoading() 并不是构造对象,而是一般类型的 Patterns 语法。匹配到对应类型,就可以访问对应子类型的数据。

代码语言:javascript
复制
String buildByAuthState(AuthState state){
  return switch(state){
    AuthLoading()=> 'AuthLoading View',
    AuthSuccess()=> 'AuthSuccess View:${state.user}',
    AuthFailure()=> 'AuthFailure View:${state.error}',
  };
}

如下也可以通过 变量 Patterns 进行匹配:

代码语言:javascript
复制
String buildByAuthState3(AuthState state){
  return switch(state){
  AuthLoading loading => 'AuthLoading View',
  AuthSuccess success => 'AuthSuccess View:${success.user}',
  AuthFailure fail => 'AuthFailure View:${fail.error}',
 };
}

或者将变量通过 _ 进行匿名处理:

代码语言:javascript
复制
String buildByAuthState1(AuthState state){
  return switch(state){
  AuthLoading _ => 'AuthLoading View',
  AuthSuccess _ => 'AuthSuccess View:${state.user}',
  AuthFailure _ => 'AuthFailure View:${state.error}',
 };
}

就像不同的正则表达式,可以完成相同的匹配目的。合理地运用匹配规则,可以方便我们便捷地处理逻辑。否则上面的代码逻辑就需要用 if 分支语句处理,会增加很多无意义的代码。所以面对一个新事物,应该去思考它存在的必要性,是为了解决什么问题而出现的,而不是拘泥于语法点,而死记硬背。

Dart 3.0 的新语法点就介绍差不多了,这里整理一下相关文章。那本文就到这了。谢谢观看~

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-06-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • theme: cyanosis
    • 一、 重新审视 switch 关键字
      • 二、从 switch 来看 Patterns 的种类
        • 三、从 switch 和 Patterns 为我们带来了什么
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档