Introduction
In our newest WCF project, we needed to pack some enum-values in a message header (BitFlags
). Also, I frequently had key-value pairs containing an enum-key with a value. Mostly, these were used for dynamic settings within the domain, but sometimes, they needed to be send to the server by the service (serializing). So I wanted to have constants, since attributes on enums did not satisfy me. In addition to this, the BitFlags
should be more scalable, at least 1024 or better more. The serialization should fit it the FastSerializer
I built according to the great articles here on CP.
Very important, if you do not need a key-value pair, or at least the BitFlags
up to 8192, this article is not intended for you. But maybe, you are the real crack that can help me with a better class design (see below). Cheers!
Background
I recommend reading these great articles which I profited a lot from and used parts in my sample code:
Using the code
The usage of the code is probably neater than the code itself. Because of the implicit operators, the initial Key-Value can be set anyhow, by using strings, constants, or decimal values.
this.myColor = 'BackGround';
this.myColor = '1';
this.myColor = 1;
this.myColor = EColor.eBackGround
this.myColor = EColor.eBackGround+EColor.eInnerBorder;
this.myColor += EColor.eForeGround;
this.myColor += 2;
Mainly for UI-Support, the flags can be set all or inverted, or we can get a list from the constants, keys, values, names (of key), or a description. The list contains either all or the used bits in the current instance.
this.myColor.Invert();
this.myColor.MarkAll();
EColor[] items = this.myColor.ListConst;
EColor[] items = this.myColor.ListFlagged;
Most useful is the serialization-capabilities as well as the provided values:
byte[] rawData = this.myColor;
this.otherColor = rawData;
bool[] bitFlags = this.myColor;
this.otherColor = bitFlags;
Int32 decimal = this.myColor.DecValue;
Color anytype = this.myColor.EnumValue;
string constName = this.myColor.ConstName;
Setup the class
First an EnumConstant
class needs to be marked with the EFlags
attribute that describes how many enum-keys the class contains (=BitFlags
). Internally, a bool[]
array holds the set bits. The class is derived from EnumBase<TC, TT>
, where TC
is the class deriving and TT
the type that the value is containing; e.g., in the sample, EColor
is defined to be enums of type Color
.
[Serializable]
[EFlags(EFlagSize.b32)]
public class EColor : EnumBase<EColor, Color>
Second, all constructors need to be declared, calling the base-constructors. The reason for this is the operators (next step). Most of the constructors need to be private
, since we have the BitFlags
managed by the constants. Therefore, the initialization of an instance is normally done by the assignment of a constant.
internal EColor() : base() { }
private EColor(E decValue, Color eValue)
: base((Int32)decValue, eValue, typeof(E), String.Empty) {}
public EColor(SerializationInfo info, StreamingContext context)
: base(info, context) {}
private EColor(Int32 decValue ) : base(decValue) {}
private EColor(Color eValue ) : base(eValue) {}
private EColor(string eName ) : base(eName) {}
private EColor(byte[] rawValue ) : base(rawValue) {}
private EColor(bool[] bitFlags ) : base(bitFlags) {}
private EColor(bool addFlags, object value1,
object value2) : base(addFlags, value1, value2) {}
Third, all operators need to be declared as well. It seems that operators from the base-class cannot be used in derived classes. Probably because of the constants that are used to describe the identity of an instance. I did not manage to have them in the base-class.
public static implicit operator EColor(Int32 operatorValue)
{
return new EColor(operatorValue);
}
public static EColor operator + (EColor value1, EColor value2)
{
return new EColor(true, value1.CmpValue, value2 .CmpValue);
}
public static EColor operator - (EColor value1, EColor value2)
{
return new EColor(false, value1.CmpValue, value2 .CmpValue);
}
Finally, all constants need to be declared, and in addition, we also have an enclosed enum inside that helps us to have this done with less text. Also, if the enum is public
, we can use the enclosed enum for attributes and switches. Personally, I declare the enum internal
.
public enum E
{
None = 0,
BackGround = 1,
ForeGround = 2,
InnerBorder = 3,
OuterBorder = 4,
TextNormal = 5,
TextHighlite = 6,
}
public static readonly EColor eNone = new EColor(E.None,
Color.Empty );
public static readonly EColor eBackGround = new EColor(E.BackGround,
Color.AliceBlue, 'use Background Color' );
public static readonly EColor eForeGround = new EColor(E.ForeGround,
Color.Bisque, 'use Foreground Color' );
public static readonly EColor eInnerBorder = new EColor(E.InnerBorder, Color.DodgerBlue );
public static readonly EColor eOuterBorder = new EColor(E.OuterBorder, Color.Tomato );
public static readonly EColor eTextNormal = new EColor(E.TextNormal, Color.Yellow );
public static readonly EColor eTextHighlite = new EColor(E.TextHighlite, Color.RosyBrown );
Not pure philosophy, but handy?
Surely, not everyone thinks that it is fun or useful to do such an amount of work to get an enum as a key-value constant. Classes like this are a full cache with some specific functionality, but easy to hook in an Business Object and serialized to be used elsewhere. The code surely becomes very readable:
if (this.myColor.Contains(EColor.eBackground))
{
control.Background = this.myColor.EnumValue;
}
If someone can tell me how to have a base class that does the same (operators/constructors) without having them declared in the derived class, I will be happy to hear. Thanks.