14

フラグが列挙値内に設定されているかどうかを検出する必要があります。このタイプはFlag属性でマークされています。

通常、次のように作成されます。

(value & flag) == flag

しかし、これを汎用で行う必要があるため(実行時に、イベントに「列挙型」参照しかない場合があります。&演算子を使用する簡単な方法が見つかりません。現時点では、次のようにしています。

    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ブロックが好きではないので、&演算子または他のソリューションを使用してこれをチェックできるこれらの値をキャストする方法はありますか?

4

7 に答える 7

15

私にとって、それは過度に複雑に見えます。これはどうですか(列挙型は常に整数値型にマップされることに注意してください):

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;
}
于 2009-07-06T12:17:33.320 に答える
10

必要な場合に備えて、列挙型の拡張メソッドのセットを作成しました。

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ことです。明示的に禁止されているため( "Constraint can be special class'System.Enum'")、拡張メソッドはすべての構造体に対してインテリセンスで表示されます...CheckEnumWithFlagsチェックするメソッドを追加しましたタイプは実際には列挙型であり、Flags属性を持っています。


更新:Jon Skeetは最近、 UnconstrainedMelodyと呼ばれる興味深いライブラリを開始しました。これは、まったく同じ種類のことを実行し、上記のジェネリック型制約の制限を回避します。

于 2009-07-06T12:36:21.093 に答える
7

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.ToInt6464ビット整数が可能な限り「最も広い」整数型であり、すべての列挙値をキャストできるために使用されます(偶数ulong)。は有効な基になるタイプではないことに注意してください。charC#では無効のようです、一般的にCIL/CLRでは有効です。

また、列挙型にジェネリック型の制約を適用することはできません(つまりwhere T : struct)。最善の方法は、値型になるようwhere T : structに強制Tし、オプションで動的チェックを実行して、それ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
    }
}
于 2009-07-06T12:16:05.753 に答える
6

個人的には、単一目的の関数にラップしているので、見栄えが良いと思います。そのコードがプログラム全体に散らばっていたら、いくつかの問題があると思いますが、作成したものは、それが使用されるすべての場所で明確さを向上させ、関数自体はそれが何をするかを十分に明確にします。

もちろん私の意見です。

ただし、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...
于 2009-07-06T12:13:12.243 に答える
2

単にEnum.HasFlag()メソッドを使用してください!

于 2010-08-12T10:16:15.180 に答える
0

または...publicstatic bool IsSet(this Enum value、Enum compare){int baseValue = value.ToInt32(); int compareValue = compare.ToInt32(); if(baseValue == 0)はfalseを返します。return((baseValue&compareValue)== compareValue); }

于 2009-07-28T08:21:09.903 に答える
0

私はこれを使って旗を比較しました

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

ここでは、さまざまな変換を行うことができます。intからshort、longまで。

于 2009-07-06T12:16:38.017 に答える