web-dev-qa-db-ja.com

Enum.HasFlag、なぜEnum.SetFlagがないのですか?

次のように、宣言するフラグタイプごとに拡張メソッドを作成する必要があります。

_public static EventMessageScope SetFlag(this EventMessageScope flags, 
    EventMessageScope flag, bool value)
{
    if (value)
        flags |= flag;
    else
        flags &= ~flag;

    return flags;
}
_

_Enum.SetFlag_のように_Enum.HasFlag_がないのはなぜですか?

また、なぜこれが常に機能しないのですか?

_public static bool Get(this EventMessageScope flags, EventMessageScope flag)
{
    return ((flags & flag) != 0);
}
_

たとえば、私が持っている場合:

_var flag = EventMessageScope.Private;
_

そしてそれを次のようにチェックしてください:

_if(flag.Get(EventMessageScope.Public))
_

_EventMessageScope.Public_が実際に_EventMessageScope.Private | EventMessageScope.PublicOnly_である場合、trueを返します。

公開されていない場合、Privateは公開されていないため、公開されているのは半分です。

同じことが当てはまります:

if(flag.Get(EventMessageScope.None))

スコープが実際にはfalse(_0x0_)である場合を除き、どちらがNoneを返しますか。

33
bevacqua

&演算子は、a & bと同じ答えをb & aと同じように与えるので、

(EventMessaageScope.Private).Get(EventMessageScope.Private | EventMessageScope.PublicOnly)

書くことと同じです

(EventMessageScope.Private | EventMessageScope.PublicOnly).Get(EventMessaageScope.Private)

値がEventMessaageScope.Publicと同じsameかどうかを知りたい場合は、equals

EventMessageScope.Private == EventMessageScope.Public

メソッドは常に(EventMessageScope.None).Get(EventMessaageScope.None)に対してfalseを返します。これは、None == 0であり、AND演算の結果がnotゼロの場合にのみtrueを返すためです。 0 & 0 == 0

3
Mark Cidade

Enum.HasFlagのようにEnum.SetFlagがないのはなぜですか?

HasFlagはビット単位の演算であるため、より複雑なロジックと同じフラグを2回繰り返す必要がありました。

 myFlagsVariable=    ((myFlagsVariable & MyFlagsEnum.MyFlag) ==MyFlagsEnum.MyFlag );

そのため、MSはそれを実装することを決定しました。

SetFlagとClearFlagはC#では簡潔です

    flags |= flag;// SetFlag

    flags &= ~flag; // ClearFlag 

残念ながら直感的ではありません。フラグを設定(またはクリア)する必要があるたびに、数秒(または数分)を費やして考えています。メソッドの名前は何ですか。インテリセンスで表示されないのはなぜですか?いいえ、ビット演算を使用する必要があります。一部の開発者はまた尋ねることに注意してください:ビット演算とは何ですか?

SetFlagおよびClearFlag拡張を作成する必要があります-はい、インテリセンスに表示されます。

SetFlagおよびClearFlag拡張機能は、開発者が使用する必要があります-効率が悪いためです。

SomeEnumHelperMethodsThatMakeDoingWhatYouWantEasier のように、ライブラリのクラスEnumFlagsHelperに拡張機能を作成しましたが、関数の名前を、RemoveではなくIncludeおよびClearFlagではなくSetFlagにしています。

SetFlagメソッドの本文(および要約コメント)に、追加することにしました

Debug.Assert( false, " do not use the extension due to performance reason, use bitwise operation with the explanatory comment instead \n 
flags |= flag;// SetFlag")

同様のメッセージをClearFlagに追加する必要があります

Debug.Assert( false, " do not use the extension due to performance reason, use bitwise operation with the explanatory comment instead \n 
         flags &= ~flag; // ClearFlag  ")
36
public static class SomeEnumHelperMethodsThatMakeDoingWhatYouWantEasier
{
    public static T IncludeAll<T>(this Enum value)
    {
        Type type = value.GetType();
        object result = value;
        string[] names = Enum.GetNames(type);
        foreach (var name in names)
        {
            ((Enum) result).Include(Enum.Parse(type, name));
        }

        return (T) result;
        //Enum.Parse(type, result.ToString());
    }

    /// <summary>
    /// Includes an enumerated type and returns the new value
    /// </summary>
    public static T Include<T>(this Enum value, T append)
    {
        Type type = value.GetType();

        //determine the values
        object result = value;
        var parsed = new _Value(append, type);
        if (parsed.Signed is long)
        {
            result = Convert.ToInt64(value) | (long) parsed.Signed;
        }
        else if (parsed.Unsigned is ulong)
        {
            result = Convert.ToUInt64(value) | (ulong) parsed.Unsigned;
        }

        //return the final value
        return (T) Enum.Parse(type, result.ToString());
    }

    /// <summary>
    /// Check to see if a flags enumeration has a specific flag set.
    /// </summary>
    /// <param name="variable">Flags enumeration to check</param>
    /// <param name="value">Flag to check for</param>
    /// <returns></returns>
    public static bool HasFlag(this Enum variable, Enum value)
    {
        if (variable == null)
            return false;

        if (value == null)
            throw new ArgumentNullException("value");

        // Not as good as the .NET 4 version of this function, 
        // but should be good enough
        if (!Enum.IsDefined(variable.GetType(), value))
        {
            throw new ArgumentException(string.Format(
                "Enumeration type mismatch.  The flag is of type '{0}', " +
                "was expecting '{1}'.", value.GetType(), 
                variable.GetType()));
        }

        ulong num = Convert.ToUInt64(value);
        return ((Convert.ToUInt64(variable) & num) == num);
    }


    /// <summary>
    /// Removes an enumerated type and returns the new value
    /// </summary>
    public static T Remove<T>(this Enum value, T remove)
    {
        Type type = value.GetType();

        //determine the values
        object result = value;
        var parsed = new _Value(remove, type);
        if (parsed.Signed is long)
        {
            result = Convert.ToInt64(value) & ~(long) parsed.Signed;
        }
        else if (parsed.Unsigned is ulong)
        {
            result = Convert.ToUInt64(value) & ~(ulong) parsed.Unsigned;
        }

        //return the final value
        return (T) Enum.Parse(type, result.ToString());
    }

    //class to simplfy narrowing values between
    //a ulong and long since either value should
    //cover any lesser value
    private class _Value
    {
        //cached comparisons for tye to use
        private static readonly Type _UInt32 = typeof (long);
        private static readonly Type _UInt64 = typeof (ulong);

        public readonly long? Signed;
        public readonly ulong? Unsigned;

        public _Value(object value, Type type)
        {
            //make sure it is even an enum to work with
            if (!type.IsEnum)
            {
                throw new ArgumentException(
                    "Value provided is not an enumerated type!");
            }

            //then check for the enumerated value
            Type compare = Enum.GetUnderlyingType(type);

            //if this is an unsigned long then the only
            //value that can hold it would be a ulong
            if (compare.Equals(_UInt32) || compare.Equals(_UInt64))
            {
                Unsigned = Convert.ToUInt64(value);
            }
                //otherwise, a long should cover anything else
            else
            {
                Signed = Convert.ToInt64(value);
            }
        }
    }
}
10
smartcaveman

私は自分のために働くことをしました、そしてそれはとても簡単です...

    public static T SetFlag<T>(this Enum value, T flag, bool set)
    {
        Type underlyingType = Enum.GetUnderlyingType(value.GetType());

        // note: AsInt mean: math integer vs enum (not the c# int type)
        dynamic valueAsInt = Convert.ChangeType(value, underlyingType);
        dynamic flagAsInt = Convert.ChangeType(flag, underlyingType);
        if (set)
        {
            valueAsInt |= flagAsInt;
        }
        else
        {
            valueAsInt &= ~flagAsInt;
        }

        return (T)valueAsInt;
    }

使用法:

    var fa = FileAttributes.Normal;
    fa = fa.SetFlag(FileAttributes.Hidden, true);
8
Eric Ouellet

EnumのSetFlagをすばやく簡単に変更する方法を次に示します。

public static T SetFlag<T>(this T flags, T flag, bool value) where T : struct, IComparable, IFormattable, IConvertible
    {
        int flagsInt = flags.ToInt32(NumberFormatInfo.CurrentInfo);
        int flagInt = flag.ToInt32(NumberFormatInfo.CurrentInfo);
        if (value)
        {
            flagsInt |= flagInt;
        }
        else
        {
            flagsInt &= ~flagInt;
        }
        return (T)(Object)flagsInt;
    }
1
Jeri Haapavuo

あなたの質問の一部に答えるために:Get関数は、バイナリロジックに従って適切に機能します。一致するかどうかをチェックします。フラグのセット全体を照合する場合は、代わりにこれを検討してください。

return ((flags & flag) != flag);

「なぜSetFlagがないのか」については、おそらく本当に必要ないためでしょう。フラグは整数です。それらを処理するための規則はすでにあり、フラグにも適用されます。 |&で記述したくない場合は、それがカスタム静的アドオンの目的です-デモで示したように、独自の関数を使用できます:)

1
viraptor

Enumsは、ずっと前にC言語に悩まされていました。 C#言語でわずかな型安全性を確保することは、設計者にとって重要であり、基礎となる型がバイトとロングの間の任意のものである場合はEnum.SetFlagsの余地を残しません。別のCが問題を引き起こしました。

これに対処する適切な方法は、この種のコードをインラインで明示的に記述し、notを使用して拡張メソッドに押し込むことです。 CマクロをC#言語で記述したくない。

1
Hans Passant