November 25, 2007

Flags Based Enumerated Types

An extension class that extends a flag based enumerated type with set/group theory type operations. See FileAttributesExtender for a usage of this class.
public partial class EnumFlagsForm : Form
{
    [Flags]
    public enum EnumFlags
    {
        None = 0,
        Emergency_Stop = 0x1,
        Greasey = 0x2,
        Spot_Spot = 0x4,
        Doughnuts = 0x8,
        Lift_Moving = 0x10,
        Doors_Open = 0x20,
        Forbidden = 0x40
    }

//SourceTypes sourceTypes;
public static class EnumFlagsExtender
{
 // Return lhs flags plus rhs flags
 public static EnumFlags Union(this EnumFlags lhs, EnumFlags rhs)
 {
  return lhs | rhs;
 }

 // Return flags common to lhs and rhs
 public static EnumFlags Intersection(this EnumFlags lhs, EnumFlags rhs)
 {
  return lhs & rhs;
 }

 // Return lhs flags minus rhs flags
 public static EnumFlags Subtract(this EnumFlags lhs, EnumFlags rhs)
 {
  EnumFlags common = lhs & rhs;
  int res = (int)lhs - (int)common;
  return (EnumFlags)(res);
 }

 // Return true if lhs contains all the flags within rhs
 public static bool Contains(this EnumFlags lhs, EnumFlags rhs)
 {
  EnumFlags common = lhs & rhs;
  return (common == rhs);
 }

 // Return true if lhs contains one of the flags within rhs
 public static bool ContainsOneOf(this EnumFlags lhs, EnumFlags rhs)
 {
  EnumFlags common = lhs & rhs;
  return ((int)common > 0);
 }

 
 // NON-extension methods here

 public static EnumFlags FromString(string source)
 {
  EnumFlags res = (EnumFlags)Enum.Parse(typeof(EnumFlags), source, true);
  return res;
 }

    }

    public void Test1()
    {
      EnumFlags test = EnumFlags.Lift_Moving |
          EnumFlags.Doors_Open | EnumFlags.Doughnuts;
      string str = test.ToString();
      //str = str.Replace('_', ' ');
      Debug.WriteLine(str);
      EnumFlags newtest = EnumFlagsExtender.FromString(str);
      Debug.Assert(newtest == test);      
    }

public void EnumFlagsSubtractTest()
{
    EnumFlags test1 = EnumFlags.Lift_Moving | EnumFlags.Doors_Open |
        EnumFlags.Doughnuts | EnumFlags.Emergency_Stop;
    EnumFlags test2 = EnumFlags.Lift_Moving | EnumFlags.Forbidden |
        EnumFlags.Emergency_Stop;
    EnumFlags expected = EnumFlags.Doors_Open | EnumFlags.Doughnuts;
    EnumFlags outcome = test1.Subtract(test2);
    Debug.Assert(outcome == expected);

    expected = EnumFlags.Doors_Open | EnumFlags.Doughnuts | EnumFlags.Greasey;
    outcome = outcome.Union(EnumFlags.Greasey);
    Debug.Assert(outcome == expected);

    EnumFlags test3 = EnumFlags.Doughnuts | EnumFlags.Greasey;
    bool res = outcome.Contains(test3);
    Debug.Assert(res);
    
    res = outcome.Contains(EnumFlags.Lift_Moving | EnumFlags.Forbidden);
    Debug.Assert(!res);
}


    public EnumFlagsForm()
    {
        InitializeComponent();
        lbSourceTypes.Items.AddRange(
UnderscoresToSpaces(
Enum.GetNames(typeof(EnumFlags))));
        OnSelectedValueChanged(this, null);
    }

    string[] UnderscoresToSpaces(string[] src)
    {
        return ReplaceInStrings('_', ' ', src);
    }

    string[] SpacesToUnderscores(string[] src)
    {
        return ReplaceInStrings('_', ' ', src);
    }

    string SpacesToUnderscores(string src)
    {
        return src.Replace(' ', '_');
    }

    string[] ReplaceInStrings(char oldchar, char newchar, params string[] src)
    {
        string[] res = new string[src.Length];
        int ix = 0;
        foreach (string targ in src)
        {
            res[ix++] = targ.Replace(oldchar, newchar);
        }
        return res;
    }


    private void OnSelectedValueChanged(object sender, EventArgs e)
    {
        EnumFlags test = EnumFlags.None;
        foreach (string type in lbSourceTypes.SelectedItems)
        {
            test = EnumFlagsHelper.Add(test, 
EnumFlagsHelper.FromString(SpacesToUnderscores(type)));
        }
        tbSelected.Text = test.ToString();
        lblIsAudioAndVideo.Text = "WoopsTest() == " + 
(EnumFlagsHelper.WoopsTest(test)).ToString();
    }

    private void butTest_Click(object sender, EventArgs e)
    {
        Test1();
 
        EnumFlagsSubtractTest();
    }

}
Here is a visual studio code snippet for the enum extender
<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
     <CodeSnippet Format="1.0.0">
          <Header><Title>Flags Enum Extender pattern</Title>
               <Description>Implement an extender for a 'Flags' type Enum. The extender adds set type methods to the enumertaion type</Description>
               <Keywords>
                   <Keyword>Enum</Keyword>
              </Keywords>
              <Author>Roger Bovill</Author>
         </Header>
         <Snippet>
         <Declarations> 
              <Literal Editable="true">
          <ID>EnumName</ID>
          <ToolTip>Name of the '[Flags]' based enumerated type</ToolTip>
    <Default>MyEnumType</Default>  
                <Function></Function>
             </Literal>
        </Declarations>
        <Code Language="CSharp">
            <![CDATA[

#region $EnumName$Extender

    public static class $EnumName$Extender
    {
        // Return lhs flags plus rhs flags
        public static $EnumName$ Union(this $EnumName$ lhs, $EnumName$ rhs)
        {
            return lhs | rhs;
        }

        // Return flags common to lhs and rhs
        public static $EnumName$ Intersection(this $EnumName$ lhs, $EnumName$ rhs)
        {
            return lhs & rhs;
        }

        // Return lhs flags minus rhs flags
        public static $EnumName$ Subtract(this $EnumName$ lhs, $EnumName$ rhs)
        {
            $EnumName$ common = lhs & rhs;
            int res = (int)lhs - (int)common;
            return ($EnumName$)(res);
        }

        // Return true if lhs contains all the flags within rhs
        public static bool Contains(this $EnumName$ lhs, $EnumName$ rhs)
        {
            $EnumName$ common = lhs & rhs;
            return (common == rhs);
        }

        // Return true if lhs contains one of the flags within rhs
        public static bool ContainsAnyOf(this $EnumName$ lhs, $EnumName$ rhs)
        {
            $EnumName$ common = lhs & rhs;
            return ((int)common > 0);
        }

        // NON-extension methods here

        public static $EnumName$ FromString(string source)
        {
            $EnumName$ res = ($EnumName$)Enum.Parse(typeof($EnumName$), source, true);
            return res;
        }

    }

#endregion $EnumName$Extender

            ]]>
        </Code>
       </Snippet>
    </CodeSnippet>
</CodeSnippets>

No comments: