天天看點

位運算符在枚舉中的應用

##位運算符在枚舉中的應用

枚舉是被命名的整型常數的集合,在程式設計中是很常見的類型。在一般情況下,枚舉類型用于辨別對象屬于什麼類型,例如在計算機圖形的幾何分類中,點、線、面等元素類型可以用枚舉來表示幾何類型。

在某些特殊的業務需求下,我們可能會選擇枚舉類型中的特定幾項類型進行特殊處理,例如“修改幾何實體的時針方向”這一功能,對于幾何點類型來說,是不存在時針方向這一說法的,是以隻能線上和面之間選擇。

假設現在擷取到了一個幾何實體,需要判斷是否屬于線,或者是否屬于面,傳統的判斷方法是

Geometry.Type == Polyline || Geometry.Type == Polygon
           

但是如果在可滿足的條件很多的情況下,這樣寫出來的代碼會變得非常的長,不利于代碼的維護。

基于類似的業務背景,位運算符可以解決此類問題。常用的位運算符指的是與

(&),或(|),非(^)

衆所周知,代碼能運作起來,靠的還是底層的機器碼0與1之間的運算。枚舉對象存儲在記憶體中,也是用機器碼來表示。位運算符就是對機器碼0和1的運算。前面提到過,枚舉類型是整形常量的幾何。整型可分成常用的八進制、十進制、十六進制表示。最常用的應該是十進制的表示方式。

上面提到的解決需要判斷的枚舉項過多的問題,使用位運算符去解決的前提是:

構成枚舉的項轉成二進制整型時,每一個項隻能有且僅有對應的一位為1,其餘位為0

舉個例子,假設現在需要做一個幾何類型枚舉的位運算。

Point = 1,對應的二進制表示為0000 0001
Polyline= 2,對應的二進制表示為0000 0010
Polygon = 4,對應的二進制表示為0000 0100
           

可以看見的是從第一個枚舉項到最後一個枚舉項,轉成二進制表示後,都是隻有一個位數上為1,其他位數上為0。我們可以用運算符<<來實作,上述的三個類型可以表示為:

Point = 1 << 0,
	Polyline = 1<<1,
	Polygon = 1<<2,
           

上面描述枚舉對應的二進制表達方式,在此處解釋下為什麼要這樣做。上面的方式的意義,就是二進制中,每一個位代表了一個枚舉項。各枚舉項進行或(|)運算後,每一位之間不會影響其他位的運算結果。這裡舉幾個例子

Point | Polyline = 0000 0011
	Polyline | Polygon = 0000 0110
	Polygon | Point = 0000 0101
	Polygon | Polyline | Point = 0000 0111
           

也就是說,若幹個枚舉項之間任意組合,它們之間的或運算結果,都是一個唯一的數字。對于上面描述的業務需求,我們可以将支援修改時針順序的幾何類型預設成一個值,這個值可以用整形存儲。

ClockWiseSupportType = Polyline | Polygon (0000 0110)
           

在判斷幾何實體是否符合要求時,就用與運算進行判斷。

bool IsSupport = ClockWiseSupportType & Geometry.Type 
           

分以下幾種情況,如果是不支援的點類型進行運算,其結果是

0000 0110 & 0000 0001 = 0000 0000 = 0 = false 
           

如果是支援的類型進行與運算,其結果是

0000 0110 & 0000 0010 = 0000 0010 = 2 = true 
0000 0110 & 0000 0100 = 0000 0100 = 4 = true
           

通過這種方式,可以判斷某類型是否在支援的類型集合中。在實際編碼中,最直覺的好處是減少代碼的備援程度,有利于代碼維護。甚至是節省了多個或運算的判斷,在非常大的運算量中,還能起到優化代碼效率的作用。

進一步地,這種方法有另一種适用場景。在許多業務需求種,都會用到狀态值。例如筆者參與研發的平台中的渲染子產品,在渲染過程中會有各種各樣的渲染模式、渲染狀态、渲染優化。但不同情況的渲染過程,是需要開啟不同的狀态的。用定義渲染枚舉,按照二進制位左移的順序排列,并定義一個記錄狀态值開關的整型Status。

用Status = Status | 狀态項 ,可以打開狀态
用Status = Status & (^狀态項),可以關閉狀态
用 IsStatusOn = Status & (狀态項),可以判斷狀态是否打開
           

通過上述方法,可以便捷地記錄一系列的狀态開關,而不用自定義

一連串的狀态值,提高了編碼效率,降低了維護成本。

補充一點,對于狀态值,一般不推薦過多的狀态值全放在同一個集合内。原因是每一個枚舉項都會占用二進制的一個位數。枚舉類型中可枚舉的範圍一般是Int64的範圍。但如果要進行或運算,需要用無符号的整形進行運算。也就是一般枚舉最大可定義 0到1 << 30的,共32位枚舉項。0一般是不用作枚舉項的,也就是說,最多支援31位枚舉項。

繼續閱讀