Windows 2008Windows Vista.NET 1.0Windows 2003.NET 1.1.NET 3.0Windows 2000高级Windows XP.NET 2.0.NET 3.5C# 2.0初学者C# 3.0中级开发Windows.NETC#
强类型和高效的 .NET 枚举
高效且强类型的 .NET 枚举类的替代方案

引言
在文章 使用 StrongEnum 进行更简洁的枚举编程 中,DevCubed 很好地展示了一个强类型的 Enum
类。这个类使使用 Enum
功能的代码更简单,但实现仍然基于内置的 Enum
类。Enum
类的一个缺点是它的性能,它基于反射查找。一些读者,包括我,都在寻找更高效的解决方案。
所需:通用、强类型、高效的枚举
我在这里想提出的解决方案是基于在初始化时(静态构造函数)调用内置的 Enum
来存储 enum
信息,然后保持它并在泛型字典(.NET 哈希表)中使用这些信息。
这里的性能提升在于初始化时间,以及存储字典的一些内存。这是这个类
public class StrongQuickEnum<T>
{
static StrongQuickEnum()
{
_t = typeof(T);
string[] names = Enum.GetNames(_t);
_strToEnum = new Dictionary<string, T>(names.Length);
_strToEnumIgnoreCase = new Dictionary<string, T>(names.Length);
_intToEnum = new Dictionary<int, T>(names.Length);
foreach (string name in names)
{
T enumObject = (T)Enum.Parse(_t, name);
_strToEnum.Add(name, enumObject);
_strToEnumIgnoreCase.Add(name.ToLower(), enumObject);
int enumInt = Convert.ToInt32(enumObject);
_intToEnum.Add(enumInt, enumObject);
}
}
/// <summary>
/// Will serve us in the Parse method
/// </summary>
private static Dictionary<string, T> _strToEnum;
/// <summary>
/// Will serve us in the Parse method, with ignoreCase == true.
/// It is possible not to hold this member and to define a Comparer class -
/// But my main goal here is the performance at runtime.
/// </summary>
private static Dictionary<string, T> _strToEnumIgnoreCase;
/// <summary>
/// Will serve us in the ToObject method
/// </summary>
private static Dictionary<int, T> _intToEnum;
private static Type _t;
public static T Parse(string value)
{
return _strToEnum[value]; // Exception will be thrown if the value is not found
}
public static T Parse(string value, bool ignoreCase)
{
if (ignoreCase)
{
string valLower = value.ToLower();
return _strToEnumIgnoreCase[valLower];
}
else
return Parse(value);
}
public static T ToObject(object value)
{
try
{
int intval = (int)value;
return ToObject(intval);
}
catch (InvalidCastException)
{
throw new ArgumentException("Cannot convert " + value + " to int");
}
//If an exception is coming from ToObject(intval), do not catch it here.
}
public static T ToObject(int value)
{
return _intToEnum[value];
}
public static string GetName(object value)
{
// We can hold an additional dictionary to map T -> string.
// In my specific usage, this usages is rare,
// so I selected the lower performance option
try
{
T valueT = (T)value;
foreach (KeyValuePair<string, T> pair in _strToEnum)
{
int x = Convert.ToInt32(pair.Value);
if (pair.Value.Equals(valueT))
return pair.Key;
}
}
catch
{
throw new ArgumentException("Cannot convert " + value +
" to " + _t.ToString());
}
return null; // should never happen
}
public static string[] GetNames()
{
// .NET 3.5:
// use the magic _strToEnum.Keys.ToArray() and that's it!
string[] res = new string[_strToEnum.Count];
int i = 0;
foreach (string str in _strToEnum.Keys)
res[i++] = str;
return res;
}
public static Array GetValues()
{
Array res = Array.CreateInstance(_t, _strToEnum.Count);
int i = 0;
foreach (T enumObject in _strToEnum.Values)
res.SetValue(enumObject, i++);
return res;
}
public static bool IsDefined(object value)
{
try
{
int intval = (int)value;
return _intToEnum.ContainsKey(intval);
}
catch
{
return false;
}
}
public static Type GetUnderlyingType()
{
// Seems like this is good enough.
return Enum.GetUnderlyingType(_t);
}
}
使用代码
附带的 C# 项目演示了 StrongQuickEnum
类的用法,并显示了使用 StrongQuickEnum
和内置 Enum
之间的计时差异
class Program
{
enum Color
{
White,
Black,
Red,
Yellow,
Blue,
Green,
Cyan,
Magenta,
Pink,
Purple,
Orange,
Brown
}
static string[] _enumStrings = new string[]
{
"White",
"Black",
"Red",
"Yellow",
"Blue",
"Green",
"Cyan",
"Magenta",
"Pink",
"Purple",
"Orange",
"Brown"
};
const int _iterations = 100000;
static void Main(string[] args)
{
Console.WriteLine("Number of iterations: " + _iterations);
Random randomNumber = new Random();
using(new PerformanceMonitor("{Built-in Enum class}"))
{
for(int i = 0; i<_iterations; i++)
{
int index = randomNumber.Next(0, 11);
Color c1 = (Color)Enum.ToObject(typeof(Color), index);
Color c2 = (Color)Enum.Parse(typeof(Color), _enumStrings[index]);
}
}
// Verify initialization of the data out of the comparative measurement.
// As you can see, this initialization is the gain for the later efficiency.
Color init = StrongQuickEnum<Color>.ToObject(2);
using (new PerformanceMonitor("{StrongQuickEnum<Color> class}"))
{
for(int i = 0; i<_iterations; i++)
{
int index = randomNumber.Next(0, 11);
Color c1 = StrongQuickEnum<Color>.ToObject(index);
Color c2 = StrongQuickEnum<Color>.Parse(_enumStrings[index]);
}
}
Console.ReadLine();
}
}
class PerformanceMonitor : IDisposable
{
long _timestarted;
string _name;
internal PerformanceMonitor(string name)
{
_name = name;
_timestarted = DateTime.Now.Ticks;
}
public void Dispose()
{
Console.WriteLine("Operation " + _name + ":\t\t" +
(DateTime.Now.Ticks - _timestarted).ToString());
}
}
关注点
这个实现展示了一个想法,而不是一个完整的实现。例如,没有实现 Enum.Format
的替代方案。如果您实现任何扩展,请在评论中与我们分享!
历史
- 2009年5月3日:初始发布