iT邦幫忙

5

[C#] 使用 Enum 避免 Magic Number

Magic Number

所謂的 Magic Number 中文翻譯是 「魔術數字」,
是指由程式設計者自己定義的某些數字,
旁人不透過註解無法了解其中的涵義,
甚至作者本人在經過一段時間後,自己也會忘了當初這個數字的用途。

像我以前常寫這樣的程式碼

if (user.Identity != 1)
{
    throw new Exception("沒有權限。");
}

var sexName = user.Sex == 1 ? "男" : "女";

這樣的程式有什麼問題?

  1. 難以閱讀,無法從程式碼了解這段程式的用途,如果又沒有寫註解加上作者離職,那就真沒人知道這段程式到底在幹嘛。
    而且隨著專案日漸龐大,類似的魔術數字越來越多,註解可能散落各處,或沒有跟著程式更新,這都會造成維護上的困難。

  2. 難以修改,需求變動或程式重構時,所有用到的地方都要修改,只要漏改一個地方,就會產生一個新 Bug。

C# 可以使用 Enum 來解決此類問題。

第一個例子,可以新增一個 IdentityEnum,統一管理使用者身分的值,這樣程式可讀性佳,要改變值也只需改一個地方。

public enum Identity
{
    //管理者
    Admin = 1,

    //一般使用者
    User = 2,
}

//需判斷的地方
if (user.Identity != Identity.Admin)
{
    throw new Exception("沒有權限。");
}

第二個例子,可以新增一個 SexEnum,然後用 GetSexName 來統一管理輸出的中文。

public enum Sex
{
    //男生
    Male = 1,
    
    //女生
    Female = 2
}

public string GetSexName(Sex sex)
{
    if (sex == Sex.Male)
    {
        return "男";
    }
    return "女";
}

//需輸出中文的地方
var sexName = GetSexName(user.Sex);

第二個例子,在 C# 還可以用 [Description("")] Attribute 來改進。

using System.ComponentModel;

public enum Sex
{
    [Description("男")]
    Male = 1,

    [Description("女")]
    Female = 2
}

這樣不僅可以當 註解,還可以寫一個共用的擴充方法 ToDescription 給所有的 Enum 使用,不用再為每個 Enum 寫各自的轉換函數。

public static class EnumExtenstions
{
    public static string ToDescription(this Enum value)
    {
        return value.GetType()
            .GetRuntimeField(value.ToString())
            .GetCustomAttributes<System.ComponentModel.DescriptionAttribute>()
            .FirstOrDefault()?.Description ?? string.Empty;
    }
}

用法:

var sexName = user.Sex.ToDescription();

Enum 的規則

不指定 Enum 的值,預設由 0 開始排。

public enum Example
{
    A,     //0
    B,     //1
    C      //2
}

指定其中一項的值,後面的項目會依序遞增。

public enum Example
{
    A = 5,   //5
    B,       //6
    C        //7
}

要注意這種情形,會造成奇怪的結果,但程式不會出錯。

public enum Example
{
    A = -1,     //-1
    B = -2,     //-2
    C           //-1
}

Enum 常見的幾種轉換

var _enum = Sex.Male;
var _number = 1;
var _string = "Male";

//將 enum 轉換為 number
var val1 = (int)_enum;

//將 number 轉換為 enum
var val2 = (Sex)_number;

//將 enum 轉換為 string
var val3 = _enum.ToString();

//將 string 轉換為 enum
var val4 = (Sex)Enum.Parse(typeof(Sex), _string);

//將 enum 轉換為 JSON
var obj = new
{
    abc = ABC.A
};
var json = Newtonsoft.Json.JsonConvert.SerializeObject(obj);
//{"abc":1}

指定 Enum 的數值型態

可以指定的型態有
bytesbyteshortushortintuintlongulong

public enum Sex : byte
{
    Male,
    Female
}

Enum 旗標的應用

在 Enum 加上 [Flags] Attribute。

[Flags]
public enum ABC
{
    A = 0x01,
    B = 0x02,
    C = 0x04
}
var abc = ABC.A | ABC.C;

var _number = (int)abc;         //5
var _string = abc.ToString();   //A, C

結語:
在實務上最常看到 Magic Number 的地方就在資料庫,
所以現在我會把這種用數字代表狀態的欄位,都做出對應的 Enum 統一管理,
忘記的時候也不用到處找註解或重新閱讀程式,只要去那裡查一下馬上就知道其意義,善用 Enum 真的是最簡單提升程式碼品質的方式。

參考文章:
魔術數字 (程式設計)
Don't use Magic number in your code
CODE-enum, string, int間的轉換
Method that returns description attribute of enum value


1 則留言

1
神Q超人
iT邦新手 1 級 ‧ 2017-12-18 09:58:03

哇嗚 這個感覺很好用
先記下來以後說不定很常用到XD

我要留言

立即登入留言