C# 枚举ToString与性能

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

我分析了一些C#代码,最后研究了枚举值ToString的实现

ToString方法最终调用System.Type.GetEnumName。以下是相关的源代码,摘自

公共虚拟字符串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
如果我理解正确,每当请求枚举名时:

  • 使用插入排序将整个枚举值和名称集插入到数组中
  • 这些值将复制到Ulong数组中
  • 此数组使用二进制搜索进行搜索
  • 我认为这是非常低效的想法是对的?(特别是对于具有数百或数千个值的枚举)

    似乎某种字典/哈希表结构将是理想的。但是,即使是对值进行简单的O(n)线性扫描,也可以在不进行排序、二进制搜索或分配多余数组的情况下实现相同的目标


    我能够为一个特定的情况预先计算字典,其中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);
    }