簡體   English   中英

比較C#中的枚舉標志

[英]Comparing enum flags in C#

我需要檢測是否在枚舉值中設置了標志,該類型用Flag屬性標記。

通常是這樣制作的:

(value & flag) == flag

但是由於我需要通過通用方式執行此操作(有時在運行時,我的事件只有一個“ Enum”引用。我找不到使用&運算符的簡便方法。此刻,我將其設置為:

    public static bool IsSet<T>(this T value, T flags) where T : Enum
    { 
        Type numberType = Enum.GetUnderlyingType(typeof(T));

        if (numberType.Equals(typeof(int)))
        {
            return BoxUnbox<int>(value, flags, (a, b) => (a & b) == b);
        }
        else if (numberType.Equals(typeof(sbyte)))
        {
            return BoxUnbox<sbyte>(value, flags, (a, b) => (a & b) == b);
        }
        else if (numberType.Equals(typeof(byte)))
        {
            return BoxUnbox<byte>(value, flags, (a, b) => (a & b) == b);
        }
        else if (numberType.Equals(typeof(short)))
        {
            return BoxUnbox<short>(value, flags, (a, b) => (a & b) == b);
        }
        else if (numberType.Equals(typeof(ushort)))
        {
            return BoxUnbox<ushort>(value, flags, (a, b) => (a & b) == b);
        }
        else if (numberType.Equals(typeof(uint)))
        {
            return BoxUnbox<uint>(value, flags, (a, b) => (a & b) == b);
        }
        else if (numberType.Equals(typeof(long)))
        {
            return BoxUnbox<long>(value, flags, (a, b) => (a & b) == b);
        }
        else if (numberType.Equals(typeof(ulong)))
        {
            return BoxUnbox<ulong>(value, flags, (a, b) => (a & b) == b);
        }
        else if (numberType.Equals(typeof(char)))
        {
            return BoxUnbox<char>(value, flags, (a, b) => (a & b) == b);
        }
        else
        {
            throw new ArgumentException("Unknown enum underlying type " + numberType.Name + ".");
        }
    }


    private static bool BoxUnbox<T>(object value, object flags, Func<T, T, bool> op)
    {
        return op((T)value, (T)flags);
    }

但是我不喜歡永無止境的if-else塊,因此有沒有一種方法可以強制使用&運算符或任何其他解決方案來檢查這些值的值?

對我來說,它看起來太復雜了。 如何處理(請記住,枚舉始終映射為整數類型):

public static bool IsSet<T>(T value, T flags) where T : struct
{
    // You can add enum type checking to be perfectly sure that T is enum, this have some cost however
    // if (!typeof(T).IsEnum)
    //     throw new ArgumentException();
    long longFlags = Convert.ToInt64(flags);
    return (Convert.ToInt64(value) & longFlags) == longFlags;
}

我為枚舉編寫了一組擴展方法,以防您需要:

public static class EnumExtensions
{
    private static void CheckEnumWithFlags<T>()
    {
        if (!typeof(T).IsEnum)
            throw new ArgumentException(string.Format("Type '{0}' is not an enum", typeof(T).FullName));
        if (!Attribute.IsDefined(typeof(T), typeof(FlagsAttribute)))
            throw new ArgumentException(string.Format("Type '{0}' doesn't have the 'Flags' attribute", typeof(T).FullName));
    }

    public static bool IsFlagSet<T>(this T value, T flag) where T : struct
    {
        CheckEnumWithFlags<T>();
        long lValue = Convert.ToInt64(value);
        long lFlag = Convert.ToInt64(flag);
        return (lValue & lFlag) != 0;
    }

    public static IEnumerable<T> GetFlags<T>(this T value) where T : struct
    {
        CheckEnumWithFlags<T>();
        foreach (T flag in Enum.GetValues(typeof(T)).Cast<T>())
        {
            if (value.IsFlagSet(flag))
                yield return flag;
        }
    }

    public static T SetFlags<T>(this T value, T flags, bool on) where T : struct
    {
        CheckEnumWithFlags<T>();
        long lValue = Convert.ToInt64(value);
        long lFlag = Convert.ToInt64(flags);
        if (on)
        {
            lValue |= lFlag;
        }
        else
        {
            lValue &= (~lFlag);
        }
        return (T)Enum.ToObject(typeof(T), lValue);
    }

    public static T SetFlags<T>(this T value, T flags) where T : struct
    {
        return value.SetFlags(flags, true);
    }

    public static T ClearFlags<T>(this T value, T flags) where T : struct
    {
        return value.SetFlags(flags, false);
    }

    public static T CombineFlags<T>(this IEnumerable<T> flags) where T : struct
    {
        CheckEnumWithFlags<T>();
        long lValue = 0;
        foreach (T flag in flags)
        {
            long lFlag = Convert.ToInt64(flag);
            lValue |= lFlag;
        }
        return (T)Enum.ToObject(typeof(T), lValue);
    }
}

主要缺點是您不能指定where T : Enum :明確禁止where T : Enum使用(“約束不能是特殊類'System.Enum'”),因此擴展方法將在所有結構的intellisense中出現。 CheckEnumWithFlags方法,以檢查類型實際上是枚舉,並具有Flags屬性。


更新:喬恩·斯凱特(Jon Skeet)最近啟動了一個有趣的庫,名為UnconstrainedMelody ,該庫可以執行完全相同的操作,並且可以解決上述通用類型約束限制

這應該對具有任何基礎類型的enum類型起作用:

public static bool IsSet<T>(this T value, T flags) where T : struct
{
    return (Convert.ToInt64(value) & Convert.ToInt64(flags)) ==
        Convert.ToInt64(flags);
}

之所以使用Convert.ToInt64是因為64位整數是可能的“最大”整數類型,所有枚舉值都可以轉換為整數(甚至ulong )。 請注意, char不是有效的基礎類型。 看來,這是不是在C#有效的,但它在一般有效的CIL /為CLR。

另外,您不能對枚舉強制使用通用類型約束(即where T : struct ); 最好的辦法是使用where T : structT強制為值類型,然后有選擇地執行動態檢查以確保T為枚舉類型。

為了完整起見,這是我非常簡短的測試工具:

static class Program
{
    static void Main(string[] args)
    {
        Debug.Assert(Foo.abc.IsSet(Foo.abc));
        Debug.Assert(Bar.def.IsSet(Bar.def));
        Debug.Assert(Baz.ghi.IsSet(Baz.ghi));
    }

    enum Foo : int
    {
        abc = 1,
        def = 10,
        ghi = 100
    }

    enum Bar : sbyte
    {
        abc = 1,
        def = 10,
        ghi = 100
    }

    enum Baz : ulong
    {
        abc = 1,
        def = 10,
        ghi = 100
    }
}

就個人而言,我認為它看起來不錯,因為您已將其包裝到一個單一目的的函數中。 如果您將代碼分散在整個程序中,我認為您會遇到一些問題,但是您創建的內容可以在使用該代碼的地方提高清晰度,而函數本身也很清楚其功能。

當然只是我的意見。

不過,您可以使用is關鍵字,這可能會有所幫助

public static bool IsSet<T>(this T value, T flags) where T : Enum
{ 
    if (value is int)
    {
        return ((int)(object)a & (int)(object)b) == (int)(object)b);
    }
    //etc...

只需使用Enum.HasFlag()方法!

我用它來比較標志

public static bool IsSet<T>(this T input, T match)
{
    return (Convert.ToUInt32(input) & Convert.ToUInt32(match)) != 0;
}

您可以在這里進行不同的轉換。 從int到short到long。

或...公共靜態布爾IsSet(此Enum值,Enum比較){int baseValue = value.ToInt32(); int compareValue = compare.ToInt32(); 如果(baseValue == 0)返回false; return((baseValue&compareValue)== compareValue); }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM