前陣子碰到個資料表儲存方式,因為這種位元運算的方式也常用在權限管理等地方,這邊就順手紀錄一下。

平常遇到二元的情形(例如 開/關、有/沒有),我們會直接宣告個 Boolean 來處理。例如 bool isNice = false

但這次遇到的是同時有多組「有/沒有」的狀況,而我遇到的程式碼並沒有分成多個 Bool 去做處理,而是直接儲存成一個數值。

這是因為「有/沒有」只佔據一個位元,那麼將多個狀況按照位元順序排列的話,就只需要一個數字就可以紀錄並傳遞給其他系統了。

例如最常見的權限系統:假設我們有「讀」、「寫」、「執行」的權限,接著就可以按照這個順序,用 1 和 0 來代表「有/沒有」來排列位元,

也就是說,如果權限是 讀=可、寫=不可、執行=可 的時候,就記做 101 = 5

這種直接用一組位元來表示狀態的方式,就叫做位元旗標(Bit flag)

1 讀   = 可
0 寫   = 不可
1 執行 = 可

/* 橫放 */
=> 101 (2進位) 
=> 5 

Enum & Flags

假使某天老闆靈光一現,決定接下來的新人員工都要記錄他們會的程式語言,並且他們報到的時候就會發一張公司列好的程式語言清單請他們勾選。這時候要怎麼做呢?

在 C# 中已經有方便的工具可以處理位元旗標,我們可以建一組叫做 SkillEnum 的列舉(Enum),並且按照上面說明的,將老闆提到的每個技能各自用一個位元來表示。

[Flags]
public enum SkillEnum
{
    C = 1,     // 0001
    PHP = 2,   // 0010
    SQL = 4,   // 0100
    Java = 8,  // 1000
}

註:[Flags]的標籤是指 C# 專門提供給位元旗標使用的 Enum,請參見 FlagsAttribute

只要在 enum 上加上 Flags 的屬性,除了自動按照 2 的次元增加以外,在使用 ToString() 也能更方便看見旗標內容

有了這個列舉之後,我們就可以表達不同排列組合的狀況了。例如:

* C: C
* P: PHP
* S: SQL
* J: Java
===========
J S P C
0 0 0 0 => 什麼都不會
0 0 0 1 => 只會 C
0 0 1 1 => 同時會 C 和 PHP
1 0 1 0 => 同時 PHP 和 Java
1 1 1 1 => 全部都會
……

閱讀全文