星期六, 11月 21, 2020

[C#] Enum - FlagsAttribute

Enum FlagsAttribute 使用練習筆記

官方文章內容:FlagsAttribute 和 Enum 的指導方針
  • 將列舉常數定義為2的乘冪,也就是1、2、4、8 等等。 這表示結合的列舉常數中的個別旗標不會重迭
  • 請考慮建立常用旗標組合的列舉常數。 例如,如果您有一個用於包含列舉常數和之檔案 i/o 作業的列舉 Read = 1 Write = 2 ,請考慮建立列舉常數 ReadWrite = Read OR Write ,其中結合了 Read 和 Write 旗標。 此外,在某些情況下,用來結合旗標的位 OR 運算可能會被視為一種簡單的概念
  • 如果您定義負數作為旗標列舉常數,因為有許多旗標位置可能設定為1,這可能會讓您的程式碼變得令人困惑,並鼓勵程式碼錯誤,請務必謹慎使用
  • 測試旗標是否設定在數值中的方便方式,是在數值和旗標列舉常數之間執行位 AND 運算,將數值中的所有位設定為零,而不會對應至旗標,然後測試該運算的結果是否等於旗標列舉常數
  • 用作 None 值為零之旗標列舉常數的名稱。 您無法使用 None 位 and 運算中的列舉常數來測試旗標,因為結果一律為零。 不過,您可以在數值和列舉常數之間執行邏輯(而非位比較), None 以判斷是否已設定數值中的任何位。如果您建立一個值列舉,而不是旗標列舉,則建立列舉常數仍然是值得的 None 。 原因是,通用語言執行時間預設會將用於列舉的記憶體初始化為零。 因此,如果您未定義其值為零的常數,則在建立時,列舉會包含不合法的值。如果您的應用程式有明顯的預設情況,您的應用程式需要表示,請考慮使用其值為零的列舉常數來表示預設值。 如果沒有預設案例,請考慮使用值為零的列舉常數,表示不是任何其他列舉常數所表示的大小寫
  • 請勿只定義列舉值來鏡像列舉本身的狀態。 例如,請勿定義只標示列舉結尾的列舉常數。 如果您需要判斷列舉的最後一個值,請明確檢查該值。 此外,如果範圍內的所有值都有效,您可以針對第一個和最後一個列舉的常數執行範圍檢查
  • 請勿指定保留給未來使用的列舉常數
  • 當您定義採用列舉常數作為值的方法或屬性時,請考慮驗證值。 原因是,您可以將數值轉換成列舉型別,即使該數值未在列舉中定義
C# Code
using System;

namespace EnumFlags
{
    class Program
    {
        static void Main(string[] args)
        {
            Days days = Days.Saturday;
            Console.WriteLine($"初始值:{days}");

            days = days.Add(Days.Sunday);
            Console.WriteLine($"把星期日加入:{days}");

            days = days.Remove(Days.Saturday);
            Console.WriteLine($"把星期六移除:{days}");

            Console.WriteLine($"星期六日是否同時存在:{days.IsSatAndSunBoth()}");
            Console.WriteLine($"星期六日是否存在一日:{days.IsSatOrSunExists()}");
        }
    }

    // 沒有特別宣告的話,Enum 預設是 int
    [Flags]
    public enum Days
    {
        Monday = 1,
        Tuesday = 2 ,
        Wednesday = 4,
        Thursday = 8 ,
        Friday = 16,
        Saturday = 32,
        Sunday = 64,

        All = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
    };

    public static class ExtDays
    {
        public static Days Add(this Days source, Days flagAdd)
        {
            return source | flagAdd;
        }

        public static Days Remove(this Days source, Days flagRemove)
        {
            return source & ~flagRemove;
        }

        // 星期六日是否同時存在
        public static bool IsSatAndSunBoth(this Days value)
        {
            return value.HasFlag(Days.Saturday | Days.Sunday);
        }

        // 星期六日是否存在一日
        public static bool IsSatOrSunExists(this Days value)
        {
            return value.HasFlag(Days.Saturday) || value.HasFlag(Days.Sunday);
        }
    }
}
一開始練習時,因為是根據官方文章把 Enum 值,從 0 開始往後編,偏偏又用 [值為 0 的列舉] 來初始化,FlagAttribute 怎麼都加不上去,閱讀 [FlagsAttribute 指方針] 才發現到問題點
用作 None 值為零之旗標列舉常數的名稱。 您無法使用 None 位 and 運算中的列舉常數來測試旗標,因為結果一律為零。 不過,您可以在數值和列舉常數之間執行邏輯(而非位比較), None 以判斷是否已設定數值中的任何位。

如果您建立一個值列舉,而不是旗標列舉,則建立列舉常數仍然是值得的 None 。 原因是,通用語言執行時間預設會將用於列舉的記憶體初始化為零。 因此,如果您未定義其值為零的常數,則在建立時,列舉會包含不合法的值。

如果您的應用程式有明顯的預設情況,您的應用程式需要表示,請考慮使用其值為零的列舉常數來表示預設值。 如果沒有預設案例,請考慮使用值為零的列舉常數,表示不是任何其他列舉常數所表示的大小寫。

沒有留言:

張貼留言