如果有多个具有相同值的枚举常量,是否存在一个常数我得到的逻辑?
我尝试了下面的变化,但无法得到合理的逻辑。
public class Program
{
public static void Main(string[] args)
{
Test a = 0;
Console.WriteLine(a);
}
}
enum Test
{
a1=0,
a2=0,
a3=0,
a4=0,
}
输出:
a2
enum Test
{
a1=0,
a2=0,
a3,
a4=0,
}
输出:
a4
enum Test
{
a1=0,
a2=0,
a3,
a4,
}
输出:
a2
enum Test
{
a1=0,
a2=0,
a3,
a4
}
输出:
a1
答案 0 :(得分:68)
documentation实际上解决了这个问题:
如果多个枚举成员具有相同的基础值,并且您尝试根据其基础值检索枚举成员名称的字符串表示形式,您的代码不应对该方法将返回的名称做出任何假设强>
(强调补充)
但是,这并不意味着结果是 random 。这意味着它是一个可能会发生变化的实现细节。只需一个补丁就可以完全改变实现,在编译器(MONO,Roslyn等)之间可能会有所不同,并且在不同的平台上会有所不同。
如果您的系统设计为需要,则枚举的反向查找在时间和平台上是一致的,那么不会使用 Enum.ToString
。要么改变你的设计,要么它不依赖于那个细节,要么编写自己的方法将保持一致。
因此,您不应编写依赖于该实现的代码,否则您将承担在将来的版本中不知情的情况下会发生更改的风险。
答案 1 :(得分:28)
TL; DR:枚举的所有字段都将通过反射提取,然后插入排序并二进制搜索第一个匹配值。
调用链看起来像这样:
Enum.Tostring();
Enum.InternalFormat(RuntimeType eT, Object value);
Enum.GetName(Type enumType, Object value);
Type.GetEnumName(object value);
Type.GetEnumName(object value)
实现如下:
public virtual string GetEnumName(object value)
{
// standard argument guards...
Array values = GetEnumRawConstantValues();
int index = BinarySearch(values, value);
if (index >= 0)
{
string[] names = GetEnumNames();
return names[index];
}
return null;
}
GetEnumRawConstantValues()
和GetEnumNames()
都依赖于GetEnumData(out string[] enumNames, out Array enumValues)
:
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;
}
关注后,GetFields(BindingFlags bindingAttr)
会导致abstract
方法,但会搜索&#34; GetFields&#34;在msdn上会产生EnumBuilder.GetFields(BindingFlags bindingAttr)
。如果我们遵循其调用链:
EnumBuilder.GetFields(BindingFlags bindingAttr);
TypeBuilder.GetFields(BindingFlags bindingAttr);
RuntimeType.GetFields(BindingFlags bindingAttr);
RuntimeType.GetFieldCandidates(String name, BindingFlags bindingAttr, bool allowPrefixLookup);
RuntimeTypeCache.GetFieldList(MemberListType listType, string name);
RuntimeTypeCache.GetMemberList<RuntimeFieldInfo>(ref MemberInfoCache<T> m_cache, MemberListType listType, string name, CacheType cacheType);
MemberInfoCache<RuntimeFieldInfo>.GetMemberList(MemberListType listType, string name, CacheType cacheType);
MemberInfoCache<RuntimeFieldInfo>.Populate(string name, MemberListType listType, CacheType cacheType);
MemberInfoCache<RuntimeFieldInfo>.GetListByName(char* pName, int cNameLen, byte* pUtf8Name, int cUtf8Name, MemberListType listType, CacheType cacheType);
MemberInfoCache<RuntimeFieldInfo>.PopulateFields(Filter filter);
// and from here, it is a wild ride...
所以,我会引用Type.GetFields
评论:
GetFields方法不会按特定顺序返回字段,例如按字母顺序或声明顺序。您的代码不得依赖于返回字段的顺序,因为该顺序会有所不同。