C# 枚举ToString与性能
我分析了一些C#代码,最后研究了枚举值ToString的实现 ToString方法最终调用System.Type.GetEnumName。以下是相关的源代码,摘自C# 枚举ToString与性能,c#,.net,enums,tostring,C#,.net,Enums,Tostring,我分析了一些C#代码,最后研究了枚举值ToString的实现 ToString方法最终调用System.Type.GetEnumName。以下是相关的源代码,摘自 公共虚拟字符串GetEnumName(对象值) { 如果(值==null) 抛出新的ArgumentNullException(“值”); if(!IsEnum) 抛出新的ArgumentException(Environment.GetResourceString(“Arg_MustBeEnum”),“enumType”); Con
公共虚拟字符串GetEnumName(对象值)
{
如果(值==null)
抛出新的ArgumentNullException(“值”);
if(!IsEnum)
抛出新的ArgumentException(Environment.GetResourceString(“Arg_MustBeEnum”),“enumType”);
Contract.EndContractBlock();
类型valueType=value.GetType();
if(!(valueType.IsEnum | | Type.IsIntegerType(valueType)))
抛出新的ArgumentException(Environment.GetResourceString(“Arg_MustBeEnumBaseTypeOrEnum”),“value”);
数组值=GetEnumRawConstantValues();
int index=BinarySearch(值,值);
如果(索引>=0)
{
字符串[]名称=GetEnumNames();
返回名称[索引];
}
返回null;
}
//以对象数组的形式返回枚举值。
私有数组GetEnumRawConstantValues()
{
字符串[]名称;
数组值;
GetEnumData(输出名称、输出值);
返回值;
}
//这将返回按值排序的EnumValue和EnumName。
私有void GetEnumData(输出字符串[]枚举名称,输出数组枚举值)
{
Contract.Contract(Contract.ValueAtReturn(out enumNames)!=null);
Contract.Contract(Contract.ValueAtReturn(out-enumValues)!=null);
FieldInfo[]flds=GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
对象[]值=新对象[flds.Length];
字符串[]名称=新字符串[flds.Length];
对于(int i=0;i0)
{
名称[j]=名称[j-1];
值[j]=值[j-1];
j--;
交换=真;
如果(j==0)
打破
}
如果(交换)
{
名称[j]=tempStr;
数值[j]=val;
}
}
枚举名称=名称;
枚举值=值;
}
//将所有内容转换为ulong,然后执行二进制搜索。
私有静态int二进制搜索(数组,对象值)
{
ulong[]ulArray=新的ulong[array.Length];
for(int i=0;i
如果我理解正确,每当请求枚举名时:
我能够为一个特定的情况预先计算字典,其中ToString在循环中被多次调用;这带来了显著的性能提升。但是,是否有任何内置的、替代的、更全面的技术也能促进JSON序列化之类的事情?请注意,
GetEnumName
是虚拟的,它被RuntimeType
覆盖。这是通常使用的类型对象的实际运行时类型,因此在正常用例中不太可能实际调用此实现。特别是在通常情况下,静态值少于20。即使Enum可以通过反射进行更新,我也有兴趣了解这种编码的原因。我假设“正常”的Enum不会有数百个值,所以字典/哈希表/任何东西的开销都不会有什么好处。我不认为这是“主要基于意见的”。关于查找工作原理的描述中有一些事实需要纠正,关于为什么二进制搜索方法是一种合理的方法,也有一些实际问题需要纠正。它可能是最好的一个可能是基于意见的,但它比Mike建议的更明智,这让我们能够深入了解好的编程细节。
public virtual string GetEnumName(object value)
{
if (value == null)
throw new ArgumentNullException("value");
if (!IsEnum)
throw new ArgumentException(Environment.GetResourceString("Arg_MustBeEnum"), "enumType");
Contract.EndContractBlock();
Type valueType = value.GetType();
if (!(valueType.IsEnum || Type.IsIntegerType(valueType)))
throw new ArgumentException(Environment.GetResourceString("Arg_MustBeEnumBaseTypeOrEnum"), "value");
Array values = GetEnumRawConstantValues();
int index = BinarySearch(values, value);
if (index >= 0)
{
string[] names = GetEnumNames();
return names[index];
}
return null;
}
// Returns the enum values as an object array.
private Array GetEnumRawConstantValues()
{
string[] names;
Array values;
GetEnumData(out names, out values);
return values;
}
// This will return enumValues and enumNames sorted by the values.
private void GetEnumData(out string[] enumNames, out Array enumValues)
{
Contract.Ensures(Contract.ValueAtReturn<String[]>(out enumNames) != null);
Contract.Ensures(Contract.ValueAtReturn<Array>(out enumValues) != null);
FieldInfo[] flds = GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
object[] values = new object[flds.Length];
string[] names = new string[flds.Length];
for (int i = 0; i < flds.Length; i++)
{
names[i] = flds[i].Name;
values[i] = flds[i].GetRawConstantValue();
}
// Insertion Sort these values in ascending order.
// We use this O(n^2) algorithm, but it turns out that most of the time the elements are already in sorted order and
// the common case performance will be faster than quick sorting this.
IComparer comparer = Comparer.Default;
for (int i = 1; i < values.Length; i++)
{
int j = i;
string tempStr = names[i];
object val = values[i];
bool exchanged = false;
// Since the elements are sorted we only need to do one comparision, we keep the check for j inside the loop.
while (comparer.Compare(values[j - 1], val) > 0)
{
names[j] = names[j - 1];
values[j] = values[j - 1];
j--;
exchanged = true;
if (j == 0)
break;
}
if (exchanged)
{
names[j] = tempStr;
values[j] = val;
}
}
enumNames = names;
enumValues = values;
}
// Convert everything to ulong then perform a binary search.
private static int BinarySearch(Array array, object value)
{
ulong[] ulArray = new ulong[array.Length];
for (int i = 0; i < array.Length; ++i)
ulArray[i] = Enum.ToUInt64(array.GetValue(i));
ulong ulValue = Enum.ToUInt64(value);
return Array.BinarySearch(ulArray, ulValue);
}