Enum列舉的尋訪、比對並進行變數指派

Enum列舉的尋訪尋值動作並不常用,每次要用的時候都要重新去翻一次System.Enum.GetValues的用法,因此把他筆記這裡,讓有需要的人可以快速地找到使用範例。

已知一整數數值,利用此整數回傳某列舉型別

假設有一列舉行別如下:

enum myFriend
{
  NoFriend = 0,
  John = 1,
  Marry = 2,
  Sam = 3,
  Chris = 4
}

如果我們從前端、或資料庫端得到一個整數數值,那麼我們可以利用下列程式碼比對myFriend裡面所有的列舉項次,並進而取得可能命中的列舉項次。

public static void Main()
{
  int iFriend = 3;
  myFriend eFind = myFriend.NoFriend;
  
  foreach (int iItem in System.Enum.GetValues(typeof (myFriend)))
  {
    if (iFriend == iItem) { eFind = (myFriend)iItem; break; }
  }
  
  Console.WriteLine($"{(int)eFind} / {eFind.ToString()}");
}

利用Linq來進行列舉的查找篩選

如果是把System.Linq弄進來,使用他的語法來進行資料搜尋的話,語法變成一行就可以解決了。這樣的寫法還有一個好處,就是因為enum是實值型別(ValueType),所以就算找不到命中FirstOrDefault也不會傳回null,而是會傳回這個enum的預設值,也就是等於0的NoFriend。(如果列舉裡面沒有等於0的項次,則會回傳一個0,你對它.GetType()還是會回傳myFriend。)

myFriend eFind = System.Enum.GetValues(typeof(myFriend)).OfType<myFriend>().Where(x => (int)x == iFriend).FirstOrDefault();

利用列舉名稱(Enum Name;String)來取得列舉型別本身

有時候我們會希望利用某個Enum的名稱來反轉換(反求得)列舉型別,可以使用下列程式碼來進行:

//Parse寫法
try
{
  var eTemp = System.Enum.Parse(typeof(myFriend), "John");
  Console.WriteLine(eTemp.ToString());
}
catch
{ Console.WriteLine("找不到這個列舉名稱。"); }

//TryParse寫法,這個方法另外有個多載引數可以支援不區分大小寫比對
System.Enum.TryParse("John", out myFriend eTemp);
//如果不存在這個字串而比對失敗,會回傳預設值NoFriend(0)
Console.WriteLine(eTemp.ToString());

利用列舉數值(Enum Value;Int)來取得列舉型別本身

有時候我們會希望利用某個Enum的數值來反轉換(反求得)列舉型別,可以使用下列程式碼來進行:

//傳統轉型寫法
myFriend eTemp = (myFriend)3;
Console.WriteLine(eTemp.ToString());  //印出Sam

//傳統轉型寫法的缺點
myFriend eTemp = (myFriend)123;       //根本沒有此列舉
Console.WriteLine(eTemp.ToString());  //不會出錯且印出123

//使用Enum.IsDefined來輔助檢查
int i = 123;
if(System.Enum.IsDefined(typeof(myFriend), i))
{ 
  myFriend eTemp = (myFriend)i;
  Console.WriteLine(eTemp.ToString());
}
else
{ Console.WriteLine("找不到這個列舉數值。"); }

要特別注意的是,當Enum被套用了[System.Flags] Attributes屬性時Enum.IsDefined將會失效(失去判斷作用),如果你有到這麼深的程度,要自己特別小心。

利用LINQ來判斷某外部列舉,其值是否符合列舉項次?

承上,若列舉的來源是外部參數賦予(例如使用Newtonsoft.Json.JsonConvert.DeserializeObject綁定回物件),那麼極有可能這個賦予的值根本不存在於你的列舉表之中,舉例下列程式碼來說是不會有任何錯誤的,但你的列舉項次其實根本沒有這個項目:

var eTemp = (myFriend)99;  //這是合法的,儘管99根本不在myFriend裡面
Console.WriteLine($"{(int)eTemp} / {eTemp}"); //將會輸出:99 / 99

多數時候我們可不想要這種外部來源來汙染我們的列舉值,或者是逼著我們採用預設值,若不想採用IsDefined()方法的話,可以另外考慮採用LINQ語法來進行檢查:

if (System.Enum.GetValues(typeof(myFriend)).OfType<myFriend>().Where(x => (int)x == (int)eTemp).Count() != 0)
{ Console.WriteLine("有在列舉項目裡"); }
else
{ Console.WriteLine("沒在列舉項目裡"); }

再修改成使用LINQ Any()方法會更有效率一點:

if (System.Enum.GetValues(typeof(myFriend)).OfType<myFriend>().Any(x => (int)x == (int)eTemp))
{ Console.WriteLine("有在列舉項目裡"); }
else
{ Console.WriteLine("沒在列舉項目裡"); }

Happy Coding!

參考文章

Enum用於位元邏輯運算的寫法

System.Enum.GetValues System.Enum.GetNames Foreach Int32 Assignment IntToEnum StringToEnum ConvertToEnum