[C#]條件式小技巧

最近筆者看到了一篇文章介紹javascript如何寫出更好的條件判斷,發現其中的用法,自己一路走來也不自覺都用上了,這些方式並不侷限於javascript,本篇就用C#來示範,不僅讓可讀性變高,自己看起來也更順眼了。

  1. 用陣列是否包含取代逐一判斷
  2. 用map取代switch
  3. 提早return,減少分支
  4. 判斷全部符合或部分符合
  5. null條件運算子

用陣列是否包含取代逐一判斷

某次需要權限判斷時,因為系統不複雜,所以身分和權限是用常數定義在程式內,為了凸顯用陣列的好處,先用瑣碎的方式來判斷:

const int ADMIN = 1;
const int MANAGER = 2;
const int USER = 3;

const int ACTION_1 = 1;
const int ACTION_2 = 2;
const int ACTION_3 = 3;
const int ACTION_4 = 4;
const int ACTION_5 = 5;
const int ACTION_6 = 6;

//依據身分判斷動作是否允許
//原始作法,個別判斷
if(permission == USER){
  if(action == ACTION_4 || action == ACTION_5 || action == ACTION_6){
    //允許執行動作
  }
}

這方式用起來很瑣碎,要新增允許的動作時,也很難維護,改寫成陣列方式試試

//先將各身分的動作定義好
int[] ADMIN_ACTIONS = new int[]{ ACTION_1, ACTION_2, ACTION_3, ACTION_4, ACTION_5, ACTION_6 };
int[] MANAGER_ACTIONS = new int[]{ ACTION_2, ACTION_3, ACTION_4, ACTION_5, ACTION_6 };
int[] USER_ACTIONS = new int[]{ ACTION_4, ACTION_5, ACTION_6 };

//直接使用陣列方法是否包含
if(permission == USER && USER_ACTIONS.Contains(action)){
  //允許執行動作
}

修改後相對簡便許多,日後增加動作直接添加在陣列內就好了

用map取代switch

承上例,可以再進一步改進,連身分也不需要判斷,如果高權限包含低權限,可以進一步優化

//各身分的動作可以向前包含,並且增減不需要額外修改
int[] USER_ACTIONS = new int[] { ACTION_4, ACTION_5, ACTION_6 };
int[] MANAGER_ACTIONS = USER_ACTIONS.Concat(new int[] { ACTION_2, ACTION_3 }).ToArray();
int[] ADMIN_ACTIONS = MANAGER_ACTIONS.Concat(new int[] { ACTION_1 }).ToArray();

Dictionary<int, int[]> PERMISSION = new Dictionary<int, int[]>() {
  { ADMIN, ADMIN_ACTIONS },
  { MANAGER, MANAGER_ACTIONS },
  { USER, USER_ACTIONS },
};

if (PERMISSION[permission].Contains(action)) { 
  //允許執行動作
}

和一開始的例子做比較,可讀性和可維護性都大大提升了許多,當初能想到這樣改良,其實也是一個習慣,當你在做重複的動作時,就代表那些動作可以找出規律統整起來,多花點心思,就可以改善許多。

提早return,減少分支

在多層if判斷時,會長出許多if else,太多層時就難以讀取或維護,萬一未來更改條件時,改錯層,就埋下bug了

public string function HaveAdminPermission(){
  if(has_login){
    if(permission == ADMIN){
      if(action == ACTION_1){
        return "允許執行動作";
      }else{
        return "wrong action";
      }
    }else{
      return "not admin";
    }
  }else{
    return "not login";
  }
}

上方的例子,看起來像千層麵一樣,如果可以減少層數,那接手的人或者未來自己失憶時也比較快理解

public string function HaveAdminPermission(){
  if(!has_login){ return "not login"; }
  if(permission != ADMIN){ return "not admin"; }
  if(action != ACTION_1){ reutrn "wrong action"; }
  return "允許執行動作";
}

如此少了許多層的else,條件看起來清晰易懂,簡化成一層,維護性大提升~

判斷全部符合或部分符合

以往這類型的判斷,都是要使用迴圈,但C#的LINQ大大簡化了這部分

class Book 
{ 
  public string Name { get; set; } 
  public string Type { get; set; } 
}

var array = new Book[] {
  new Book(){Name = "a", Type = "novel"},
  new Book(){Name = "b", Type = "novel"},
  new Book(){Name = "c", Type = "novel"}
};

//是否包含名稱b
bool have_b = false;
for(var item in array){
  if(item.Name == "b"){
    have_b = true;
    break;
  }
}

//是否全部類型都是novel
var all_is_novel = true;
for(var item in array){
  if(item.Type != "novel"){
    all_is_novel = false;
  }
}

//改用LINQ判斷
have_b = array.Any(x => x.Name == "b");//True
all_is_novel = array.All(x => x.Type == "novel");//True

不得不說,現在寫C#,不用LINQ渾身不對勁,畢竟太方便了,一行搞定以前需要好幾行的事,但是要注意別走火入魔,真的沒法簡化的,或是破壞可讀性的,還是用傳統方式好。

null條件運算子

有時候null的判斷,真心煩人,不判斷又會有exception,但每次判斷又覺得做重複的事,這事早已經在C# 6.0之後被解決了,是不是很方便呢~

Book book = null;
string name = null;
if(book != null && book.Name != null){
  name = book.Name;
}
else{
  name = string.Empty;
}

//簡化取值的判斷
name = book?.Name;//null

//簡化預設值的設置,若是null則為string.Empty
name = book?.Name ?? string.Empty;

參考

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com 標誌

您的留言將使用 WordPress.com 帳號。 登出 /  變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 /  變更 )

連結到 %s

在 WordPress.com 建立網站或網誌

向上 ↑

%d 位部落客按了讚: