Flutter 中由 BuildContext 引發的血案

今天和各位分享一個博主在實際開發中遇到的問題,以及解決方法。廢話不多說,我們先來看需求:
我們要做一個iOS風格的底部菜單彈出組件,具體涉及showCupertinoModalPopup()方法,該方法被執行后,會出現如下圖類似所示的菜單彈出視圖:

相信這個彈出菜單視圖都有見過吧?下面重點來了:在本次的項目需求中,該視圖的選項文字是由Server端返回的。也就是說,這些選項的內容和個數都不固定,因此不能將其在代碼中寫固定值。
為了簡化代碼以突出重點,下面放上我在一開始的實現方案:

  openActionSheet() {
    List<Widget> menuWidgets = new List();
    menuItems.forEach((element) {
      menuWidgets.add(CupertinoActionSheetAction(
        child: Text(element),
        onPressed: () {
          Navigator.pop(context);
          debugPrint("操作$element被執行“);
        },
        isDefaultAction: true,
      ));
    });

    showCupertinoModalPopup(
        context: context,
        builder: (buildContext) {
          return CupertinoActionSheet(
              title: Text('測試菜單'),
              message: Text('點擊菜單項試試吧!'),
              actions: menuWidgets);
        });
  }

如上述代碼所示,openActionSheet()是显示該組件的方法。其中,showCupertinoModalPopup()為Flutter SDK內置方法,其作用即显示這個組件;再其上面的循環以及List聲明、賦值等操作實際上就是在動態添加菜單項。menuItems類型是List<String>。
通過對代碼的解釋,相信大家能夠一目瞭然地看出,當某個菜單項被點擊時,整個菜單組件消失,並打印Debug Log(對應為真實項目要執行的操作)。
大家覺得上述代碼有問題嗎?如果有問題,問題在哪兒呢?
現在公布答案:這段代碼有問題!
上述代碼執行時,當用戶點擊菜單項后,其運行結果並非如我們預想的那樣:菜單組消失並輸出Log,而變成了:整個頁面被Pop,菜單組保留,並輸出Log!
這是什麼原因呢?
實際上,罪魁禍首就在我們循環遍歷賦值操作時的這條語句:

Navigator.pop(context);

這裏的context是整個頁面的BuildContext,而非菜單組的。這裏我們要明確一個概念——我們想Pop誰,一定要用誰的BuildContext對象。
在這裏,正確的BuildContext對象是誰呢?它在這裏:

showCupertinoModalPopup(
    context: context,
    builder: (buildContext) {
      return CupertinoActionSheet(
          title: Text('測試菜單'),
          message: Text('點擊菜單項試試吧!'),
          actions: menuWidgets);
    }
);

注意到了嗎?上面第三行括號里的buildContext才是我們真正要用的對象。因此,正確的做法是什麼呢?

  openActionSheet() {
    BuildContext tempContext;
    List<Widget> menuWidgets = new List();
    menuItems.forEach((element) {
      menuWidgets.add(CupertinoActionSheetAction(
        child: Text(element),
        onPressed: () {
          Navigator.pop(tempContext);
          debugPrint("操作$element被執行");
        },
        isDefaultAction: true,
      ));
    });

    showCupertinoModalPopup(
        context: context,
        builder: (buildContext) {
          tempContext = buildContext;
          return CupertinoActionSheet(
              title: Text('測試菜單'),
              message: Text('點擊菜單項試試吧!'),
              actions: menuWidgets);
        });
  }

如上所示,我們只需將正確的對象“帶”到其作用域外面就可以了。
好了,這就是本篇文章的全部內容,希望能夠對你有所幫助!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

反聖嬰現象形成 颶風、野火恐更活躍

摘錄自2020年9月14日聯合報報導

國家海洋暨大氣管理局(NOAA)11日宣布,「反聖嬰現象」(La Nia,又稱拉尼娜現象)已然形成;此現象將影響颶風季節的剩餘時間,並可能至少持續整個冬季,直到春季才回到略為放鬆的「中性」狀態。

拉尼娜(La Nia)與聖嬰現象(El Nio)相反。反聖嬰現象讓赤道熱帶太平洋的海水異常變冷,可能影響太平洋以外的天氣模式。由於預料反聖嬰現象到來,NOAA得以在8月初預報「極端活躍」的颶風季節,颶風表現可能更為激烈。

NOAA氣象預報員表示,反聖嬰現象有75%可能性會停留整個冬季。讓太平洋西北地區、北部平原,大湖區和新英格蘭北部地區的降雪機率,高於平均水平。大西洋中部及南部、南部/中部平原和西南部的降雪,則可能更加稀少。

反聖嬰現象即將到來,對中部和南加州來說是個壞消息,那裡海拔較高的積雪可能減少、冬季風暴減少,可能延長今年的火災季節。當地的野火嚴重程度近來已創下紀錄。

氣候變遷
國際新聞
反聖嬰
颶風
野火

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

動不動就賣4~5萬輛!是價格的淪喪還是省油省的扭曲?

昂科威和途觀依然堅挺,果然昂科威換9AT之後實力大增。轎車方面,卡羅拉一舉奪得冠軍,朗逸依舊是很隨緣的調性,這次沒能拿下第一,估計是在憋大招,英朗出乎意料跌至第九,至於兄弟車型威朗相隔幾名了,寶駿310開年攻勢並不猛,不過依然力壓pOLO。

持續幾天的陰冷潮濕天氣之後

燦爛的太陽公公終於在周五齣現啦~

今天也是陽光明媚的~

(廣州這邊的天氣,其他地方就不知道啦)

周末終於就能好好去浪了~

而周六當然叫上朋友,去嗨、去玩、去飛啦~

當然如果你不想到外面去浪

最好是刷刷朋友圈、微博了

這两天的朋友圈在刷什麼呢?看看就知道了啦~

新年第一個月,各個領域的銷量也是頗有看點的,SUV方面,寶駿510與冠軍失之交臂,只差一千多輛的銷量,不過這樣的情況好幾次都這樣已經不算什麼新鮮事了,合資大熱門CR-V和奇駿爆冷跌出前十,原因嘛,咳咳……昂科威和途觀依然堅挺,果然昂科威換9AT之後實力大增。

轎車方面,卡羅拉一舉奪得冠軍,朗逸依舊是很隨緣的調性,這次沒能拿下第一,估計是在憋大招,英朗出乎意料跌至第九,至於兄弟車型威朗相隔幾名了,寶駿310開年攻勢並不猛,不過依然力壓pOLO。

豪華車方面,Q5雖然面臨換代,卻依舊霸着榜首不讓位,實力依舊強勁,可以說是佔著茅坑又拉屎,這泡屎拉得夠久了,凱迪拉克XT5重新回歸萬輛俱樂部,進入第三,實力也是不可小覷,優惠大,用料豪華,美式豪華值得信賴。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

C Primer Plus(三)

重讀C Primer Plus ,查漏補缺

  重讀C Primer Plus,記錄遺漏的、未掌握的、不清楚的知識點

 

文件輸入/輸出

  1、fgets函數在讀取文件內容時會將換行符讀入,但gets不會,fputs函數在寫入文件時不會追加一個換行符,但puts會,應該對應配合使用。

  2、不同操作系統下,以文本方式打開文件,幾乎沒有區別,但由於不同操作系統文件結尾的的標識符不同,以二進制方式打開時,可能會將結尾標識符錯誤輸出。

  3、對於大型文件,有兩個特殊的函數提供支持:

1 int fgetpos(FILE * restrict stream, fpos_t * restrict pos);
2 int fsetpos(FILE * stream, const fpos_t *pos);

  其中,fpos_t是通過其他類型定義的文件定位類型,在使用上述函數時,fsetpos中的pos必須是通過fgetpos函數獲得的。當兩個函數執行成功時,會返回0。

  4、其他標準IO函數

 1 size_t fwrite(const void * restrict ptr, size_t size, size_t nmemb, FILE* restrict fp);
 2 size_t fread(void * restrict ptr, size_t size, size_t nmemb, FILE* restrict fp);
 3 // 是否到達文件結尾
 4 int feof(FILE* fp);
 5 // 是否發生讀寫錯誤
 6 int ferror(FILE* fp);
 7 // 將字符迴流進緩衝區
 8 int ungetc(int c, FILE* fp)
 9 // 立刻將緩衝區內容寫入文件
10 int fflush(FILE* fp)
11 // 替換緩衝區
12 int setvbuf(FILE* restrict fp, char * restrict buf, int mode, size_t size)

  當然,上述的一些函數在目前的VS Studio中會被認為是不安全的函數,已經過時。

  

結構和其他數據格式

   5、C99標準下支持對結構體初始化時的任意字段賦值:

1 struct book gift = {.value=25.99, .author="Harry Potter", .title="Yoo"};
2 // 此時 0.25 會被賦給定義結構體時author后的那個成員,即便那個成員已經被初始化過。
3 struct book gift = {.value=25.99, .author="Harry Potter", 0.25};

  6、對於結構體數組,數組名不是其首個元素的地址,需要引用首個元素再取地址。

  7、在結構中一般使用字符數組,而不使用字符指針,結構中的字符指針無法很好的初始化地址,這樣會有使用上的風險,所以結構中的字符指針最好只指向那些字符串常量或者是指向由malloc分配的內存。

  8、C99標準對結構也支持複合文字,同時複合文字的結構也可以作為函數參數,也可以取地址,也和普通變量有相同的生存周期,聲明方式如下:

1 (struct book) {"The Idiot", "Fyodor Dostoyevsky", 6.99}

  9、C99支持一種伸縮型數組成員,這個成員必須是結構中最後一個成員,而且不是唯一一個成員,就像聲明普通數組一樣,但括號內為空,這個成員不會在聲明后立即存在,實際上,C99希望使用malloc為這樣含有伸縮型成員的數組分配空間。

1 struct flex{
2     int count;
3     double avreage;
4     double scores[]; // 伸縮型成員
5 }
6 struct flex * pf;
7 pf = malloc(sizeof(struct flex) + 5*sizeof(double))
8 pf->count = 5;
9 pf->scores[2] = 2.99;

  10、對於C中的枚舉類型,某些屬性不能順延至C++,例如C允許對枚舉做++運算,但C++不允許。

1 enum spectrum {red, yellow, green, blue};
2 spectrum color;
3 for(color = red; color != blue; color++);

  11、在C中,對於同一作用域下的標記和變量名可以使用同一個名字,因為對於標記(枚舉、結構,聯合),他們使用的名字空間與普通變量不同,但C++中不可以,例:

1 struct complex{double x, double y};
2 int complex; // 在C中不會引起衝突,但C++中則不允許

  12、對於函數指針執行函數時,會出現兩種語法,ANSI C把他們視為等價的。

1 void ToUpper(char *);
2 void (*pf) (char*);
3 char str[] = "hello";
4 pf = ToUpper;
5 (*pf)(str); // 語法1
6 pf(str);    // 語法2

  

位操作

  13、為什麼一個字節可以表示的有符號整數的範圍是-128~+127?

  看這裏:https://www.cnblogs.com/Dylan7/p/12649972.html

  14、計算機中小數是如何表示的?(一部分表示指數,一部分表示小數,有精確度問題)

  15、對位進行操作的第二種方法就是位字段(從沒用過,細節可以用到時再研究),位字段好比一個結構體,但其中的成員,代表的是某幾位上的值,好處是避免了通過複雜的位運算去控制某些位上的值,聲明例如:

1 struct box
2 {
3     unsigned int opaque       :1 // 整體結構的對齊補齊依據無符號整型
4     unsigned int fill_color   :3 // 数字代表需要幾位來表示這個字段
5     unsigned int              :4 // 可以跳過一些位
6     unsigned int show_border  :1 // 但一個字段不能橫跨兩個無符號整型的邊界
7 }
8 struct box b;
9 b.fill_color = 7; // 不可以超過字段所佔用的位可表示的上限

  

C預處理器和C庫

  16、程序翻譯的第一步,在預處理前,編譯器會對代碼做一些翻譯,將代碼中出現的字符映射到源字符集(用來處理多字節字符和使C外觀更加國際化的三元字符擴展),接着查找反斜杠后緊跟換行符的實例,將其轉換為一行,然後將文本劃分為預處理的語言符號序列以及空白字符及註釋序列(將用一個空格代替一個註釋),最後進入預處理階段,尋找每一個預處理指令。

  17、 幾個宏定義

1 #define F(x) #x      // #將語言符號字符串化
2 #define F(x) F##x    // ##將兩個語言符號組成一個語言符號
3 #define F(x,...)  printf("x", __VA_ARGS__)  // ...和__VA_ARGS__,可變參數(必須為最後一個參數)

  18、#if 指令後面跟常量整數表達式,可以與 #elif 配合使用,例如:

1 #if 1 == SYS
2     ...
3 #elif 2 == SYS
4     ...
5 #endif    

  同時,還有以下新的實現方式,defined 是一個預處理運算符,如果參數使用#define定義過,defined返回1,否則返回0。

1 #if defined(INMPC)
2     ...
3 #elif defined(VAX)
4     ...
5 #endif   

  19、#line 用於重置__LINE__,__FILE__宏所報告的行數

    #error 指令使預處理器可以發出一條錯誤信息

1 #line 10000
2 #line 10 cool.c"
3 #if __STD_VERSION__ != 199901L
4     #error Not C99
5 #endif

  20、C99 提供了_Pragma預處理器運算符,可以將字符串轉換成常規的編譯指示

1 _Pragma("c99 on") 等價於
2 #pragma c99 on

  21、內聯函數不會在調試器中显示,例如使用gdb調試時,有些內聯函數無法被手動執行,同時內聯函數具有內部鏈接屬性,所以在多文件程序中,使用其他文件的內聯函數時,要單獨聲明一次,並且在嘗試獲 取內聯函數的地址時,編譯器都會產生非內聯函數,也就是說可能產生外部定義的函數。

  23、在main()函數結束時,會隱式地調用exit()函數,同時,可以通過atexit()函數,向exit()註冊在程序允許結束時執行的函數,ANSI C保證可以設置至少32個函數,按照先設置后執行的順序執行,atexit()函數接受一個返回值為void,參數也為void的函數指針作為唯一參數。

  24、memcpy()與memmove()兩個函數的區別在於聲明上,以及memcpy()會假定兩個內存區沒有重疊。

1 void *memcpy(void * restrict s1, const void * restrict s2, size_t n);
2 void *memmove(void *s1, void *s2, size_t n);

  25、可變參數的相關內容包含在stdarg.h頭文件中,使用起來比較複雜,包括初始化可變參數列表,遍歷列表,清理列表,拷貝列表等一系列操作,需要時再研究。

 

高級數據表示

  26、 這章沒什麼新奇內容,但它告訴我們,用C可以實現很多複雜的數據結構。

 

 

  2020年4月16日,星期五,晚23點09分,首次完整讀完這本書,共勉。

  學如逆水行舟,不進則退;心似平原放馬,易縱難收。

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

亂棄口罩掀生態災難 企鵝誤吞N95口罩伏屍巴西沙灘

摘錄自2020年9月25日蘋果新聞報導

近日巴西聖保羅有企鵝伏屍沙灘,經驗屍發現牠胃內藏有一個無法消化的N95口罩,當地保育組織斥人類不當棄置廢物會導致海洋動物傷亡。

當地海洋保育組織Instituto Argonauta解剖時發現,企鵝的胃部有一個完整的N95口罩,相信令企鵝無法消化致死。組織又指出,自4月起在聖保羅北部沿岸已撿到多達113個棄置口罩,當中包括企鵝伏屍的沙灘。組織斥企鵝之死反映不當棄置廢物可導致海洋動物傷亡。

世界自然基金會(WWF)在7月亦警告過不當棄置口罩等個人防護設備(PPE),恐會對環境造成威脅。即使僅有1%的口罩未有妥善處理,已可導致每月有1000萬個棄置口罩流出大自然。

生物多樣性
國際新聞
巴西
企鵝
口罩
誤食

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

共同社:全球再生能源發電量 首次超越核能

摘錄自2020年9月27日聯合報報導

日本共同社報導,法國、日本和英國等研究團隊日前報告稱,全球再生能源發電量在去年首次超越核電核電。太陽能和風力發電增加,但核能因先進國家反應爐相繼報廢而明顯停滯,僅略超過去年發電量。

今年出版的世界核能產業現狀報告顯示,去年啟用的核能發電廠分別是俄國3座、中國大陸2座及南韓1座,發電量為2兆6570億度,較上年增加3.7%。今年上半年沒有核電廠啟動,法國、德國和美國等國去年決定關閉的核電廠有5座,今年上半年為3座。

報告還稱,涉及放射性物質發電的核能與其他發電方式相比,新冠肺炎疫情蔓延導致核電廠相關設備檢查延遲對安全性影響更大。

能源轉型
國際新聞
再生能源
核能

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

比哈弗H6強10倍,這5台車真的能上山下海!

威利斯可能你不知道,但給你看以下圖片你肯定恍然大悟。沒錯這就是二戰美軍的吉普車。而牧馬人則是其最純正的後裔沒有之一,你可能買不起路虎衛士,買不起奔馳G class,但你卻可以40多萬享受一台同樣B逼格滿滿的純種硬派越野。

雖然大家都喜歡SUV,但不少消費者心目中SUV就是越野車,這其實是一個錯誤的解讀,普通SUV跟越野車的通過能力還是有着非常巨大鴻溝,其最大的分別在於在車身結構:

承載式車身是一體式的概念,底盤跟發動機直接可以安裝在車身,這樣做優點在於重量輕,重心低,配合不同懸架可以得到極佳的舒適性能。一般用於轎車及通勤性質的SUV;

非載式車身是有車架的,車身、引擎、懸架都是作為零件安裝固定在車架上,這樣的車普遍重心較高難以擁有舒適性,整車也比較笨重,但相對的路面通過性及承載能力會更強,一般用於貨車,越野車上;

所以我們從車身結構上即可分辨出SUV跟越野車的分別,可以說硬派越野車都可以成為SUV,但不是所有SUV都能成為硬派越野! 下面我們來看看視頻感受下硬派越野的通過性吧!

看到視頻裏面普拉多逆天的通過能力!你是不是也很想擁有一台屬於自己能隨意闖蕩,遨遊大地的”硬派越野”呢?!接下來就跟你們推薦幾部吧!

人到山前必有路,有路必有豐田車!說的就是當年名聲在外的陸地巡洋艦(LAND CRUISER)!很多人認為LAND CRUISER是酷路澤與普拉多沒有關係,其實不然,普拉多跟FJ酷路澤都屬LAND CRUISER系列細分市場的產品,所以我們也能在普拉多車尾看見LAND CRUISER的字樣。而剛剛視頻的主角也正是普拉多!而且它的價格也算是親民!

威利斯可能你不知道,但給你看以下圖片你肯定恍然大悟;

沒錯這就是二戰美軍的吉普車!而牧馬人則是其最純正的後裔沒有之一,你可能買不起路虎衛士,買不起奔馳G class,但你卻可以40多萬享受一台同樣B逼格滿滿的純種硬派越野!而且它擁有着逆天越野能力,無論你攀山涉水,上天下地,它都能氣定神閑的輕鬆征服!

沙漠之王途樂的威名不是吹出來的,自1951年誕生開始,途樂67年來十年如一日地肩負起的尼桑越野大哥的地位!

其強悍的越野實力,且兼顧豪華舒適更一直都備受好評!

英菲尼迪QX80的出現與途樂上演了一出本是同根生相煎何太急的好戲!因為它們都是同平台產品,他們之間越野能力眾說紛紜,都在伯仲之間,但事實對比之下途樂是更加偏向越野;

而QX80則更靠攏豪華,其實第一眼從它奢華霸氣的外觀便可以看出!

這位享譽盛名的“黑武士”在全世界範圍內獲獎無數,是地球上最強的全地形越野車之一,在此列舉一下選它為座駕的世界名宿:納粹黨首希特勒、羅馬教皇、俄國前總統恭弘=叶 恭弘利欽、東正教教皇阿列克謝二世、沙特酋長穆罕默德·勒沙·普哈列維,這些無一不是影響世界發展歷史的重要人物;

奔馳G Class誕生至今核心追求從未改變,就是“功能決定形式”的設計理念!同時把舒適性與越野功能結合得天衣無縫!

結語

中國人那麼喜愛SUV,是有道理的!認為SUV確實是好東西,不論是讓你共享天倫的城市道路SUV還是今天的主角們“硬派越野”都在他們的領域作出巨大的貢獻,也希望各位能得到自己稱心滿意的產品!本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

波蘭首次喊出停止產煤時程 2049年煤礦走入歷史 工會也點頭

環境資訊中心綜合外電;姜唯 編譯;林大利 審校

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

口述歷史與訪談 看見女性環境倡議者身處疫情下的困境

環境資訊中心綜合外電;黃鈺婷 翻譯;林大利 審校;稿源:Mongabay

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

C# 9.0 新特性之參數非空檢查簡化

閱讀本文大概需要 1.5 分鐘。

參數非空檢查是縮寫類庫很常見的操作,在一個方法中要求參數不能為空,否則拋出相應的異常。比如:

public static string HashPassword(string password)
{
    if(password is null)
    {
        throw new ArgumentNullException(nameof(password));
    }
    ...
}

當異常發生時,調用者很容易知道是什麼問題。如果不加這個檢查,可能就會由系統拋出未將對象引用為實例之類的錯誤,這不利於調用者診斷錯誤。

由於這個場景太常見了,於是我經常在我的項目中通過一個輔助類來做此類檢查。這個類用來檢查方法參數,所以命名為 Guard,主要代碼如下:

public static class Guard
{
    public static void NotNull(object param, string paramName)
    {
        if (param is null)
        {
            throw new ArgumentNullException(paramName);
        }
    }

    public static void NotNullOrEmpty(string param, string paramName)
    {
        NotNull(param, paramName);
        if (param == string.Empty)
        {
            throw new ArgumentException($"The string can not be empty.", paramName);
        }
    }

    public static void NotNullOrEmpty<T>(IEnumerable<T> param, string paramName)
    {
        NotNull(param, paramName);
        if (param.Count() == 0)
        {
            throw new ArgumentException("The collection can not be empty.", paramName);
        }
    }
    ...
}

這個類包含了三個常見的非空檢查,包括 null、空字符串、空集合的檢查。使用示例:

public static string HashPassword(string password)
{
    Guard.NotNull(password, nameof(password));
    ...
}

public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector)
{
    Guard.NotNullOrEmpty(source, nameof(source));
    ...
}

介於這種非空檢查極其常見,C# 9.0 對此做了簡化,增加了操作符‘!’,放在參數名後面,表示此參數不接受 null 值。使用方式如下:

public static string HashPassword(string password!)
{
    ...
}

簡化了很多有木有。這個提案已經納入 C# 9.0 的特性中,但目前(2020-06-13)還沒有完成開發。

這個特性只支持非 null 檢查,其它參數檢查場景還是不夠用的,我還是會通過輔助類來進行像空字符串、空集合的檢查。

這個特性在寫公共類庫的時候很有用,但我想大多數人在寫業務邏輯代碼的時候可能用不到這個特性,一般會封自己的參數檢查機制。比如,我在項目中,對於上層 API 開發,我通過封裝一個輔助類(ApiGuard)來對對參數進行檢查,如果參數不通過,則拋出相應的業務異常,而不是 ArgumentNullException。比如下面的一段截取自我的 GeekGist 小項目的代碼:

public static class ApiGuard
{
    public static void EnsureNotNull(object param, string paramName)
    {
        if (param == null) throw new BadRequestException($"{paramName} can not be null.");
    }

    public static void EnsureNotEmpty<T>(IEnumerable<T> collection, string paramName)
    {
        if (collection == null || collection.Count() == 0)
            throw new BadRequestException($"{paramName} can not be null or empty.");
    }

    public static void EnsureExist(object value, string message = "Not found")
    {
        if (value == null) throw new BadRequestException(message);
    }

    public static void EnsureNotExist(object value, string message = "Already existed")
    {
        if (value != null) throw new BadRequestException(message);
    }
    ...
}

使用示例:

public async Task UpdateAsync(long id, BookUpdateDto dto)
{
    ApiGuard.EnsureNotNull(dto, nameof(dto));
    ApiGuard.EnsureNotEmpty(dto.TagValues, nameof(dto.TagValues));

    var book = await DbSet
        .Include(x => x.BookTags)
        .FirstOrDefaultAsync(x => x.Id == id);
    ApiGuard.EnsureExist(book);

    Mapper.Map(dto, book);

    ...
}

ApiGuard 的好處是,當 API 接口接到不合要求的參數時,可以自定義響應返回內容。比如,增加一個 Filter 或中間件用來全局捕獲業務代碼異常,根據不同的異常返回給前端不同的狀態碼和消息提示:

private Task HandleExceptionAsync(HttpContext context, Exception exception)
{
    ApiResult result;
    if (exception is BadRequestException)
    {
        result = ApiResult.Error(exception.Message, 400);
    }
    else if (exception is NotFoundException)
    {
        message = string.IsNullOrEmpty(message) ? "Not Found" : message;
        result = ApiResult.Error(message, 404);
    }
    else if (exception is UnauthorizedAccessException)
    {
        message = string.IsNullOrEmpty(message) ? "Unauthorized" : message;
        result = ApiResult.Error(message, 401);
    }
    ...
}

只是一個參數非空檢查,在實際開發中卻有不少的學問,所以學好了理論還要多實踐才能更透徹的理解它。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化