Extension Method on Enumeration

摘要:Extension Method on Enumeration

說到 Enum 大家都知道是什麼東西, 尤其是在 Java 中, Enum 都快不 Enum 了 (茶), 在 C# 和 VB 中 Enum 不能算是個類別, 因此不能繼承, 功能和 Java 的相比的確是有點不方便. Java 在編譯時會處理 Enum, 轉成另一種複合的類別, 這個可以參考別人反編譯後的文章如下:

http://zhxing.iteye.com/blog/621475

主要是動態形成一個提供方法的 final class, 然後原本 Enum 中的元素都被宣告成這個類別型態的 final static fields, 最後在靜態建構子中給予當初 Enum 中的初始值. 這種作法就跟C++路線的一樣, 只不過在 Java 中只需定義 Enum, 後面的都由編譯器幫你橋好, 真方便啊!

那 C# 和 VB 中的 Enum 的值就只能是整數類的型態, 如 Byte, Integer, Long, SByte, Short, UInteger, Ulong 和 UShort, 預設是 Integer. 因為 Enum 不像一般類別可以繼承擴充, 所以都採用 Aggregation Extension, 也就是用另一個類別去包覆 Enum, 所以使用到 Enum 的時機也不會太複雜就是了!

 .NET framework 在 3.0 後新增了 Extension Method, 這整個使類別擴充更具彈性; 那麼就總共有三種擴充方式: Inheritance Extension, Aggregation Extension 和 Extension Method. 如果你不清楚擴充方法, 可以參考下列連結:

http://msdn.microsoft.com/zh-tw/library/bb384936(v=VS.100).aspx

雖然好用, 但 Extension Method 是以靜態的方式去擴充!! 不過在 Linq 中到處都可以看到 Lambda Expression 與 Extension Method, 科科...

這種靜態擴充對 Enum 來說剛好適用, 因為一般 Enum 除了自己本身的元素之外, 不太會有其他的物件資料, 只會有物件結構, 而靜態函式專注處理物件自身中既有資料狀態, 這也是 Linq 大量使用 Extension Method 的方式.

講了這麼多, 總得貼一些程式碼, 不然就會覺得寂寞和空虛, 下面貼了 Enum 很常用到的函式:
Imports System.Runtime.CompilerServices

Module CommonEnumExtension
    <extension()>
    Public Function Name(ByVal e As [Enum]) As String
        Return [Enum].GetName(e.GetType(), e)
    End Function

    <extension()>
    Public Function toIntValue(ByVal e As [Enum]) As Integer
        Dim fi As FieldInfo = e.GetType().GetField(e.Name)
        Return CType(fi.GetValue(Nothing), Integer)
    End Function
End Module

其實取得 Enum Name 很簡單, 但人家就是想要程式碼看起來比較優雅嘛 =w=, 一般取得元素名稱可以使用 ToString 或 GetName, 因為 ToString 的意圖太空泛, 說不定哪一天它的輸出值被改成跟 Type.ToString 一樣就囧了.

另一個轉型成整數的擴充方法, 倒是比較複雜, 怎用起 Reflection 來了, 其實我也不想這麼麻煩 (茶). 一般 Integer Casting 使用 CType 就可以, 如下:

CType( EnumObj, Integer)

但上面這行中的 EnumObj 如果更換成引數中的 e 就不行, 因為 [Enum] 無法轉型成 Integer, 我囧了, 我試了 CInt, Integer.Parse 和 Integer.TryParse 都不行, 但我又想對所有 Enum 提供這個方法, 我就只有想到用反射囉! 有誰知道更好的方法, 請麻煩告訴我 <3

當加入上面這兩個擴充方法後, 我隨便宣告個 Enum 就可以使用了!

Public Enum Action As Integer
    View = 1
    Query
End Enum

Dim actName as String = Action.View.Name
Dim intValue as Integer = Action.Query.toIntValue


actName  的值會是 “View”, 而後 intValue 會是 2. (怎麼這麼好用, 被揍飛 ~).

這邊提醒一下, 在 Name 擴充方法中, GetName 的 e 實際值與 e.GetType 的型態有可能會對應不起來而得到 Nothing, 這在 Enum 轉型時非常容易發生.

最後還是展示一下 Aggregation Extension 的寫法, 這個大家應該都很熟了! 就把作用函式包裝在另一個類別中, 一般這個函式的範圍是靜態的, 而 Enum 要不要放進包裝類別和作用函式的參數是否為 [Enum] 型就視設計者的需求而變.

Public Class MyWorks
    Enum Action
        view
        query
    End Enum

    Public Shared Function Name(ByVal act As Action) As String
        Return [Enum].GetName(act.GetType(), act)
    End Function
End Class

Public Sub test() 
    MyWorks.Name(MyWorks.Action.view)
End Sub


如果你想知道 Enum 混合類別資料狀態的擴充可以參考另一篇 Enumeration with Class State.