Dot Net

Syrinx .NET Development Blog
Need help on your project? info@syrinx.com, or toll free (888) 579-7469, press 1

News



Need help with your .NET Development project?

Syrinx works with clients throughout New England to architect, design, develop, and deploy .NET Applications. Working on fully outsourced projects, as part of your team, helping to train your team, or rescuing projects in trouble, we are comfortable doing it all. Projects from a couple weeks to several months in duration, reference clients available. Contact us today - info@syrinx.com, or toll free (888) 579-7469 and press 1 to speak to someone now!

Enumerators Decorated with the Flag Attribute

 

An enumerator decorated with the [Flags] or [FlagAttribute] attribute (they are one in the same), is able to store a byte that represents the concatenation of all possible combinations of the enumeration.   Because of this concatenation it is required that the enumeration be numbered implementing a base 2 sequence (i.e. 0, 1, 2, 4).  As seen in the sample code this evaluates to the following base 2 numbers:

0 = 0000

1 = 0001

2 = 0010

4 = 0100

For the sake of simplicity we added another enum to represent when all of the flags have been selected All = 7

7 = 0111

The following code (a simple aspx page and its code-behind) will display a the uses of Flags as well as the differences from regular enums.  The code uses Bitwise OR to concatenate a flag (i.e. will toggle the bit on if either or both bits in the sequence are true (1)), as follows:

0010

0001

0011

The code also uses the the Bitwise Complement (~) operator as well as the Bitwise AND (&) operator to negate (remove) a specific flag (i.e. will toggle the bit off if both bits in the sequence are true (1)), as follows:

  0111

~0010

  0101

ASPX Code:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="UsingEnums.aspx.cs" Inherits="UsingEnums" %>

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head runat="server">

    <title>Using Enumerators Page</title>

</head>

<body>

    <form id="form1" runat="server">

    <div style="text-align: center; width: 604px;">

        <asp:Label ID="lblProjectRequirements" runat="server" Text="Project Requirements (Pick up to two)"></asp:Label>

        <asp:CheckBox ID="chkAutoCorrect" runat="server" Text="Auto-correct" />

        <asp:CheckBoxList ID="cblProjectPreference" runat="server">

        </asp:CheckBoxList>

        <asp:Button ID="btnSelectRequirements" runat="server" Text="Select Requirements"

            OnClick="btnSelectRequirements_Click" Style="height: 26px" /><br />

        <asp:Label ID="lblResults" runat="server" Height="91px" Width="552px"></asp:Label><br />

        <asp:Button ID="btnCompare" runat="server" Text="Comapre Flags to Enums" OnClick="btnCompare_Click"

            Style="height: 26px" /><br />

        <asp:Label ID="lblFlagsDisplay" runat="server" Height="40px" Width="300px"></asp:Label>

        <asp:Label ID="lblEnumDisplay" runat="server" Height="40px" Width="300px"></asp:Label>

    </div>

    </form>

</body>

</html>

Code-Behind:

using System;

using System.Web.UI;

using System.Web.UI.WebControls;

 

[Flags]

public enum ProjectPreference

{

    /// <summary>

    /// No project requirements are selected

    /// </summary>

    None = 0,                   // (0000)

    /// <summary>

    /// The project must be done as inexpensively as possible

    /// </summary>

    Cheap = 1,                  // (0001)

    /// <summary>

    /// The project must be done as quickly as possible

    /// </summary>

    Fast = 2,                   // (0010)

    /// <summary>

    /// The project must be of the highest quality

    /// </summary>

    Good = 4,                   // (0100)

    /// <summary>

    /// This is an illegal value, since only two can be slected at one time

    /// </summary>

    All = Cheap | Fast | Good   // (0111)

}

 

// Based on Microsoft http://msdn.microsoft.com/en-us/library/system.flagsattribute.aspx

// Define an Enum without FlagsAttribute.

public enum SingleHue : short

{

    Black = 0,

    Red = 1,

    Yellow = 2,

    Blue = 4

}

 

// Define an Enum with FlagsAttribute.

[FlagsAttribute]

public enum MultiHue : short

{

    Black = 0,

    Red = 1,

    Yellow = 2,

    Blue = 4

}

 

public partial class UsingEnums : Page

{

    private const int SKIP_FOR_NONE_AND_ALL = 2;

    protected void Page_Load(object sender, EventArgs e)

    {

        if (!IsPostBack)

        {

            // Get a string array of all the enum names

            string[] projectTypes = Enum.GetNames(typeof(ProjectPreference));

            // Don't display None or All enum values

            string[] displayProjectPreferences = new string[projectTypes.Length - SKIP_FOR_NONE_AND_ALL];

            int i = 0;

            foreach (string projectType in projectTypes)

            {

                if (

                        !projectType.Equals(ProjectPreference.None.ToString())

                    &&

                        !projectType.Equals(ProjectPreference.All.ToString())

                    )

                {

                    displayProjectPreferences[i++] = projectType;

                }

            }

            cblProjectPreference.DataSource = displayProjectPreferences;

            cblProjectPreference.DataBind();

        }

    }

    protected void btnSelectRequirements_Click(object sender, EventArgs e)

    {

        ProjectPreference selectedProjectPreferences = new ProjectPreference();

        try

        {

            // Gather all of the selected flags

            foreach (ListItem liProjectPreference in cblProjectPreference.Items)

            {

                if (liProjectPreference.Selected)

                {

                    // Concatenate the flag using the Enum.Parse method and the Bitwise OR (|) operator

                    selectedProjectPreferences |=

                        (ProjectPreference) Enum.Parse(typeof (ProjectPreference), liProjectPreference.Value, true);

                }

            }

            // if all have been selected then ...

            if (selectedProjectPreferences == ProjectPreference.All)

            {

                // if auto-correct is on remove the 'Cheap' flag

                if (chkAutoCorrect.Checked)

                {

                    // Use the Bitwise Complement (~) operator as well as the Bitwise AND (&)

                    // operator to negate (remove) a specific flag

                    selectedProjectPreferences &= ~ProjectPreference.Cheap;

                    foreach (ListItem liProjectPreference in cblProjectPreference.Items)

                    {

                        if (liProjectPreference.Value == ProjectPreference.Cheap.ToString())

                        {

                            liProjectPreference.Selected = false;

                        }

                    }

                }

                // otherwise throw an error

                else

                {

                    cblProjectPreference.SelectedIndex = -1;

                    throw new Exception("You can select no more than two project preferences.");

                }

            }

            lblResults.Text = selectedProjectPreferences.ToString();

        }

        catch (Exception ex)

        {

            // The flag enumeration evaluates to 'All' - no coding required

            string errMessage = string.Format("The error value of the flag enumeration is: {0}<br />",

                                              selectedProjectPreferences);

                    // Reset to 'None'

                    selectedProjectPreferences = ProjectPreference.None;

            errMessage += string.Format("{0}<br />The current value of the flag enumeration has been reset to: {1}",

                                            ex.Message, selectedProjectPreferences);

            lblResults.Text = errMessage;

        }

    }

 

    protected void btnCompare_Click(object sender, EventArgs e)

    {

        // Display all possible combinations of values.

        // Also display an invalid value.

        string flagDisplay = "<b>With</b> FlagsAttribute:<br />";

 

        for (int val = 0; val <= 8; val++)

        {

            flagDisplay += string.Format("{0,3} - {1}<br />", val, ((MultiHue)val));

        }

        lblFlagsDisplay.Text = flagDisplay;

       

        string enumDisplay = "<b>Without</b> FlagsAttribute:<br />";

        for (int val = 0; val <= 8; val++)

        {

            enumDisplay += string.Format("{0,3} - {1}<br />", val, ((SingleHue)val));

        }

        lblEnumDisplay.Text = enumDisplay;

    }

}

Using the [Flags] attribute allows you to let the code handle some of the display of the flags, as the ToString() method will return a comma seperated list of enums (e.g. "Cheap, Fast"),  it will also resolve the ToString() to an aggregated value (in this case to 'All' if all of the flags are selected) and finally in walking through code you will be able to hover and see the concatenated list of flags instead of a somewhat hard-to-decipher number.  That is, if you are walking through code after a breakpoint you can hover over a [Flags] enum and see "Cheap | Good" instead of "5", making debugging far easier.

That wraps it up for this time.  Hope it helps.

Posted: Dec 15 2008, 02:33 AM by JohnF | with no comments
Filed under: , , ,

Comments

No Comments