System.FlagsAttribute, klasa

Ten artykuł zawiera dodatkowe uwagi dotyczące dokumentacji referencyjnej dla tego interfejsu API.

Atrybut FlagsAttribute wskazuje, że wyliczenie może być traktowane jako pole bitowe, czyli zestaw flag.

Pola bitowe są zwykle używane w przypadku list elementów, które mogą wystąpić w połączeniu, podczas gdy stałe wyliczenia są zwykle używane dla list wzajemnie wykluczających się elementów. W związku z tym pola bitowe są przeznaczone do łączenia z operacją bitową OR w celu generowania nienazwanych wartości, natomiast wyliczone stałe nie są. Języki różnią się w zależności od ich użycia pól bitowych w porównaniu do stałych wyliczenia.

Atrybuty flagsAttribute

AttributeUsageAttribute jest stosowany do tej klasy, a jej Inherited właściwość określa false. Ten atrybut można zastosować tylko do wyliczenia.

Wytyczne dotyczące flagsAttribute i wyliczenia

  • Użyj atrybutu niestandardowego FlagsAttribute dla wyliczenia tylko wtedy, gdy operacja bitowa (AND, OR, EXCLUSIVE OR) ma być wykonywana na wartości liczbowej.

  • Zdefiniuj stałe wyliczenia w uprawnieniach dwóch, czyli 1, 2, 4, 8 itd. Oznacza to, że poszczególne flagi w połączonych stałych wyliczenia nie nakładają się na siebie.

  • Rozważ utworzenie stałej wyliczonej dla powszechnie używanych kombinacji flag. Jeśli na przykład masz wyliczenie używane dla operacji we/wy plików, które zawierają wyliczone stałe Read = 1 i Write = 2, rozważ utworzenie wyliczonej stałej ReadWrite = Read OR Write, która łączy Read flagi i Write . Ponadto bitowa operacja OR używana do łączenia flag może być uznawana za zaawansowaną koncepcję w pewnych okolicznościach, które nie powinny być wymagane w przypadku prostych zadań.

  • Zachowaj ostrożność, jeśli zdefiniujesz liczbę ujemną jako stałą wyliczeniową flagę, ponieważ wiele pozycji flag może być ustawionych na 1, co może spowodować, że kod będzie mylący i zachęca do błędów kodowania.

  • Wygodnym sposobem sprawdzenia, czy flaga jest ustawiona w wartości liczbowej, jest wykonanie bitowej operacji AND między wartością liczbową a wyliczeniową stałą flagą, która ustawia wszystkie bity w wartości liczbowej na zero, które nie odpowiadają flagi, a następnie przetestuj, czy wynik tej operacji jest równy wyliczonej stałej flagi.

  • Użyj None jako nazwy flagi wyliczonej stałej, której wartość to zero. Nie można użyć wyliczonej stałej None w bitowej operacji AND, aby przetestować flagę, ponieważ wynik jest zawsze zerowy. Można jednak wykonać logiczne, a nie bitowe porównanie wartości liczbowej i wyliczonej stałej None , aby określić, czy są ustawione jakiekolwiek bity w wartości liczbowej.

    Jeśli utworzysz wyliczenie wartości zamiast wyliczenia flag, nadal warto utworzyć None wyliczona stała. Przyczyną jest to, że domyślnie pamięć używana do wyliczania jest inicjowana do zera przez środowisko uruchomieniowe języka wspólnego. W związku z tym jeśli nie zdefiniujesz stałej, której wartość jest równa zero, wyliczenie będzie zawierać niedozwoloną wartość podczas jej tworzenia.

    Jeśli istnieje oczywisty przypadek domyślny, który aplikacja musi reprezentować, rozważ użycie wyliczonej stałej, której wartość jest równa zero do reprezentowania wartości domyślnej. Jeśli nie ma przypadku domyślnego, rozważ użycie stałej wyliczonej, której wartość wynosi zero, co oznacza, że przypadek, który nie jest reprezentowany przez żadną z innych wyliczonych stałych.

  • Nie należy definiować wartości wyliczenia wyłącznie w celu zdublowania stanu samego wyliczenia. Na przykład nie należy definiować wyliczonej stałej, która jedynie oznacza koniec wyliczenia. Jeśli musisz określić ostatnią wartość wyliczenia, sprawdź jawnie, czy ta wartość jest jawna. Ponadto można wykonać sprawdzanie zakresu dla pierwszej i ostatniej wyliczonej stałej, jeśli wszystkie wartości w zakresie są prawidłowe.

  • Nie należy określać wyliczonych stałych zarezerwowanych do użytku w przyszłości.

  • Podczas definiowania metody lub właściwości, która przyjmuje wyliczonej stałej jako wartość, rozważ zweryfikowanie wartości. Przyczyną jest możliwość rzutowania wartości liczbowej na typ wyliczenia, nawet jeśli ta wartość liczbowa nie jest zdefiniowana w wyliczeniem.

Przykłady

Poniższy przykład ilustruje użycie atrybutu FlagsAttribute i pokazuje wpływ na ToString metodę użycia FlagsAttribute w Enum deklaracji.

using System;

class Example
{
    // Define an Enum without FlagsAttribute.
    enum SingleHue : short
    {
        None = 0,
        Black = 1,
        Red = 2,
        Green = 4,
        Blue = 8
    };

    // Define an Enum with FlagsAttribute.
    [Flags]
    enum MultiHue : short
    {
        None = 0,
        Black = 1,
        Red = 2,
        Green = 4,
        Blue = 8
    };

    static void Main()
    {
        // Display all possible combinations of values.
        Console.WriteLine(
             "All possible combinations of values without FlagsAttribute:");
        for (int val = 0; val <= 16; val++)
            Console.WriteLine("{0,3} - {1:G}", val, (SingleHue)val);

        // Display all combinations of values, and invalid values.
        Console.WriteLine(
             "\nAll possible combinations of values with FlagsAttribute:");
        for (int val = 0; val <= 16; val++)
            Console.WriteLine("{0,3} - {1:G}", val, (MultiHue)val);
    }
}
// The example displays the following output:
//       All possible combinations of values without FlagsAttribute:
//         0 - None
//         1 - Black
//         2 - Red
//         3 - 3
//         4 - Green
//         5 - 5
//         6 - 6
//         7 - 7
//         8 - Blue
//         9 - 9
//        10 - 10
//        11 - 11
//        12 - 12
//        13 - 13
//        14 - 14
//        15 - 15
//        16 - 16
//
//       All possible combinations of values with FlagsAttribute:
//         0 - None
//         1 - Black
//         2 - Red
//         3 - Black, Red
//         4 - Green
//         5 - Black, Green
//         6 - Red, Green
//         7 - Black, Red, Green
//         8 - Blue
//         9 - Black, Blue
//        10 - Red, Blue
//        11 - Black, Red, Blue
//        12 - Green, Blue
//        13 - Black, Green, Blue
//        14 - Red, Green, Blue
//        15 - Black, Red, Green, Blue
//        16 - 16
open System

// Define an Enum without FlagsAttribute.
type SingleHue =
    | None = 0
    | Black = 1
    | Red = 2
    | Green = 4
    | Blue = 8

// Define an Enum with FlagsAttribute.
[<Flags>]
type MultiHue =
    | None = 0
    | Black = 1
    | Red = 2
    | Green = 4
    | Blue = 8

// Display all possible combinations of values.
printfn "All possible combinations of values without FlagsAttribute:"
for i = 0 to 16 do
    printfn $"{i,3} - {enum<SingleHue> i:G}"

// Display all combinations of values, and invalid values.
printfn "\nAll possible combinations of values with FlagsAttribute:"
for i = 0 to 16 do
    printfn $"{i,3} - {enum<MultiHue> i:G}"

// The example displays the following output:
//       All possible combinations of values without FlagsAttribute:
//         0 - None
//         1 - Black
//         2 - Red
//         3 - 3
//         4 - Green
//         5 - 5
//         6 - 6
//         7 - 7
//         8 - Blue
//         9 - 9
//        10 - 10
//        11 - 11
//        12 - 12
//        13 - 13
//        14 - 14
//        15 - 15
//        16 - 16
//
//       All possible combinations of values with FlagsAttribute:
//         0 - None
//         1 - Black
//         2 - Red
//         3 - Black, Red
//         4 - Green
//         5 - Black, Green
//         6 - Red, Green
//         7 - Black, Red, Green
//         8 - Blue
//         9 - Black, Blue
//        10 - Red, Blue
//        11 - Black, Red, Blue
//        12 - Green, Blue
//        13 - Black, Green, Blue
//        14 - Red, Green, Blue
//        15 - Black, Red, Green, Blue
//        16 - 16
Module Example
   ' Define an Enum without FlagsAttribute.
   Enum SingleHue As Short
      None = 0
      Black = 1
      Red = 2
      Green = 4
      Blue = 8
   End Enum

   ' Define an Enum with FlagsAttribute.
   <Flags()> 
   Enum MultiHue As Short
      None = 0
      Black = 1
      Red = 2
      Green = 4
      Blue = 8
   End Enum

   Sub Main()
      ' Display all possible combinations of values.
      Console.WriteLine(
           "All possible combinations of values without FlagsAttribute:")
      For val As Integer = 0 To 16
         Console.WriteLine("{0,3} - {1:G}", val, CType(val, SingleHue))
     Next 
     Console.WriteLine()
     
     ' Display all combinations of values, and invalid values.
     Console.WriteLine( 
          "All possible combinations of values with FlagsAttribute:")
     For val As Integer = 0 To 16
        Console.WriteLine( "{0,3} - {1:G}", val, CType(val, MultiHue))
     Next 
   End Sub 
End Module 
' The example displays the following output:
'       All possible combinations of values without FlagsAttribute:
'         0 - None
'         1 - Black
'         2 - Red
'         3 - 3
'         4 - Green
'         5 - 5
'         6 - 6
'         7 - 7
'         8 - Blue
'         9 - 9
'        10 - 10
'        11 - 11
'        12 - 12
'        13 - 13
'        14 - 14
'        15 - 15
'        16 - 16
'       
'       All possible combinations of values with FlagsAttribute:
'         0 - None
'         1 - Black
'         2 - Red
'         3 - Black, Red
'         4 - Green
'         5 - Black, Green
'         6 - Red, Green
'         7 - Black, Red, Green
'         8 - Blue
'         9 - Black, Blue
'        10 - Red, Blue
'        11 - Black, Red, Blue
'        12 - Green, Blue
'        13 - Black, Green, Blue
'        14 - Red, Green, Blue
'        15 - Black, Red, Green, Blue
'        16 - 16

W poprzednim przykładzie zdefiniowano dwa wyliczenia związane z kolorami i SingleHueMultiHue. Ten ostatni ma FlagsAttribute atrybut; pierwszy nie. W przykładzie pokazano różnicę w zachowaniu, gdy zakres liczb całkowitych, w tym liczby całkowite, które nie reprezentują wartości bazowych typu wyliczenia, są rzutowane na typ wyliczenia i wyświetlane ich reprezentacje ciągów. Należy na przykład zauważyć, że 3 nie może być reprezentowane jako wartość, ponieważ 3 nie jest wartością SingleHueMultiHue bazową żadnego SingleHue elementu członkowskiego, natomiast FlagsAttribute atrybut umożliwia reprezentowanie wartości 3 jako wartości Black, Red.

W poniższym przykładzie zdefiniowano inną wyliczenie z atrybutem FlagsAttribute i pokazano, jak używać operatorów logicznych i równości bitowych w celu określenia, czy co najmniej jedno pole bitowe jest ustawione w wartości wyliczenia. Możesz również użyć Enum.HasFlag metody , aby to zrobić, ale nie jest to pokazane w tym przykładzie.

using System;

[Flags]
public enum PhoneService
{
    None = 0,
    LandLine = 1,
    Cell = 2,
    Fax = 4,
    Internet = 8,
    Other = 16
}

public class Example1
{
    public static void Main()
    {
        // Define three variables representing the types of phone service
        // in three households.
        var household1 = PhoneService.LandLine | PhoneService.Cell |
                         PhoneService.Internet;
        var household2 = PhoneService.None;
        var household3 = PhoneService.Cell | PhoneService.Internet;

        // Store the variables in an array for ease of access.
        PhoneService[] households = { household1, household2, household3 };

        // Which households have no service?
        for (int ctr = 0; ctr < households.Length; ctr++)
            Console.WriteLine("Household {0} has phone service: {1}",
                              ctr + 1,
                              households[ctr] == PhoneService.None ?
                                  "No" : "Yes");
        Console.WriteLine();

        // Which households have cell phone service?
        for (int ctr = 0; ctr < households.Length; ctr++)
            Console.WriteLine("Household {0} has cell phone service: {1}",
                              ctr + 1,
                              (households[ctr] & PhoneService.Cell) == PhoneService.Cell ?
                                 "Yes" : "No");
        Console.WriteLine();

        // Which households have cell phones and land lines?
        var cellAndLand = PhoneService.Cell | PhoneService.LandLine;
        for (int ctr = 0; ctr < households.Length; ctr++)
            Console.WriteLine("Household {0} has cell and land line service: {1}",
                              ctr + 1,
                              (households[ctr] & cellAndLand) == cellAndLand ?
                                 "Yes" : "No");
        Console.WriteLine();

        // List all types of service of each household?//
        for (int ctr = 0; ctr < households.Length; ctr++)
            Console.WriteLine("Household {0} has: {1:G}",
                              ctr + 1, households[ctr]);
        Console.WriteLine();
    }
}
// The example displays the following output:
//    Household 1 has phone service: Yes
//    Household 2 has phone service: No
//    Household 3 has phone service: Yes
//
//    Household 1 has cell phone service: Yes
//    Household 2 has cell phone service: No
//    Household 3 has cell phone service: Yes
//
//    Household 1 has cell and land line service: Yes
//    Household 2 has cell and land line service: No
//    Household 3 has cell and land line service: No
//
//    Household 1 has: LandLine, Cell, Internet
//    Household 2 has: None
//    Household 3 has: Cell, Internet
open System

[<Flags>]
type PhoneService =
    | None = 0
    | LandLine = 1
    | Cell = 2
    | Fax = 4
    | Internet = 8
    | Other = 16

// Define three variables representing the types of phone service
// in three households.
let household1 = 
    PhoneService.LandLine ||| PhoneService.Cell ||| PhoneService.Internet

let household2 = 
    PhoneService.None

let household3 = 
    PhoneService.Cell ||| PhoneService.Internet

// Store the variables in a list for ease of access.
let households =
    [ household1; household2; household3 ]

// Which households have no service?
for i = 0 to households.Length - 1 do
    printfn $"""Household {i + 1} has phone service: {if households[i] = PhoneService.None then "No" else "Yes"}"""
printfn ""

// Which households have cell phone service?
for i = 0 to households.Length - 1 do
    printfn $"""Household {i + 1} has cell phone service: {if households[i] &&& PhoneService.Cell = PhoneService.Cell then "Yes" else "No"}"""
printfn ""

// Which households have cell phones and land lines?
let cellAndLand = 
    PhoneService.Cell ||| PhoneService.LandLine

for i = 0 to households.Length - 1 do
    printfn $"""Household {i + 1} has cell and land line service: {if households[i] &&& cellAndLand = cellAndLand then "Yes" else "No"}"""
printfn ""

// List all types of service of each household?//
for i = 0 to households.Length - 1 do
    printfn $"Household {i + 1} has: {households[i]:G}"

// The example displays the following output:
//    Household 1 has phone service: Yes
//    Household 2 has phone service: No
//    Household 3 has phone service: Yes
//
//    Household 1 has cell phone service: Yes
//    Household 2 has cell phone service: No
//    Household 3 has cell phone service: Yes
//
//    Household 1 has cell and land line service: Yes
//    Household 2 has cell and land line service: No
//    Household 3 has cell and land line service: No
//
//    Household 1 has: LandLine, Cell, Internet
//    Household 2 has: None
//    Household 3 has: Cell, Internet
<Flags()>
Public Enum PhoneService As Integer
   None = 0
   LandLine = 1
   Cell = 2
   Fax = 4
   Internet = 8
   Other = 16
End Enum

Module Example1
   Public Sub Main()
      ' Define three variables representing the types of phone service
      ' in three households.
      Dim household1 As PhoneService = PhoneService.LandLine Or
                                       PhoneService.Cell Or
                                       PhoneService.Internet
      Dim household2 As PhoneService = PhoneService.None
      Dim household3 As PhoneService = PhoneService.Cell Or
                                       PhoneService.Internet

      ' Store the variables in an array for ease of access.
      Dim households() As PhoneService = { household1, household2,
                                           household3 }

      ' Which households have no service?
      For ctr As Integer = 0 To households.Length - 1
         Console.WriteLine("Household {0} has phone service: {1}",
                           ctr + 1,
                           If(households(ctr) = PhoneService.None,
                              "No", "Yes"))
      Next
      Console.WriteLine()
      
      ' Which households have cell phone service?
      For ctr As Integer = 0 To households.Length - 1
         Console.WriteLine("Household {0} has cell phone service: {1}",
                           ctr + 1,
                           If((households(ctr) And PhoneService.Cell) = PhoneService.Cell,
                              "Yes", "No"))
      Next
      Console.WriteLine()
      
      ' Which households have cell phones and land lines?
      Dim cellAndLand As PhoneService = PhoneService.Cell Or PhoneService.LandLine
      For ctr As Integer = 0 To households.Length - 1
         Console.WriteLine("Household {0} has cell and land line service: {1}",
                           ctr + 1,
                           If((households(ctr) And cellAndLand) = cellAndLand,
                              "Yes", "No"))
      Next
      Console.WriteLine()
      
      ' List all types of service of each household?'
      For ctr As Integer = 0 To households.Length - 1
         Console.WriteLine("Household {0} has: {1:G}",
                           ctr + 1, households(ctr))
      Next
      Console.WriteLine()
   End Sub
End Module
' The example displays the following output:
'    Household 1 has phone service: Yes
'    Household 2 has phone service: No
'    Household 3 has phone service: Yes
'
'    Household 1 has cell phone service: Yes
'    Household 2 has cell phone service: No
'    Household 3 has cell phone service: Yes
'
'    Household 1 has cell and land line service: Yes
'    Household 2 has cell and land line service: No
'    Household 3 has cell and land line service: No
'
'    Household 1 has: LandLine, Cell, Internet
'    Household 2 has: None
'    Household 3 has: Cell, Internet