dart入門潛修基礎(chǔ)篇之控制流語句

本文收錄于dart入門潛修系列教程豌鸡。

創(chuàng)作不易段标,轉(zhuǎn)載還請(qǐng)備注涯冠。

控制流語句

所謂控制流語句就是能夠改變程序執(zhí)行流程的語句逼庞,比如if else、switch赛糟、for、assert語句等等璧南,dart為我們提供了和其他語言一樣豐富的控制流語法吨瞎,羅列如下:
—if else: 條件判斷語句
—for循環(huán):循環(huán)遍歷語句
—while 和 do-while語句: 循環(huán)遍歷語句
—break:跳出當(dāng)前循環(huán)體
—continue:跳過循環(huán)中的本次執(zhí)行
—switch 和 case: 條件匹配語句
—assert:斷言
下面一一來看一下上面的語句的用法穆咐。

if-else

dart中的if-else寫法同其他語言一致,但是需要注意的是对湃,只有boolean值才能作為if-else中的條件!if-else示例如下:

void main() {
  int i = 1;
  if (i > 0) {
    print("i > 0");
  } else if (i < 0) {
    print("i < 0");
  } else {
    print("i == 0");
  }
}

我們無法像下面這樣使用if-else語句:

void main() {
  int i = 0;
  if (!i) {//這里i不是boolean值拍柒,僅僅是個(gè)對(duì)象,所以不能這么使用
    print(i);
  }
}

for

for是用于迭代遍歷的語句拆讯,典型的場(chǎng)景是用于遍歷lists养叛。示例如下:

  var lists = [1, 2, 3];
  for (int i = 0; i < lists.length; i++) {
    print(i);//打印'1 2 3'
  }

注意,for循環(huán)中的變量i的作用域就只在for循環(huán)體內(nèi)部可以訪問弃甥,無法像javascript那樣可以在外部訪問,也就是說dart嚴(yán)格限制了作用域淆攻,這有助于避免一些未知錯(cuò)誤。

dart還提供了forEach方法嘿架,該方法可以用于遍歷具有迭代屬性的對(duì)象,來看個(gè)例子:

class Person {//聲明了一個(gè)Person類
  String name;
}
//測(cè)試方法
void main() {
//生成3個(gè)Person對(duì)象
  Person p1 = Person();
  Person p2 = Person();
  Person p3 = Person();

  p1.name="張三";
  p2.name="李四";
  p3.name="王五";
//列表顯然具備迭代屬性
  var lists = [p1, p2, p3];
//這里我們采用forEach方法遍歷lists中的元素耸彪,并打印name值
  lists.forEach((person) => print(person.name));//打印 '張三 李四 王五'
}

此外,對(duì)于可迭代的對(duì)象蝉娜,dart還提供了“增加for循環(huán)”for-in,如下所示:

  var lists = [1, 2, 3];//lists是一個(gè)可迭代的對(duì)象
  for(var item in lists){//可以用for in循環(huán)進(jìn)行遍歷
    print(item);//打印'1 2 3'
  }

上面多次提到可迭代的對(duì)象蜀肘,那么什么是可迭代的對(duì)象稽屏?在dart中,可迭代對(duì)象就是指實(shí)現(xiàn)了Iterable類狐榔,并提供Iterator迭代器的對(duì)象。下面我們來闡述下如何定義自己的可迭代對(duì)象薄腻。由于這些涉及到面向?qū)ο蟛糠值闹R(shí)收捣,如果看不太明白庵楷,可以暫時(shí)略過。

import 'dart:collection';
//定義了一個(gè)Person類尽纽,這個(gè)是我們要遍歷的元素類型
class Person {
  String name;
  Person(String name){
    this.name = name;
  }
}
//我們需要提供一個(gè)迭代器,用于遍歷Person弄贿,我們必須實(shí)現(xiàn)該接口中定義的“抽象元素”
class PersonIterator implements Iterator<Person> {
//簡(jiǎn)單起見,這里并沒有提供對(duì)外設(shè)置入口差凹,暫時(shí)寫死
  var lists = [Person("張三"), Person("李四")];
//元素其實(shí)索引
  var index = -1;
//必須實(shí)現(xiàn)Iterator接口中的current成員的getter方法
//用于返回當(dāng)前遍歷的元素對(duì)象
  get current => lists[index];
//必須實(shí)現(xiàn)moveNext方法
  bool moveNext() {
    index++;
    return index < lists.length;
  }
}
//實(shí)現(xiàn)IterableBase侧馅,IterableBase實(shí)現(xiàn)了Iterable抽象類
class Persons extends IterableBase<Person> {
//這里我們需要提供一個(gè)迭代器,for-in遍歷的時(shí)候會(huì)獲取該迭代器
  @override
  final Iterator<Person> iterator = PersonIterator();
}
//測(cè)試方法
void main() {
//我們可以使用for-in來遍歷Persons類型的對(duì)象馁痴。
  for(var person in Persons()){
    print(person.name);//打印'張三 李四'
  }
}

上面簡(jiǎn)單演示了如何自定義一個(gè)具有迭代屬性的對(duì)象,同時(shí)也演示了dart中迭代器中用法弥搞。對(duì)于上述代碼需要注意以下幾點(diǎn)點(diǎn):

  1. 迭代類必須要提供一個(gè)迭代器(本例中即PersonIterator),這個(gè)迭代器同時(shí)需要實(shí)現(xiàn)兩個(gè)方法渠旁,一個(gè)是表示當(dāng)前元素current的getter方法(即get current => lists[i];),另一個(gè)則是尋找下一個(gè)元素并確認(rèn)是否還有剩余元素的moveNext方法顾腊。

  2. 對(duì)于迭代器的實(shí)現(xiàn)須遵循以下步驟:首先,迭代器的默認(rèn)指向位置位于第一個(gè)元素之前杂靶,所以我們定義了var index = -1,表示初始化索引為第一個(gè)元素的前一個(gè)元素(其實(shí)就是不存在)吗垮。其次,moveNext方法的含義是烁登,首先尋找下一個(gè)元素,并返回是否還有下一個(gè)元素的判斷饵沧,如果有則返回true,否則返回false狼牺。所以,這里我們先對(duì)moveNext進(jìn)行了index++運(yùn)算是钥,然后返回是否還存在該元素的判斷掠归。最后悄泥,current的getter方法返回的就是當(dāng)前元素值,所以我們只需簡(jiǎn)單返回即可码泞。

  3. 最后,我們需要實(shí)現(xiàn)抽象類Iterable,這個(gè)類包含了forEach方法领铐,可用于forEach遍歷。這里我們采用的是繼承IterableBase的方式绪撵,因?yàn)镮terableBase本身繼承了自Iterable類。

當(dāng)然音诈,這個(gè)迭代器只是演示案例,沒有任何擴(kuò)展性细溅,如果想實(shí)現(xiàn)類似于系統(tǒng)庫list那樣具有高擴(kuò)展性、健壯性的迭代機(jī)制喇聊,還需要做一些工作恍风。這里只需明白其原理即可誓篱。

while和do-while

這兩條也是存在于眾多語言中的遍歷語句,while是先判斷條件再?zèng)Q定是否執(zhí)行循環(huán)體窜骄,而do-while則是先執(zhí)行一次循環(huán)體,然后結(jié)合條件判斷決定是否繼續(xù)執(zhí)行下一次循環(huán)體邻遏,二者示例如下:

//定義了一個(gè)整型變量i,其值為2
  int i = 2;
//while循環(huán)遍歷党远,這里顯然i不小于2,所以while
//循環(huán)體根本不會(huì)執(zhí)行
  while (i < 2) {
    print(i++);
  }
//do-while循環(huán)沟娱,因?yàn)橄葓?zhí)行一次while循環(huán)體,
//然后再判斷i是否小于2济似,所以會(huì)打印 2 
  do {
    print(i++);
  } while (i < 2);

break和continue

break和continue都是用于改變代碼執(zhí)行流程的語句,break用于跳出當(dāng)前循環(huán)體砰蠢,而continue則是跳過循環(huán)體的當(dāng)前執(zhí)行,繼續(xù)執(zhí)行下一次循環(huán)台舱,示例如下:

  var lists = [1, 3, 2, 3];//定義了一個(gè)lists
//break示例,這里當(dāng)遇到lists中的偶數(shù)元素即停止打印
  for (int i = 0; i < lists.length; i++) {
    if (lists[i] % 2 == 0) {
      break;
    }
    print(lists[i]);//打印'1 3'
  }
//continue示例竞惋,這里的意思是跳過lists中的偶數(shù)元素
  for (int i = 0; i < lists.length; i++) {
    if (lists[i] % 2 == 0) {
      continue;
    }
    print(lists[i]);//打印'1 3 3'

當(dāng)然對(duì)于可迭代對(duì)象柜去,我們還可以利用其提供的便利方法來達(dá)到上面代碼的目的拆宛,如下所示:

//定義一個(gè)Person類
class Person {
  int age;
  String name;
  Person(String name, int age) {
    this.name = name;
    this.age = age;
  }
}
//測(cè)試方法main
void main() {
//包含有年齡不同的三個(gè)對(duì)象
  var lists = [Person("張三", 20), Person("李四", 21), Person("王五", 22)];
//我們可以使用where方法嗓奢,進(jìn)行條件判斷浑厚,
//這里我們過濾掉 age<= 21歲的Person
  lists.where((person) => person.age > 21)
      .forEach((person) => print(person.name));//打印'王五'
}

上面代碼用到了where方法,where的方法定義如下所示;

//where方法的定義
  Iterable<E> where(bool test(E element)) => new WhereIterable<E>(this, test);
//WhereIterable的定義
  WhereIterable(this._iterable, this._f);

由此可見钳幅,where實(shí)際上是結(jié)合了外部的判斷條件以及內(nèi)部的迭代器完成了條件過濾。

在dart內(nèi)置的list中贡这,where是以mixin的方式提供的(后面會(huì)有文章闡述mixin機(jī)制),位于ListMixin中盖矫,而ListMixin最終實(shí)現(xiàn)了可迭代的抽象類Iterable,在該類中定義了where方法辈双,所以任何實(shí)現(xiàn)迭代功能的對(duì)象责掏,都可以使用該方法進(jìn)行條件過濾湃望。比如我們同樣可以使用where方法來顧慮我們自己定義的可迭代對(duì)象,如下所示:

//Person類
  class Person {
  int age;
  String name;
  Person(String name, int age) {
    this.name = name;
    this.age = age;
  }
}
//我們自己實(shí)現(xiàn)的迭代器证芭,用于迭代Persons對(duì)象
class PersonIterator implements Iterator<Person> {
  var lists = [Person("張三", 22), Person("李四", 20), Person("王五", 22)];
  var index = -1;
  get current => lists[index];

  bool moveNext() {
    index++;
    return index < lists.length;
  }
}
//可迭代對(duì)象類Persons,
class Persons extends IterableBase<Person> {
  @override
  final Iterator<Person> iterator = PersonIterator();
}
//測(cè)試方法
void main() {
  var persons = Persons();
  persons.where((person) => (person.age > 21))
      .forEach((person) => print(person.name));//打印' 張三 王五'
}

switch和case

dart中的switch-case機(jī)制同其他語言一樣废士,都是作為條件匹配存在的語句,不過dart對(duì)switch-case做了一些健壯性控制官硝,先來看一個(gè)最基本的switch-case使用案例:

void main() {
//假設(shè)我們默認(rèn)有三種顏色,red氢架、yellow、green
  String color = "red";//可以通過改變此值來觀察不同的打印結(jié)果
//現(xiàn)在我們想根據(jù)不同的顏色做不同的事情
//就可以使用switch-case語句岖研,
  switch (color) {
    case "red":
      print("red");
      break;
    case "yellow":
      print("yellow");
      break;
    default:
      print("green");
  }
}

在使用switch-case的時(shí)候,必須要使用break及時(shí)終止剩余代碼的執(zhí)行,在有些語言中硬纤,如果不指定break,會(huì)繼續(xù)執(zhí)行下一個(gè)case語句筝家,直到遇到break或者default為止,而dart對(duì)其進(jìn)行了控制溪王,如下所示:

void main() {
  String color = "red";
  switch (color) {
    case "red":
      print("red");//!!!注意這里,我們沒有顯示使用break莹菱,dart編譯器會(huì)報(bào)錯(cuò)!!!
    case "yellow":
      print("yellow");
      break;
    default:
      print("green");
  }
}

但是移国,如果多個(gè)case確實(shí)對(duì)應(yīng)了同樣的執(zhí)行邏輯道伟,該怎么辦迹缀?

很簡(jiǎn)單蜜徽,dart很人性化的給我們考慮到了這種情況,只要case分支中拘鞋,沒有具體的執(zhí)行邏輯,那么dart是允許你共用同一個(gè)case邏輯 的盆色,比如下面的代碼:

void main() {
  String color = "red";
  switch (color) {
    case "red"://注意這里灰蛙,我們沒有任何實(shí)現(xiàn)
    case "yellow":
      print("red or yellow");
      break;
    default:
      print("green");
  }
}

上面代碼執(zhí)行過后隔躲,會(huì)打印red or yellow,這就是dart對(duì)switch-case所做的健壯處理宣旱。

但是,可能又有朋友說了响鹃,我現(xiàn)在的需求確實(shí)是要執(zhí)行兩個(gè)不同case下的不同邏輯驾霜!那么买置,此時(shí)該怎么做强霎?

其實(shí)這個(gè)需求很怪異忿项,因?yàn)閟witch-case的本意表達(dá)更多的是“單條件”匹配,而現(xiàn)在這種情況意思就是要多條件匹配轩触,然而dart針對(duì)這種情況還是提供了一種機(jī)制,即結(jié)合continue來做脱柱,示例如下:

void main() {
  String color = "red";//注意這里的匹配條件是red
  switch (color) {
    case "red"://匹配,按道理可以結(jié)束了
      print("red");
      continue anotherColor;//但是這里卻有個(gè)continue語句榨为,指向了anotherColor標(biāo)簽
    anotherColor://anotherColor標(biāo)簽實(shí)際上執(zhí)行了case "yellow"這個(gè)匹配case
    case "yellow":
      print("yellow");
      break;
    default:
      print("green");
  }
}

上面代碼執(zhí)行完后,打印如下:

red
yellow

這個(gè)也是dart語言中關(guān)于switch-case的一個(gè)特性随闺。

上面闡述了dart中switch-case的各種判斷場(chǎng)景,那么switch的入?yún)㈩愋投伎梢允悄男?/p>

dart中的swith支持多種常見的入?yún)㈩愋途乩郑热缯土渚洹⒆址⒑薄⒕幾g時(shí)常量、枚舉等等笨使。switch語句在進(jìn)行匹配的時(shí)候卿樱,會(huì)調(diào)用了相應(yīng)的==操作符來進(jìn)行匹配比較硫椰,當(dāng)然,前提是比較的兩個(gè)對(duì)象必須具備同一個(gè)類型(連子類型都不行)靶草。

assert

assert即斷言蹄胰,是很多語言都提供的一種檢測(cè)機(jī)制奕翔,它是用來判斷是否符合執(zhí)行邏輯的語句裕寨,示例如下:

void main() {
  String str = null;
  assert(str != null);
  print(str.length);
}

上面代碼會(huì)直接中斷執(zhí)行流程,拋出一個(gè)斷言異常派继,因?yàn)閟tr != null為非真值宾袜。如果此時(shí)str不為null,則程序會(huì)正常執(zhí)行驾窟。

我們會(huì)發(fā)現(xiàn)庆猫,很多時(shí)候在生產(chǎn)的代碼中都寫有大量的斷言,這個(gè)在不符合斷言條件的時(shí)候顯然也會(huì)拋出異常绅络,那為什么還要這么寫月培?實(shí)際上嘁字,斷言默認(rèn)只在開發(fā)環(huán)境中生效,發(fā)布到生產(chǎn)環(huán)境中的斷言是會(huì)被忽略的杉畜。因此纪蜒,斷言實(shí)際上起到了在開發(fā)環(huán)境調(diào)試程序、測(cè)試程序的目的此叠。

可能會(huì)有朋友說纯续,上面的代碼我不用assert不照樣拋出異常嗎?確實(shí)杆烁,不用的話也會(huì)拋出空指針這種異常寂恬!但是我們還是需要嘗試從兩個(gè)角度來理解斷言:1. 空指針異常實(shí)際上是被動(dòng)拋出的,是程序執(zhí)行過程中報(bào)出的錯(cuò)誤闰挡,更多的是給程序員看的举娩;而斷言則更多的是用戶側(cè)主動(dòng)定義的行為析校,除了程序員,測(cè)試開發(fā)人員也可以利用斷言機(jī)制來來測(cè)試系統(tǒng)铜涉,斷言可以清楚的給出我們異常的原因是什么智玻。2. 上面只是一個(gè)演示例子,實(shí)際上斷言處理的更多的是邏輯層面上的測(cè)試芙代,這個(gè)也是最重要的一點(diǎn)吊奢。比如下面代碼:

//這里提供一個(gè)極其簡(jiǎn)單的元轉(zhuǎn)分的實(shí)現(xiàn)(注意這里并沒有考慮溢出問題)
//僅僅是為了說明assert的用法
int yuan2fen(int yuan) {
//這里的入?yún)uan顯然可能為負(fù)數(shù),但這個(gè)不符合現(xiàn)實(shí)邏輯
//所以我們進(jìn)行了一層斷言纹烹,保證金額>0;
  assert(yuan >= 0, "金額錯(cuò)誤!");
  return yuan * 100;
}
//測(cè)試方法
void main() {
  print(yuan2fen(100));//正確页滚!打印'10000'
  print(yuan2fen(-1));//錯(cuò)誤铺呵!拋出異常'Failed assertion: line 15 pos 10: 'yuan >= 0': 金額錯(cuò)誤!'
}

上面代碼演示了斷言的一種應(yīng)用場(chǎng)景裹驰,如果沒有斷言,當(dāng)我們輸入的數(shù)字為負(fù)數(shù)時(shí)片挂,程序不會(huì)拋出任何異常,進(jìn)而會(huì)產(chǎn)生錯(cuò)誤的金額值音念。加上斷言后沪饺,則在開發(fā)、測(cè)試期間就能發(fā)現(xiàn)類似的錯(cuò)誤闷愤。

由上述代碼可知整葡,assert還可以接收第二個(gè)參數(shù):即錯(cuò)誤描述信息,用于在斷言不符合需求的情況下肝谭,提示給用戶的描述性文案掘宪。這樣可以很明了的知道發(fā)生了什么錯(cuò)誤。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末攘烛,一起剝皮案震驚了整個(gè)濱河市魏滚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌坟漱,老刑警劉巖鼠次,帶你破解...
    沈念sama閱讀 216,843評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異芋齿,居然都是意外死亡腥寇,警方通過查閱死者的電腦和手機(jī)觅捆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門赦役,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人栅炒,你說我怎么就攤上這事掂摔。” “怎么了赢赊?”我有些...
    開封第一講書人閱讀 163,187評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵乙漓,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我释移,道長(zhǎng)叭披,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,264評(píng)論 1 292
  • 正文 為了忘掉前任玩讳,我火速辦了婚禮涩蜘,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘熏纯。我一直安慰自己皱坛,他們只是感情好往扔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評(píng)論 6 390
  • 文/花漫 我一把揭開白布蝗罗。 她就那樣靜靜地躺著北苟,像睡著了一般闺骚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上乳绕,一...
    開封第一講書人閱讀 51,231評(píng)論 1 299
  • 那天菠发,我揣著相機(jī)與錄音,去河邊找鬼。 笑死楔敌,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播杨何,決...
    沈念sama閱讀 40,116評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蕊玷,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼贸诚!你這毒婦竟也來了运悲?” 一聲冷哼從身側(cè)響起鳖敷,我...
    開封第一講書人閱讀 38,945評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體呵恢,經(jīng)...
    沈念sama閱讀 45,367評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評(píng)論 2 333
  • 正文 我和宋清朗相戀三年本刽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,754評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡墙歪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,458評(píng)論 5 344
  • 正文 年R本政府宣布土居,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏上岗。R本人自食惡果不足惜呆瞻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評(píng)論 3 327
  • 文/蒙蒙 一丰泊、第九天 我趴在偏房一處隱蔽的房頂上張望亏推。 院中可真熱鬧绢掰,春花似錦、人聲如沸假丧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽逃糟。三九已至绰咽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間地粪,已是汗流浹背取募。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蟆技,地道東北人玩敏。 一個(gè)月前我還...
    沈念sama閱讀 47,797評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像质礼,于是被迫代替她去往敵國(guó)和親旺聚。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容