|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionEvery time you have a set of ordinal values that are related, it is a good practice to put them in an enumeration rather than leave them as constant values. Enumerations have been around at least since C++ and .NET continues to do so and has even added new features like flags attribute for the type or description for the elements. BackgroundLet's consider a simple C# enumeration: enum myenum : byte
{
a = 1,
b = 2,
c = 3
}
You can easily assign a value that is the type of the underlying type like: myenum eval = default(myenum);//that is actually 0
eval = (myenum)4;
The problem lies in the fact that [Flags]// Define an Enum with FlagsAttribute.
enum MultiHue : sbyte
{
[Description("no Color")]Black = 0,
[Description("pure red")]Red = 1,
[Description("pure green")]Green = 2,
//[Description("pure yellow")]Yellow = 3,
//Green | Red number 3 is deliberately omitted
[Description("pure blue")]Blue = 4,
[Description("composite green + blue")]Cyan = Green | Blue,//6
[Description("composite red + blue")]Magenta = Red | Blue,//5
[Description("composite red + blue + green")]White = Red | Blue | Green,//7
[Description("not a valid color")]BadColor = 127
};
If you run Validate the Value of the EnumerationStarting with C# 3.0, in its quest to improve usability, Microsoft introduced the extension methods that we can also use as an elegant way to validate the public static bool IsDefined(this System.Enum value)
{
return System.Enum.IsDefined(value.GetType(), value);
}
Using this extension method, validation becomes very elegant: MultiHue mh = MultiHue.Blue;
bool isdefined = mh.IsDefined();
Validating an public static bool IsValidEnumValue(this System.Enum value)
{
if (value.HasFlags())
return IsFlagsEnumDefined(value);
else
return value.IsDefined();
}
private static bool IsFlagsEnumDefined(System.Enum value)
{// modeled after Enum's InternalFlagsFormat
Type underlyingenumtype = Enum.GetUnderlyingType(value.GetType());
switch (Type.GetTypeCode(underlyingenumtype))
{
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.SByte:
case TypeCode.Single:
{
object obj = Activator.CreateInstance(underlyingenumtype);
long svalue = System.Convert.ToInt64(value);
if (svalue < 0)
throw new ArgumentException(
string.Format("Can't process negative {0} as {1}
enum with flags", svalue, value.GetType().Name));
}
break;
default:
break;
}
ulong flagsset = System.Convert.ToUInt64(value);
Array values = Enum.GetValues(value.GetType());//.Cast
The workhorse of the validation is the Converting a Variable to an EnumSo far, I've been dealing with checked
{
//MultiHue has the underlying type of sbyte: -127...127
MultiHue mh = (MultiHue)(object)128;//InvalidCastException
int i = 128;
mh = (MultiHue)i;//OverflowException
}
An If the block was unchecked, there will be no exception and the If you use the //MultiHue has the underlying type of sbyte: -127...127
MultiHue mh = (MultiHue)Enum.Parse(typeof(MultiHue),"128");//OverflowException
Hence I've created a method to deal with public static bool SafeConvertToEnum(object value, out EnumType retv)
{
Type enumType = typeof(EnumType);
if (!enumType.IsEnum)
throw new System.ArgumentException(string.Format("{0} is not an Enum.",
enumType.Name));
if (value == null)
{
retv = default(EnumType);
return false;
}
Type valType = value.GetType();
bool isString = valType == typeof(string);
bool isOrdinal = valType.IsPrimitive ||
typeof(decimal) == valType || valType.IsEnum;
if (!isOrdinal && !isString)
throw new System.ArgumentException
(string.Format("{0} can not be converted to an enum", valType.Name));
try
{
checked
{
if (valType == Enum.GetUnderlyingType(enumType))
retv = (EnumType)value;
else
{
if(isString)
retv = (EnumType) Enum.Parse(typeof(EnumType), value as string);
else
if (valType.IsEnum)
{
Enum en = (Enum)value;
object zero = Activator.CreateInstance(valType);
value = (en.CompareTo(zero) >= 0)?
Convert.ToUInt64(value):Convert.ToUInt64(value);
}
retv = (EnumType)Enum.Parse(typeof(EnumType), value.ToString());
}
}
if (!((System.Enum)(object)retv).IsValidEnumValue())
{
retv = default(EnumType);
return false;
}
}
catch(ArgumentException)
{
retv = default(EnumType);
return false;
}
catch (OverflowException)
{
retv = default(EnumType);
return false;
}
catch (InvalidCastException)
{
retv = default(EnumType);
return false;
}
catch (Exception ex)
{
throw new System.ArgumentException(string.Format
("Can't convert value {0}\nfrom the type of {1}
into the underlying enum type of {2}\nbecause {3}",
value, valType.Name, Enum.GetUnderlyingType(enumType).Name,
ex.Message), ex);
}
return true;
}
Since I've used reflection quite intensely to make this safe conversion, I think that performance was the reason why Microsoft did not make enumeration casting 'boiler plate'. The method usage should be straight forward: MultiHue mh;
//for strings
bool res = EnumHelper
public static class EnumHelper < EnumType >
where EnumType : struct, IComparable, IConvertible, IFormattable
{........
The other extension methods like Using the CodeIn order to use this code, you have to add the namespace to your code... using CTSExtenders;
... and add a reference to CTSExtenders.dll, or better yet to the project History
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||