C#で[フラグ]列挙型属性とはどういう意味ですか?


1447

時々、次のような列挙型が表示されます。

[Flags]
public enum Options 
{
    None    = 0,
    Option1 = 1,
    Option2 = 2,
    Option3 = 4,
    Option4 = 8
}

[Flags]属性が何をするのか正確にはわかりません。

誰かが彼らが投稿できる良い説明や例がありますか?


認められた回答に加えて、VB.NETが実際 [フラグ]を必要とすることも注目に値します-少なくとも.NETの連中によれば、social.msdn.microsoft.com
Rushyo

8
最近のVBでは必要ありません。動作をC#として保存-ToString()出力を変更するだけです。Enum自体の中で論理ORを行うこともできます。とてもかっこいい。猫= 1、犬= 2、CatAndDog =猫|| 犬。
チョーキー2015

14
@Chalky CatAndDog = Cat | Dog(つまり、条件付きの代わりに論理OR)という意味ですか?
DdW 2016

5
@DdW、部分的に正しい:| 使用する必要がありますが、| バイナリORと呼ばれます。IIは論理OR(短絡を可能にする)です:少なくともMicrosoftによると;)msdn.microsoft.com/en-us/library/f355wky8.aspx
Pieter21

回答:


2154

[Flags]列挙は、可能な値の集合ではなく、単一の値を表しているときはいつでも属性を使用すべきです。このようなコレクションは、多くの場合、ビットごとの演算子とともに使用されます。次に例を示します。

var allowedColors = MyColor.Red | MyColor.Green | MyColor.Blue;

[Flags]属性これだけではこれを有効にしないことに注意してください。属性でできることは、.ToString()メソッドによる適切な表現を可能にすることだけです。

enum Suits { Spades = 1, Clubs = 2, Diamonds = 4, Hearts = 8 }
[Flags] enum SuitsFlags { Spades = 1, Clubs = 2, Diamonds = 4, Hearts = 8 }

...

var str1 = (Suits.Spades | Suits.Diamonds).ToString();
           // "5"
var str2 = (SuitsFlags.Spades | SuitsFlags.Diamonds).ToString();
           // "Spades, Diamonds"

は列挙値を2のべき乗に自動的に設定[Flags] しないことに注意することも重要です。数値を省略すると、デフォルトでは値が0から始まりインクリメントされるため、ビットごとの演算で期待されるようにenumは機能しません。

誤った宣言:

[Flags]
public enum MyColors
{
    Yellow,  // 0
    Green,   // 1
    Red,     // 2
    Blue     // 3
}

この方法で宣言した場合、値は黄色= 0、緑= 1、赤= 2、青= 3になります。これにより、フラグとして使用できなくなります。

正しい宣言の例を次に示します。

[Flags]
public enum MyColors
{
    Yellow = 1,
    Green = 2,
    Red = 4,
    Blue = 8
}

プロパティの個別の値を取得するには、次のようにします。

if (myProperties.AllowedColors.HasFlag(MyColor.Yellow))
{
    // Yellow is allowed...
}

または.NET 4より前:

if((myProperties.AllowedColors & MyColor.Yellow) == MyColor.Yellow)
{
    // Yellow is allowed...
}

if((myProperties.AllowedColors & MyColor.Green) == MyColor.Green)
{
    // Green is allowed...
}    

カバーの下に

これは、列挙で2の累乗を使用したため機能します。内部では、列挙値はバイナリの1と0で次のようになります。

 Yellow: 00000001
 Green:  00000010
 Red:    00000100
 Blue:   00001000

同様に、バイナリビットOR 演算子を使用してプロパティAllowedColorsをRed、Green、Blueに設定すると|AllowedColorsは次のようになります。

myProperties.AllowedColors: 00001110

したがって、値を取得するとき、実際&には値に対してビットAND を実行しています。

myProperties.AllowedColors: 00001110
             MyColor.Green: 00000010
             -----------------------
                            00000010 // Hey, this is the same as MyColor.Green!

なし= 0の値

そして0、あなたの列挙での使用に関して、MSDNからの引用:

[Flags]
public enum MyColors
{
    None = 0,
    ....
}

値がゼロのフラグ列挙定数の名前としてNoneを使用します。結果は常にゼロであるため、ビットごとのAND演算でNone列挙定数を使用してフラグをテストすることはできません。ただし、数値とNone列挙定数の間でビット単位ではなく論理比較を実行して、数値のビットが設定されているかどうかを判別できます。

msdnでflags属性とその使用法に関する詳細情報を、msdnフラグを設計することができます。


156
フラグ自体は何もしません。また、C#ではフラグ自体は必要ありません。しかし、ToStringあなたの列挙型の実装がないので、フラグを使用し、かつEnum.IsDefinedEnum.ParseなどMyColor.Yellowの結果で国旗や外観を削除するようにしてください| MyColor.Red; それなしでは「5」、Flagsでは「黄、赤」になります。フレームワークの他のいくつかの部分も[フラグ]を使用します(例:XMLシリアル化)。
ルーベン

7
//黄色が設定されています...、少し誤解を招きます。何も設定されていません。これは、黄色がAllowedColorsのメンバーであることを意味します。おそらく//黄色が許可されますか?
スコットウィーバー

48
私は、A = 1 << 0、B = 1 << 1、C = 1 << 2 ...という形式の定数を使用することを好んでいます。
Nick Westgate

2
@borrrden、地獄そう!私はこれを見つけました:msdn.microsoft.com/en-us/library/system.enum.aspx- 「備考」の部分を参照してください:「列挙型は.NET Frameworkのすべての列挙型の基本クラスです。」および「列挙はEnumから明示的に継承されません。継承関係はコンパイラによって暗黙的に処理されます。」だから、あなたが書くとき:public enum bla bla bla-これは値型です。しかし、HasFlagメソッドは、クラス(参照タイプ:)であるSystem.Enumのインスタンスを彼に与えることを望んでいます
Aleksei Chepovoi 2013年

2
列挙型からフラグを除外するには、C#では^であるxorを使用します。だからあなたが持っている場合myProperties.AllowedColors = MyColor.Red | MyColor.Green | MyColor.Blue;。あなたは行うことができます:myProperties.AllowedColors = myProperties.AllowedColors ^ MyColor.Blue //myProperties.AllowedColors == MyColor.Red | Mycolor.Green
ジョシュ・ノエ

779

これもできます

[Flags]
public enum MyEnum
{
    None   = 0,
    First  = 1 << 0,
    Second = 1 << 1,
    Third  = 1 << 2,
    Fourth = 1 << 3
}

4、8、16、32などと入力するよりもビットシフトが簡単だと思います。すべてコンパイル時に行われるため、コードに影響を与えません


18
つまり、私は完全なintをソースコードに含めることを好みます。MyEnumと呼ばれるデータベース内のテーブルに、列挙型の1つの値を格納する列があり、レコードに131,072がある場合、値1の列挙型に対応するものを計算するために電卓を取得する必要があります。 << 17。ソースに書き込まれた値131,072を表示するだけではありません。
JeremyWeir

6
@jwg同意します。これの実行時のパフォーマンスを過度に心配するのはばかげていますが、同じことですが、これが列挙型を使用する場所にビットシフトを挿入しないことは知っていると思います。パフォーマンスに関連するものではなく、「それはきちんとした」ものの多く
オリオンエドワーズ

18
@JeremyWeir-フラグ列挙値にいくつかのビットが設定されます。したがって、データ分析の方法は不適切です。整数値をバイナリで表すためのプロシージャを実行します。131,072 [D] = 0000 0000 0000 0001 0000 0000 0000 0000 [B] [32] .. 17番目のビットを設定すると、1 << 17が割り当てられた列挙値を簡単に特定できます。0110 0011 0011 0101 0101 0011 0101 1011 [b] [32] ..割り当てられた列挙値1 << 31、1 << 30、1 << 26、1 << 25など。計算機の助けを借りて..バイナリの担当者がいなくても、それを決定できるとは思えません。
Brett Caswell 2013年

13
また、あなたは「前」列挙値ではなく、直接の番号からのシフトは、ビット、つまりはでき指摘Third = Second << 1するのではなくThird = 1 << 2参照- 以下でより完全な説明を
drzaus

26
このような私が、あなたは根本的な列挙型の上限に到達すると、コンパイラは、次のようなビットシフトに対して警告しません1 << 31 == -21474836481 << 32 == 11 << 33 == 2、など。対照的に、ThirtySecond = 2147483648int型の列挙型に対して言うと、コンパイラーはエラーをスローします。
aaaantoine 2014年

115

https://stackoverflow.com/a/8462/1037948(ビットシフトによる宣言)とhttps://stackoverflow.com/a/9117/1037948(宣言で組み合わせを使用)の回答を組み合わせると、以前の値をビットシフトできます数字を使うよりも。必ずしも推奨しているわけではありませんが、推奨しているだけです。

のではなく:

[Flags]
public enum Options : byte
{
    None    = 0,
    One     = 1 << 0,   // 1
    Two     = 1 << 1,   // 2
    Three   = 1 << 2,   // 4
    Four    = 1 << 3,   // 8

    // combinations
    OneAndTwo = One | Two,
    OneTwoAndThree = One | Two | Three,
}

宣言できます

[Flags]
public enum Options : byte
{
    None    = 0,
    One     = 1 << 0,       // 1
    // now that value 1 is available, start shifting from there
    Two     = One << 1,     // 2
    Three   = Two << 1,     // 4
    Four    = Three << 1,   // 8

    // same combinations
    OneAndTwo = One | Two,
    OneTwoAndThree = One | Two | Three,
}

LinqPadで確認:

foreach(var e in Enum.GetValues(typeof(Options))) {
    string.Format("{0} = {1}", e.ToString(), (byte)e).Dump();
}

結果:

None = 0
One = 1
Two = 2
OneAndTwo = 3
Three = 4
OneTwoAndThree = 7
Four = 8

23
組み合わせは良い推奨事項ですが、チェーンされたビットシフトは、2 = One << 1、3 = One << 1などのコピーアンドペーストエラーを起こしやすいと思います...の増加する整数フォーム1 << nの方が安全であり、意図がより明確です。
Rupert Rawnsley 2014年

2
@RupertRawnsleyが私の回答を引用します:>必ずしもそれを推奨するわけではありませんが、あなたができることを指摘するだけです
drzaus 14年

49

宣言と潜在的な使用法を示す例については、以下を参照してください。

namespace Flags
{
    class Program
    {
        [Flags]
        public enum MyFlags : short
        {
            Foo = 0x1,
            Bar = 0x2,
            Baz = 0x4
        }

        static void Main(string[] args)
        {
            MyFlags fooBar = MyFlags.Foo | MyFlags.Bar;

            if ((fooBar & MyFlags.Foo) == MyFlags.Foo)
            {
                Console.WriteLine("Item has Foo flag set");
            }
        }
    }
}

この例は、[フラグ]を省略しても機能します。それは私が学ぼうとしている[フラグ]の部分です。
gbarry

39

受け入れられた答えの拡張として、C#7では、列挙フラグはバイナリリテラルを使用して記述できます。

[Flags]
public enum MyColors
{
    None   = 0b0000,
    Yellow = 0b0001,
    Green  = 0b0010,
    Red    = 0b0100,
    Blue   = 0b1000
}

この表現は、フラグが隠された状態でどのように機能するかを明確にすると思います。


2
そして、C#7.2では、先行セパレーターでさらに明確になりました! 0b_0100
Vincenzo Petronio、

37

私は最近尋ね似た何か。

フラグを使用する場合は、列挙型に拡張メソッドを追加して、含まれているフラグのチェックを簡単にすることができます(詳細については投稿を参照)

これにより、次のことが可能になります。

[Flags]
public enum PossibleOptions : byte
{
    None = 0,
    OptionOne = 1,
    OptionTwo = 2,
    OptionThree = 4,
    OptionFour = 8,

    //combinations can be in the enum too
    OptionOneAndTwo = OptionOne | OptionTwo,
    OptionOneTwoAndThree = OptionOne | OptionTwo | OptionThree,
    ...
}

次に、次のことができます。

PossibleOptions opt = PossibleOptions.OptionOneTwoAndThree 

if( opt.IsSet( PossibleOptions.OptionOne ) ) {
    //optionOne is one of those set
}

これは、含まれているフラグをチェックするほとんどの方法よりも読みやすいと思います。


IsSetは私が想定している拡張メソッドですか?
Robert MacLean

うん-詳細については、私がリンクしている他の質問を読んでください:stackoverflow.com/questions/7244
Keith

71
.NET 4はHasFlag列挙型にメソッドを追加するopt.HasFlag( PossibleOptions.OptionOne )ため、独自の拡張機能を記述することなく実行できます
Orion Edwards

1
HasFlagはビット単位の操作を行うよりはるかに遅いことに注意してください。
Wai Ha Lee

1
@WaiHaLee式を使用して高速バージョンにコンパイルされたCodeJam.EnumHelper.IsFlagSet拡張メソッドを使用できます:github.com/rsdn/CodeJam/wiki/M_CodeJam_EnumHelper_IsFlagSet__1
NN_

22

ニドノク

既存の値のセットに別のフラグを追加するには、OR代入演算子を使用します。

Mode = Mode.Read;
//Add Mode.Write
Mode |= Mode.Write;
Assert.True(((Mode & Mode.Write) == Mode.Write)
  && ((Mode & Mode.Read) == Mode.Read)));

18

フラグを操作するときは、追加のNoneおよびAllアイテムを宣言することがよくあります。これらは、すべてのフラグが設定されているか、フラグが設定されていないかを確認するのに役立ちます。

[Flags] 
enum SuitsFlags { 

    None =     0,

    Spades =   1 << 0, 
    Clubs =    1 << 1, 
    Diamonds = 1 << 2, 
    Hearts =   1 << 3,

    All =      ~(~0 << 4)

}

使用法:

Spades | Clubs | Diamonds | Hearts == All  // true
Spades & Clubs == None  // true

 
アップデート2019-10:

C#7.0以降、バイナリリテラルを使用できます。

[Flags] 
enum SuitsFlags { 

    None =     0b0000,

    Spades =   0b0001, 
    Clubs =    0b0010, 
    Diamonds = 0b0100, 
    Hearts =   0b1000,

    All =      0b1111

}


14

if ((x & y) == y)...特にxAND yが両方ともフラグの複合セットであり重複があるかどうかのみを知りたい場合は、構成について非常に冗長なものがあります。

この場合、ビットマスクした後にゼロ以外の値[1]が存在するかどうかを知る必要があるだけです。

[1] Jaimeのコメントを参照してください。本物のビットマスキングを行っている場合は、結果が良好であることを確認するだけで済みます。ただし、enums [Flags] 属性と組み合わせると、奇妙なことに、負になる可能性があるため、!= 0ではなくのコードに対して防御的です> 0

@andnilのセットアップから構築...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BitFlagPlay
{
    class Program
    {
        [Flags]
        public enum MyColor
        {
            Yellow = 0x01,
            Green = 0x02,
            Red = 0x04,
            Blue = 0x08
        }

        static void Main(string[] args)
        {
            var myColor = MyColor.Yellow | MyColor.Blue;
            var acceptableColors = MyColor.Yellow | MyColor.Red;

            Console.WriteLine((myColor & MyColor.Blue) != 0);     // True
            Console.WriteLine((myColor & MyColor.Red) != 0);      // False                
            Console.WriteLine((myColor & acceptableColors) != 0); // True
            // ... though only Yellow is shared.

            Console.WriteLine((myColor & MyColor.Green) != 0);    // Wait a minute... ;^D

            Console.Read();
        }
    }
}

列挙型は署名された型に基づいている可能性があるため、 "> 0"の代わりに "!= 0"を使用する必要があります。
レイヴン2013

@JaimePardos-私が正直なバイトを保持している限り、この例のように、否定的な概念はありません。ただ0から255。MSDNが警告するように、「フラグ列挙定数として負の数を定義する場合は注意してください。コードが混乱し、コーディングエラーが発生する可能性があるためです。」「負のビットフラグ」の観点から考えるのは奇妙です!; ^)後でもっと編集します。しかし、そうです。もしで負の値を使用する場合はenum、を確認する必要があり!= 0ます。
ruffin 2013

10

フラグを使用すると、列挙内でビットマスキングを使用できます。これにより、指定された値を保持しながら、列挙値を組み合わせることができます。

[Flags]
public enum DashboardItemPresentationProperties : long
{
    None = 0,
    HideCollapse = 1,
    HideDelete = 2,
    HideEdit = 4,
    HideOpenInNewWindow = 8,
    HideResetSource = 16,
    HideMenu = 32
}

0

誰かがすでにこのシナリオに気づいていたら申し訳ありません。反射で見ることができるフラグの完璧な例。はいバインディングフラグENUM。 https://docs.microsoft.com/en-us/dotnet/api/system.reflection.bindingflags?view=netframework-4.8

[System.Flags]
[System.Runtime.InteropServices.ComVisible(true)]
[System.Serializable]
public enum BindingFlags

使用法

             // BindingFlags.InvokeMethod
            // Call a static method.
            Type t = typeof (TestClass);

            Console.WriteLine();
            Console.WriteLine("Invoking a static method.");
            Console.WriteLine("-------------------------");
            t.InvokeMember ("SayHello", BindingFlags.InvokeMethod | BindingFlags.Public | 
                BindingFlags.Static, null, null, new object [] {});
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.