按声明顺序对枚举进行排序
本文关键字:排序 枚举 声明 顺序 | 更新日期: 2023-09-27 18:16:27
public enum CurrencyId
{
USD = 840,
UAH = 980,
RUR = 643,
EUR = 978,
KZT = 398,
UNSUPPORTED = 0
}
是否有办法按顺序排序Enum.GetValues(typeof(CurrencyId)).Cast<CurrencyId>()
的结果,他们在。cs文件中声明(美元,UAH, RUR, EUR, KZT,不支持),而不是他们的底层代码?就我个人而言,我认为答案是否定的,因为原始顺序在二进制中丢失了,所以……我该如何执行任务?
以下是带有自定义属性的版本:
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public class EnumOrderAttribute : Attribute
{
public int Order { get; set; }
}
public static class EnumExtenstions
{
public static IEnumerable<string> GetWithOrder(this Enum enumVal)
{
return enumVal.GetType().GetWithOrder();
}
public static IEnumerable<string> GetWithOrder(this Type type)
{
if (!type.IsEnum)
{
throw new ArgumentException("Type must be an enum");
}
// caching for result could be useful
return type.GetFields()
.Where(field => field.IsStatic)
.Select(field => new
{
field,
attribute = field.GetCustomAttribute<EnumOrderAttribute>()
})
.Select(fieldInfo => new
{
name = fieldInfo.field.Name,
order = fieldInfo.attribute != null ? fieldInfo.attribute.Order : 0
})
.OrderBy(field => field.order)
.Select(field => field.name);
}
}
用法:
public enum TestEnum
{
[EnumOrder(Order=2)]
Second = 1,
[EnumOrder(Order=1)]
First = 4,
[EnumOrder(Order=3)]
Third = 0
}
var names = typeof(TestEnum).GetWithOrder();
var names = TestEnum.First.GetWithOrder();
短答:
foreach(FieldInfo fi in typeof(CurrencyId).GetFields()
.Where(fi => fi.IsStatic).OrderBy(fi => fi.MetadataToken))
Console.WriteLine(fi.Name);
原因:
public enum EnumOrder {
Bad = -1, Zero = 0, One = 1 }
public class ClassOrder {
public int first;
public int First { get { return first; } }
public int second;
public int Second { get { return second; } } }
private void PrintInfos<T>(string head, IEnumerable<T> list) where T: MemberInfo {
memo.AppendText(string.Format(" {0}: ", head));
bool first = true; foreach(var e in list) {
if(first) first = false; else memo.AppendText(", ");
memo.AppendText(e.Name); }
memo.AppendText("'r'n"); }
private void ReflectionOrderTest(object sender, EventArgs e) {
typeof(EnumOrder).GetField("One");
typeof(ClassOrder).GetField("second");
typeof(ClassOrder).GetProperty("Second");
memo.AppendLine("First time order:");
PrintInfos("Enum", typeof(EnumOrder).GetFields().Where(fi => fi.IsStatic));
PrintInfos("Fields", typeof(ClassOrder).GetFields());
PrintInfos("Properties", typeof(ClassOrder).GetProperties());
PrintInfos("Members", typeof(ClassOrder).GetMembers());
memo.AppendLine("Broken order:");
PrintInfos("Enum", typeof(EnumOrder).GetFields().Where(fi => fi.IsStatic));
PrintInfos("Fields", typeof(ClassOrder).GetFields());
PrintInfos("Properties", typeof(ClassOrder).GetProperties());
PrintInfos("Members", typeof(ClassOrder).GetMembers());
memo.AppendLine("MetadataToken Sorted:");
PrintInfos("Enum", typeof(EnumOrder).GetFields().Where(fi => fi.IsStatic).OrderBy(fi => fi.MetadataToken));
PrintInfos("Fields", typeof(ClassOrder).GetFields().OrderBy(fi => fi.MetadataToken));
PrintInfos("Properties", typeof(ClassOrder).GetProperties().OrderBy(fi => fi.MetadataToken));
PrintInfos("Members", typeof(ClassOrder).GetMembers().OrderBy(fi => fi.MetadataToken));
}
输出:
<>之前第一次顺序:枚举:坏,零,一Fields:第一,第二属性:第一,第二成员:get_First, get_Second, ToString, Equals, GetHashCode, GetType, .ctor, Second, First, Second, First破碎的顺序:枚举:1,Bad, 0Fields:第二,第一属性:第二,第一成员:get_Second, get_First, ToString, Equals, GetHashCode, GetType, .ctor, Second, First, Second, FirstMetadataToken排序:枚举:坏,零,一Fields:第一,第二属性:第一,第二成员:first, second, ToString, Equals, GetHashCode, GetType, get_First, get_Second, .ctor, first, second之前重要提示: MemberInfo.GetFields()
自。net 2.0以来由一些缓存支持(阅读这篇关于它的好文章),并且可能不会以声明的顺序返回字段(更准确地说:编译器发出的顺序似乎保留了一个文件的顺序,但合并partial class
的顺序未定义)。在stackoverflow上也有类似的问题,Marc Gravell的一条评论是:
10.2.6 Members[…]类型中成员的顺序对c#代码来说并不重要,但在与其他语言和环境。在这种情况下,排序在多个部分声明的类型中的成员是未定义的。
这样做可以解决缓存的问题:
GC.Collect();
GC.WaitForPendingFinalizers();
var fields = typeof(Whatever).GetFields();
按元数据token排序可能也有帮助。没有找到一个保证,但这应该提供了一个很好的理由为什么它应该工作:
下面的三个字节,称为记录标识符(RID),对象所指向的元数据表中行的索引token的MSB指。例如,带有value的元数据标记0x02000007表示当前作用域内TypeDef表的第7行。类似地,令牌0x0400001A指的是FieldDef中的第26行(十进制)
原始答:使用typeof(CurrencyId).GetFields()
,检查FieldInfo.IsStatic
(一个__value
不会),然后根据需要使用FieldInfo.Name
或GetValue
。
链接到IDEONE: http://ideone.com/hnT6YL
using System;
using System.Reflection;
public class Test
{
public enum CurrencyId {
USD = 840,
UAH = 980,
RUR = 643,
EUR = 978,
KZT = 398,
UNSUPPORTED = 0
}
public static void Main()
{
foreach(FieldInfo fi in typeof(CurrencyId).GetFields())
if(fi.IsStatic) Console.WriteLine(fi.Name);
}
}
输出:USD
UAH
RUR
EUR
KZT
UNSUPPORTED
EDIT:订单不保证:((见注释)
GetFields方法不按特定顺序返回字段,例如字母顺序或声明顺序。你的代码不能依赖返回字段的顺序,因为该顺序不同。
这可能是。net 4.5
的解决方案using System;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = false)]
public sealed class OrderAttribute : Attribute {
private readonly int order_;
public OrderAttribute(
[CallerLineNumber] int order = 0) {
order_ = order; }
public int Order { get { return order_; } }
}
public class Test {
public enum CurrencyId {
[Order] USD = 840,
[Order] UAH = 980,
[Order] RUR = 643,
[Order] EUR = 978,
[Order] KZT = 398,
[Order] UNSUPPORTED = 0
}
public static void Main() {
foreach(FieldInfo fi in typeof(CurrencyId).GetFields()
.Where(fi => fi.IsStatic)
.OrderBy(fi => ((OrderAttribute)fi.GetCustomAttributes(
typeof(OrderAttribute), true)[0]).Order))
Console.WriteLine(fi.GetValue(null).ToString());
}
}
就用DisplayAttribute
吧。
public enum CurrencyId
{
[Display(Order = 0)]
USD = 840,
[Display(Order = 1)]
UAH = 980,
[Display(Order = 2)]
RUR = 643,
[Display(Order = 3)]
EUR = 978,
[Display(Order = 4)]
KZT = 398,
[Display(Order = 5)]
UNSUPPORTED = 0
}