Click here to Skip to main content
15,881,852 members
Articles / Programming Languages / C#

Key-Value Pairs as Enum-Constants

Rate me:
Please Sign up or sign in to vote.
4.33/5 (7 votes)
7 Sep 2008CPOL3 min read 61.8K   213   19  
An enum-like class that supports flags (up to 8192), has additional value-type data, description, and FastSerializer support.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.Diagnostics;

namespace Dialogik.Utils.Defines
{
    public interface IEnumBase
    {
        bool    InitValue   (Int32 decValue);
    }

    public class EnumBase<TC, TT> : ISerializable, IEnumBase
    {
        internal enum EnumOperator
        {
            DecValue    = 1,
            EnumValue   = 2,
            EnumName    = 3,
            Bytes       = 4,
            BitFlags    = 5,
            BitFlagsAdd = 6,
            BitFlagsSub = 7,
        }

        private static int              s_flagEnumSize   = 0;
        private static int              s_SegmentCount   = 0;
        private static List<object>     s_constantEnums  = new List<object>();
        private static int[]            s_constantArray  = null;
        private static Type             s_classType      = typeof(TC);
        private static Type             s_enumType       = typeof(TT);
        private static Type             s_decType        = null;

        #region Members

        private EnumContext<TT> _innerValue = null;

        internal EnumContext<TT> Value 
        {
            get
            {
                return this._innerValue;
            }
            set
            {
                this._innerValue = value;
            }
        }


        public List<TC> ListConst
        {
            get
            {
                return (GetInnerList(false));
            }
        }

        public List<TC> ListFlagged
        {
            get
            {
                return (GetInnerList(true));
            }            
        }

        private List<TC> GetInnerList(bool flaggedOnly)
        {
            Instancer   _instancer = Factory.GetInstancer(s_classType);
            List<TC>    _innerList = new List<TC>();

            if (s_flagEnumSize > 0)
            {
                bool[] bitFlags = this.Value.BitFlags;
                int count = bitFlags.Length;
                for (int i = 0; i < count; i++)
                {
                    if (!flaggedOnly || bitFlags[i])
                    {
                        TC          _innerItem = (TC)_instancer.CreateNew();
                        IEnumBase   _innerBase = _innerItem as IEnumBase;

                        if (_innerBase.InitValue(i + 1))
                        {
                            _innerList.Add(_innerItem);
                        }
                    }
                }
            }
            return _innerList;
        }


        public List<Int32> ListConstDecValues
        {
            get
            {
                List<Int32>             _result = new List<Int32>();
                List<EnumContext<TT>>   _inner  = GetContextValues(false);
                foreach(EnumContext<TT> _item in _inner)
                {
                    _result.Add(_item.DecValue);
                }

                return (_result);
            }
        }

        public List<Int32> ListFlaggedDecValues
        {
            get
            {
                List<Int32>             _result = new List<Int32>();
                List<EnumContext<TT>>   _inner  = GetContextValues(true);
                foreach(EnumContext<TT> _item in _inner)
                {
                    _result.Add(_item.DecValue);
                }

                return (_result);
            }
        }

        public List<TT> ListConstEnumValues
        {
            get
            {
                List<TT>                _result = new List<TT>();
                List<EnumContext<TT>>   _inner  = GetContextValues(false);
                foreach(EnumContext<TT> _item in _inner)
                {
                    _result.Add(_item.EnumValue);
                }

                return (_result);
            }
        }

        public List<TT> ListFlaggedEnumValues
        {
            get
            {
                List<TT>                _result = new List<TT>();
                List<EnumContext<TT>>   _inner  = GetContextValues(true);
                foreach(EnumContext<TT> _item in _inner)
                {
                    _result.Add(_item.EnumValue);
                }

                return (_result);
            }
        }

        private List<EnumContext<TT>> GetContextValues(bool flaggedOnly)
        {
            List<EnumContext<TT>> _innerList = new List<EnumContext<TT>>();

            if (s_flagEnumSize > 0)
            {
                bool[] bitFlags = this.Value.BitFlags;
                int count = bitFlags.Length;
                for (int i = 0; i < count; i++)
                {
                    if (!flaggedOnly || bitFlags[i])
                    {
                        EnumContext<TT> itemValue = null;

                        itemValue = InitEnumType(EnumOperator.DecValue, i + 1);
                        if (itemValue.DecValue > 0)
                        {
                            _innerList.Add(itemValue);
                        }
                    }
                }
            }
            return _innerList;
        }




        #endregion


        #region Contructors

        internal EnumBase()
        {
            InitFlags();
            this.Value = new EnumContext<TT>(s_flagEnumSize, 0, null);
        }

        //  for constants
        internal EnumBase(Int32 decValue, TT enumValue, Type enumType, string description)
        {
            s_decType = enumType;

            string _enumName = Enum.GetName(enumType, decValue);

            InitFlags();

            this.Value = new EnumContext<TT>(s_flagEnumSize, decValue, null) { EnumValue = enumValue, EnumName = _enumName, Description = description };
            s_constantEnums.Add((object)this);
            if (decValue > 0)
            {
                decValue--;
                s_constantArray[decValue] = s_constantEnums.Count - 1;
            }
        }

        //  because constants are required by design and static, it is sufficient to call InitFlags
        //  in above constructors only
        private void InitFlags()
        {
            if (s_constantEnums.Count == 0)
            {
                //  Reflection is done only once per class
                EFlagsAttribute[] flagsAttributes = (EFlagsAttribute[])this.GetType().GetCustomAttributes(typeof(EFlagsAttribute), true);
                if (flagsAttributes.Length > 0)
                {
                    s_flagEnumSize = (int)flagsAttributes[0].FlagSize;
                    s_SegmentCount = s_flagEnumSize / 8 /*Byte*/;
                }
                if (s_flagEnumSize > 0)
                {
                    s_constantArray = new int[s_flagEnumSize];
                }
            }
        }


        internal EnumBase(TT enumValue)             {   this.Value   = InitEnumType(EnumOperator.EnumValue,    enumValue); }
        internal EnumBase(Int32 decValue)           {   this.Value   = InitEnumType(EnumOperator.DecValue,     decValue);  }
        internal EnumBase(string enumName)          {   this.Value   = InitEnumType(EnumOperator.EnumName,     enumName);  }
        internal EnumBase(byte[] serBytes)          {   this.RawData = serBytes;                                           }
        internal EnumBase(bool[] bitFlags)          {   this.Value   = InitEnumType(EnumOperator.BitFlags,     bitFlags);  }

        internal EnumBase(bool addFlags, object value1, object value2)
        {
            if (s_flagEnumSize == 0)
            {
                Debug.Assert(false);
            }
            else
            {
                this.Value = InitEnumType(addFlags ? EnumOperator.BitFlagsAdd : EnumOperator.BitFlagsSub, (bool[])value1, (bool[])value2);
            }
        }

        #endregion

        #region ISerializable

        public EnumBase(SerializationInfo info, StreamingContext context)
        {
            this.RawData = (byte[])info.GetValue("b", typeof(byte[]));
        }

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("b", this.RawData);
        }
        #endregion

        #region Property - RawData

        public byte[] RawData
        {
            #region Getter

            get
            {
                List<byte> resultBytes = new List<byte>();

                if (s_flagEnumSize == 0)
                {
                    //  first byte is 1 = decimal
                    resultBytes.Add((byte)0x01);
                    Int32 decValue = Value.DecValue;
                    resultBytes.Add((byte)decValue);
                    decValue = decValue >> 8;
                    resultBytes.Add((byte)decValue);
                    decValue = decValue >> 8;
                    resultBytes.Add((byte)decValue);
                    decValue = decValue >> 8;
                    resultBytes.Add((byte)decValue);
                }
                else
                {
                    //  first byte is 2 = bitflags
                    resultBytes.Add((byte)0x02);

                    int i;
                    byte[]       serBytes     = this.Value.ByteValue;
                    List<byte>   usedBytes    = new List<byte>();

                    //  first we name the used Segments (0 based)
                    for (i = 0; i < serBytes.Length; i++)
                    {
                        if (serBytes[i] != 0x00)
                        {
                            usedBytes.Add((byte)i);
                        }
                    }
                    //  number of used Segments
                    resultBytes.Add((byte)usedBytes.Count);
                    
                    //  used segments (the number only)
                    for (i = 0; i < usedBytes.Count; i++)
                    {
                        resultBytes.Add(usedBytes[i]);
                    }

                    //  now the segments themselves
                    for (i = 0; i < serBytes.Length; i++)
                    {
                        if (serBytes[i] != 0x00)
                        {
                            resultBytes.Add(serBytes[i]);
                        }
                    }
                }
                return resultBytes.ToArray();
            }
            #endregion

            #region Setter

            set
            {
                byte[] serBytes = value;

                if (serBytes[0] == 0x01)
                {
                    Int32 decValue = 0x00;
                    decValue += serBytes[4];
                    decValue =  decValue << 8;
                    decValue += serBytes[3];
                    decValue =  decValue << 8;
                    decValue += serBytes[2];
                    decValue =  decValue << 8;
                    decValue += serBytes[1];

                    this.Value = InitEnumType(EnumOperator.DecValue, decValue);
                }
                else
                {
                    int i;
                    List<bool> usedBytes   = new List<bool>(new bool[s_SegmentCount]);
                    List<byte> resultBytes = new List<byte>(new byte[s_SegmentCount]);

                    //  number of used Segments
                    int  byteCount = serBytes[1];
                    byte segment   = 0;

                    //  used segments (the number only)
                    for (i = 0; i < byteCount; i++)
                    {
                        //  read with offset 2 since first is identifier + count
                        segment = serBytes[(i+2)];
                        usedBytes[segment] = true;
                    }

                    //  now the data
                    for (i = 0; i < s_SegmentCount; i++)
                    {
                        resultBytes[i] = 0x00;
                        if (usedBytes[i])
                        {
                            // read with offset
                            resultBytes[i] = serBytes[(i+2+byteCount)];
                        }
                    }
                    this.Value = InitEnumType(EnumOperator.Bytes, resultBytes.ToArray());
                 }
            }
            #endregion
        }

        #endregion


        #region Methods

        internal static EnumContext<TT> InitEnumType(EnumOperator searchMode, object searchValue)
        {
            return InitEnumType(searchMode, searchValue, null);
        }

        internal static EnumContext<TT> InitEnumType(EnumOperator searchMode, object searchValue1, object searchValue2)
        {
            bool itemfound = false;

            if (searchValue1 != null)
            {
                EnumContext<TT> itemValue    = null;

                switch (searchMode)
                {
                    case EnumOperator.DecValue:

                            int dec = (int)searchValue1;
                            if (dec>0)
                            {
                                dec--;
                                int pos = s_constantArray[dec];
                                itemValue = ((EnumBase<TC, TT>)s_constantEnums[pos]).Value;
                            }
                            else
                            {
                                itemValue = new EnumContext<TT>(s_flagEnumSize, 0, null);
                            }
                            break;

                    case EnumOperator.BitFlags:

                            itemValue = new EnumContext<TT>(s_flagEnumSize, 0, (bool[])searchValue1);
                            break;

                    case EnumOperator.BitFlagsAdd:

                            itemValue = new EnumContext<TT>(s_flagEnumSize, 0, (bool[])searchValue1);
                            itemValue.AddBitFlag((bool[])searchValue2);
                            itemValue.CalcDecValue();
                            break;

                    case EnumOperator.BitFlagsSub:

                            itemValue = new EnumContext<TT>(s_flagEnumSize, 0, (bool[])searchValue1);
                            itemValue.SubBitFlag((bool[])searchValue2);
                            itemValue.CalcDecValue();
                            break;


                    case EnumOperator.Bytes:

                            itemValue = new EnumContext<TT>(s_flagEnumSize, 0, null);
                            if (typeof(byte[]).IsAssignableFrom(searchValue1.GetType()))
                            {
                                itemValue.ByteValue = (byte[])searchValue1;
                            }
                            itemValue.CalcDecValue();
                            break;

                    case EnumOperator.EnumValue:
                    case EnumOperator.EnumName:

                            //  check if we have a corresponding item
                            foreach (EnumBase<TC, TT> constantItem in s_constantEnums)
                            {
                                itemValue = ((EnumBase<TC, TT>)constantItem).Value;
                                
                                if (searchMode == EnumOperator.EnumValue)     itemfound = object.Equals(itemValue.EnumValue,(TT)    searchValue1);
                                if (searchMode == EnumOperator.EnumName )     itemfound = object.Equals(itemValue.EnumName, (string)searchValue1);

                                if (!itemfound)
                                {
                                    itemValue = null;
                                }
                                else
                                {
                                    break;
                                }
                            }
                            break;
                }
                return itemValue;
            }
            return null;
        }

        #endregion



        #region IEnumBase Members

        bool IEnumBase.InitValue(int decValue)
        {
            this.Value = InitEnumType(EnumOperator.DecValue, decValue);
            return this.Value.DecValue > 0;
        }

        #endregion


        internal object CmpValue
        {
            get { return this.Value.CmpValue; }
        }

        public Int32 DecValue
        {
            get { return this.Value.DecValue; }
        }


        public TT EnumValue
        {
            get { return this.Value.EnumValue; }
        }

        public string EnumName
        {
            get
            {
                return this.Value.EnumName;
            }
        }

        public string Description
        {
            get { return this.Value.Description; }
        }

        #region Operators

        public static implicit operator TT      (EnumBase<TC, TT> operatorValue)    { return  operatorValue.Value.EnumValue;    }
        public static implicit operator Int32   (EnumBase<TC, TT> operatorValue)    { return  operatorValue.Value.DecValue;     }
        public static implicit operator string  (EnumBase<TC, TT> operatorValue)    { return  operatorValue.Value.EnumName;     }
        public static implicit operator byte[]  (EnumBase<TC, TT> operatorValue)    { return  operatorValue.RawData;            }
        public static implicit operator bool[]  (EnumBase<TC, TT> operatorValue)    { return  operatorValue.Value.BitFlags;     }

        public static implicit operator EnumBase<TC, TT>    (TT operatorValue)      { return new EnumBase<TC, TT>(operatorValue); }
        public static implicit operator EnumBase<TC, TT>    (Int32 operatorValue)   { return new EnumBase<TC, TT>(operatorValue); }
        public static implicit operator EnumBase<TC, TT>    (string operatorValue)  { return new EnumBase<TC, TT>(operatorValue); }
        public static implicit operator EnumBase<TC, TT>    (byte[] operatorValue)  { return new EnumBase<TC, TT>(operatorValue); }
        public static implicit operator EnumBase<TC, TT>    (bool[] operatorValue)  { return new EnumBase<TC, TT>(operatorValue); }

        public static EnumBase<TC, TT> operator + (EnumBase<TC, TT> value1, EnumBase<TC, TT> value2) { return new EnumBase<TC, TT>(true,  value1.CmpValue, value2.CmpValue); }
        public static EnumBase<TC, TT> operator + (EnumBase<TC, TT> value1, TT               value2) { return new EnumBase<TC, TT>(true,  value1.CmpValue, new EnumBase<TC, TT>(value2).CmpValue); }
        public static EnumBase<TC, TT> operator + (EnumBase<TC, TT> value1, Int32            value2) { return new EnumBase<TC, TT>(true,  value1.CmpValue, new EnumBase<TC, TT>(value2).CmpValue); }
        public static EnumBase<TC, TT> operator + (EnumBase<TC, TT> value1, string           value2) { return new EnumBase<TC, TT>(true,  value1.CmpValue, new EnumBase<TC, TT>(value2).CmpValue); }

        public static EnumBase<TC, TT> operator - (EnumBase<TC, TT> value1, EnumBase<TC, TT> value2) { return new EnumBase<TC, TT>(false, value1.CmpValue, value2.CmpValue); }
        public static EnumBase<TC, TT> operator - (EnumBase<TC, TT> value1, TT               value2) { return new EnumBase<TC, TT>(false, value1.CmpValue, new EnumBase<TC, TT>(value2).CmpValue); }
        public static EnumBase<TC, TT> operator - (EnumBase<TC, TT> value1, Int32            value2) { return new EnumBase<TC, TT>(false, value1.CmpValue, new EnumBase<TC, TT>(value2).CmpValue); }
        public static EnumBase<TC, TT> operator - (EnumBase<TC, TT> value1, string           value2) { return new EnumBase<TC, TT>(false, value1.CmpValue, new EnumBase<TC, TT>(value2).CmpValue); }

        public static bool operator ==(EnumBase<TC, TT> a, EnumBase<TC, TT> b)
        {
            //  both of them are null, so equal
            if (object.Equals(a, null) && object.Equals(b, null))
            {
                return true;
            }
            //  one of them is null, so not equal
            if (object.Equals(a, null) || object.Equals(b, null))
            {
                return false;
            }
            return (object.Equals(a.CmpValue.ToString(), b.CmpValue.ToString()));
        }


        public static bool operator !=(EnumBase<TC, TT> a, EnumBase<TC, TT> b)
        {
            //  both of them is null, so equal
            if (object.Equals(a, null) && object.Equals(b, null))
            {
                return false;
            }
            //  one of them is null, so not equal
            if (object.Equals(a, null) || object.Equals(b, null))
            {
                return true;
            }
            return (!object.Equals(a.CmpValue.ToString(), b.CmpValue.ToString()));
        }

        #endregion


        #region Overrides

        public override int GetHashCode()
        {
            return this.Value.CmpValue.GetHashCode();
        }

        public override string ToString()
        {
            return this.EnumName.ToString();
        }

        public override bool Equals(object obj)
        {
            if (obj is EnumBase<TC, TT>)
            {
                return (object.Equals(this.Value.CmpValue, ((EnumBase<TC, TT>)obj).CmpValue));
            }
            return false;
        }

        #endregion

        public bool Contains(EnumBase<TC, TT> enumConst)
        {
            return this.Value.IsFlagSet(enumConst.DecValue);
        }

        public void MarkAll()
        {
            this.Value = InitEnumType(EnumOperator.DecValue, 0);
            this.Value.InvertFlags();
            this.Value.CalcDecValue();
        }

        public void Invert()
        {
            this.Value.InvertFlags();
            this.Value.CalcDecValue();
        }

    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Switzerland Switzerland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions