重寫 class 的 ToString() 來簡化獲取 enum 的 DescriptionAttribute 值 目錄 一、常見的 enum 類型版本 二、演變:class 版本的 enum 類型 三、演進:class 和 enum 兩者共存的版本 一、常見的 enum 類型版本 新建一個 Alg ...
重寫 class 的 ToString() 來簡化獲取 enum 的 DescriptionAttribute 值
目錄
一、常見的 enum 類型版本
新建一個 AlgorithmType 枚舉,裡面包含 MD5、SHA1 和 SHA2 等一系列的枚舉值,預設為 int 類型。有的時候我們會在對應的枚舉值上使用特性 [Description] 來添加備註信息,方便我們後期提供描述信息(如返回給前端界面展示時可能需要使用)。
AlgorithmType.cs
/// <summary> /// 演算法類型 /// </summary> public enum AlgorithmType { /// <summary> /// MD5 /// </summary> [Description("Message-Digest Algorithm 5")] MD5, /// <summary> /// SHA1 /// </summary> [Description("Secure Hash Algorithm 1")] SHA1, /// <summary> /// SHA224 /// </summary> [Description("Secure Hash Algorithm 224")] SHA224, /// <summary> /// SHA256 /// </summary> [Description("Secure Hash Algorithm 256")] SHA256, /// <summary> /// SHA384 /// </summary> [Description("Secure Hash Algorithm 384")] SHA384, /// <summary> /// SHA512 /// </summary> [Description("Secure Hash Algorithm 512")] SHA512 }
常見的一個做法是,編寫一個擴展方法來獲取 enum 的描述信息:
public static class EnumExtensionMethods { /// <summary> /// 獲取枚舉類型的描述信息 /// </summary> public static string GetDescription(this Enum value) { var type = value.GetType(); var name = Enum.GetName(type, value); if (name == null) return null; var field = type.GetField(name); if (!(Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) is DescriptionAttribute attribute)) { return name; } return attribute.Description; } }
因為直接輸出枚舉值,或者通過枚舉類型對應的 ToString() 的方式輸出字元串,結果都為它本身的值,BCL 也並沒有提供快捷的方式去獲取描述信息,所以只能通過類似上述的擴展方法來獲取 [DescriptionAttribute]。
【分析】因為枚舉屬於值類型,它不需要在堆上分配空間,並且在計算時(如比較運算)效率相比引用類型更高,所以從性能上枚舉類型是占據了絕對的優勢;但是由於 BCL 並沒有提供快捷方式的去獲取 [Description],並且編寫對應的獲取方法也較為麻煩(其實也不麻煩,因為你會使用程式員的兩大法寶:CTRL+C 和 CTRL+V)。
二、演變:class 版本的 enum 類型
這次我們不直接使用 enum,而是採取新建 class 的方式去模仿 enum 類型,也就是使用引用類型來取代值類型,不過,性能方面會有所損耗。
HashAlgorithmType.cs
/// <summary> /// 哈希演算法類型 /// </summary> public class HashAlgorithmType { /// <summary> /// MD5 /// </summary> public static readonly HashAlgorithmType MD5 = new HashAlgorithmType(0); /// <summary> /// SHA1 /// </summary> public static readonly HashAlgorithmType SHA1 = new HashAlgorithmType(1); /// <summary> /// SHA224 /// </summary> public static readonly HashAlgorithmType SHA224 = new HashAlgorithmType(2); /// <summary> /// SHA256 /// </summary> public static readonly HashAlgorithmType SHA256 = new HashAlgorithmType(3); /// <summary> /// SHA384 /// </summary> public static readonly HashAlgorithmType SHA384 = new HashAlgorithmType(4); /// <summary> /// SHA512 /// </summary> public static readonly HashAlgorithmType SHA512 = new HashAlgorithmType(5); private readonly int _hashAlgorithmType; private HashAlgorithmType(int hashAlgorithmType) { _hashAlgorithmType = hashAlgorithmType; } public override string ToString() { string result; switch (_hashAlgorithmType) { case 0: result = "Message-Digest Algorithm 5"; break; case 1: result = "Secure Hash Algorithm 1"; break; case 2: result = "Secure Hash Algorithm 224"; break; case 3: result = "Secure Hash Algorithm 256"; break; case 4: result = "Secure Hash Algorithm 384"; break; case 5: result = "Secure Hash Algorithm 512"; break; default: throw new Exception("哈希演算法類型有誤"); } return result; } }
我們採用了 private 進行構造函數私有化的操作,這樣就不會允許在類的外部 new 對象了;其次,使用 public static readonly 欄位提供給外部進行訪問,這樣的話就和枚舉類型的調用方式一致;最後,我們對 ToString() 方法進行了重寫,在 return 的值中返回對應的描述信息。
這樣,我們就可以直接通過 class.field 的方式得到對應枚舉值的描述信息。
【分析】性能受損;但 ToString() 比 GetDescription() 更淺顯直白、清晰明瞭;不過,參數以數字“寫死”的方式進行提供也欠妥。
三、演進:class 和 enum 兩者共存的版本
在上一個版本的類中,我們在進行構造函數初始化時直接使用了數字 0~5,並且重寫 ToString() 時也是直接使用數字 0~5,除了不直觀的因素之外,隨著枚舉值數量的增加,枚舉值和自身描述兩者間的對應關係也容易出錯。現在,我們嘗試以私有嵌套類的形式將 enum 和 class 的兩者有機結合起來。
HashAlgorithmType.cs
/// <summary> /// 哈希演算法類型 /// </summary> public class HashAlgorithmType { /// <summary> /// MD5 /// </summary> public static HashAlgorithmType MD5 = new HashAlgorithmType(AlgorithmType.MD5); /// <summary> /// SHA1 /// </summary> public static readonly HashAlgorithmType SHA1 = new HashAlgorithmType(AlgorithmType.SHA1); /// <summary> /// SHA224 /// </summary> public static readonly HashAlgorithmType SHA224 = new HashAlgorithmType(AlgorithmType.SHA224); /// <summary> /// SHA256 /// </summary> public static readonly HashAlgorithmType SHA256 = new HashAlgorithmType(AlgorithmType.SHA256); /// <summary> /// SHA384 /// </summary> public static readonly HashAlgorithmType SHA384 = new HashAlgorithmType(AlgorithmType.SHA384); /// <summary> /// SHA512 /// </summary> public static readonly HashAlgorithmType SHA512 = new HashAlgorithmType(AlgorithmType.SHA512); /// <summary> /// 演算法類型 /// </summary> private readonly AlgorithmType _algorithmType; private HashAlgorithmType(AlgorithmType algorithmType) { _algorithmType = algorithmType; } public override string ToString() { string result; switch (_algorithmType) { case AlgorithmType.MD5: result = "Message-Digest Algorithm 5"; break; case AlgorithmType.SHA1: result = "Secure Hash Algorithm 1"; break; case AlgorithmType.SHA224: result = "Secure Hash Algorithm 224"; break; case AlgorithmType.SHA256: result = "Secure Hash Algorithm 256"; break; case AlgorithmType.SHA384: result = "Secure Hash Algorithm 384"; break; case AlgorithmType.SHA512: result = "Secure Hash Algorithm 512"; break; default: throw new Exception("哈希演算法類型有誤"); } return result; } /// <summary> /// 演算法類型 /// </summary> private enum AlgorithmType { /// <summary> /// MD5 /// </summary> MD5, /// <summary> /// SHA1 /// </summary> SHA1, /// <summary> /// SHA224 /// </summary> SHA224, /// <summary> /// SHA256 /// </summary> SHA256, /// <summary> /// SHA384 /// </summary> SHA384, /// <summary> /// SHA512 /// </summary> SHA512 } }
在 HashAlgorithmType 類中,新建了一個私有的 AlgorithmType 枚舉,這樣就只允許 HashAlgorithmType 類訪問,而不會在該類的外部(其它類型)進行訪問。
正所謂蘿蔔青菜,各有所愛,假如你過於關於效率性能,或者說不需要使用諸如 [Description] 附加的特性,也不想額外的增添更多的行為和特征時,我們依然可以採用最原始的、大眾化的版本。