使用reflection按声明的顺序获取属性
我需要按照在类中声明的顺序使用reflection来获取所有属性。 根据MSDN,使用GetProperties()
时不能保证顺序
GetProperties方法不会以特定顺序(如字母或声明顺序)返回属性。
但是我已经读过,有一个解决方法是通过MetadataToken
对属性进行sorting。 所以我的问题是,这是安全的吗? 我似乎无法find有关MSDN的任何信息。 或者还有解决这个问题的其他方法吗?
我目前的执行情况如下:
var props = typeof(T) .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) .OrderBy(x => x.MetadataToken);
根据MSDN, MetadataToken
在一个模块中是独一无二的 – 没有任何说法能够保证任何顺序。
即使它确实按照您所希望的方式行事,这也是针对特定实现的,并且可能随时更改,恕不另行通知。
看到这个老的MSDN博客条目 。
我强烈build议远离这种实现细节的任何依赖 – 请参阅Marc Gravell的这个答案 。
如果你在编译时需要一些东西,你可以看看Roslyn (尽pipe它已经处于很早的阶段)。
在.net 4.5 (甚至vs2012中的.net 4.0)中,你可以使用[CallerLineNumber]
属性的巧妙技巧来reflection效果更好,让编译器向你的属性插入顺序:
[AttributeUsage(AttributeTargets.Property, 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 { //This sets order_ field to current line number [Order] public int Property2 { get; set; } //This sets order_ field to current line number [Order] public int Property1 { get; set; } }
然后使用reflection:
var properties = from property in typeof(Test).GetProperties() where Attribute.IsDefined(property, typeof(OrderAttribute)) orderby ((OrderAttribute)property.GetCustomAttributes(typeof(OrderAttribute), false).Single()).Order select property; foreach (var property in properties) { }
如果必须处理部分类,则可以使用[CallerFilePath]
添加属性对属性进行sorting。
如果你要去的属性路线,这是我以前使用的一种方法;
public static IOrderedEnumerable<PropertyInfo> GetSortedProperties<T>() { return typeof(T) .GetProperties() .OrderBy(p => ((Order)p.GetCustomAttributes(typeof(Order), false)[0]).Order); }
然后像这样使用它;
var test = new TestRecord { A = 1, B = 2, C = 3 }; foreach (var prop in GetSortedProperties<TestRecord>()) { Console.WriteLine(prop.GetValue(test, null)); }
哪里;
class TestRecord { [Order(1)] public int A { get; set; } [Order(2)] public int B { get; set; } [Order(3)] public int C { get; set; } }
如果你在一个没有可比属性的types上运行它,那么这个方法就会被禁止,所以要小心如何使用它,它应该足够满足要求。
我遗漏了Order:Attribute的定义,因为在Yahia和Marc Gravell的post中有一个很好的例子。
我testing了通过MetadataTokensorting的作品。
这里的一些用户声称这是一个不好的方法/不可靠的,但我还没有看到任何证据 – 也许你可以在这里发表一些代码snipet给定的方法不起作用?
关于向后兼容性 – 当您正在处理.net 4 / .net 4.5 – Microsoft正在制作.net 5或更高版本时,几乎可以认为这种sorting方法将来不会被破坏。
当然,也许到2017年,当你升级到.net9的时候,你将会遇到兼容性问题,但到那个时候,微软可能会弄清楚“官方sorting机制”。 回去或破坏事情是没有意义的。
使用属性sorting的额外属性也需要时间和实现 – 为什么要打扰如果MetadataTokensorting工作?
您可以在System.Component.DataAnnotations中使用DisplayAttribute,而不是自定义属性。 无论如何,你的要求必须做一些显示。
如果您对额外的依赖感到满意,那么Marc Gravell的Protobuf-Net就可以用来做这个,而不必担心实现reflection和caching等的最佳方式。只需使用[ProtoMember]
来装饰您的字段,然后使用数值订单使用:
MetaType metaData = ProtoBuf.Meta.RuntimeTypeModel.Default[typeof(YourTypeName)]; metaData.GetFields();