使用反射按声明顺序获取属性
我需要按照类中声明的顺序使用反射获取所有属性。根据 MSDN,使用 GetProperties()
时不能保证顺序
GetProperties 方法不返回特定属性 顺序,例如字母顺序或声明顺序。
但我读到有一种解决方法,可以通过 MetadataToken
对属性进行排序。所以我的问题是,这安全吗?我在 MSDN 上似乎找不到任何有关它的信息。或者还有其他方法可以解决这个问题吗?
我当前的实现如下所示:
var props = typeof(T)
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.OrderBy(x => x.MetadataToken);
I need to get all the properties using reflection in the order in which they are declared in the class. According to MSDN the order can not be guaranteed when using GetProperties()
The GetProperties method does not return properties in a particular
order, such as alphabetical or declaration order.
But I've read that there is a workaround by ordering the properties by the MetadataToken
. So my question is, is that safe? I cant seem find any information on MSDN about it. Or is there any other way of solving this problem?
My current implementation looks as follows:
var props = typeof(T)
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.OrderBy(x => x.MetadataToken);
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
在 .net 4.5 (甚至 .net 4.0在 vs2012 中) 你可以使用带有
[CallerLineNumber]
属性的巧妙技巧,通过反射做得更好,让编译器为你插入顺序到你的属性中:然后使用反射:
如果您必须处理部分类,您还可以使用
[CallerFilePath]
对属性进行排序。On .net 4.5 (and even .net 4.0 in vs2012) you can do much better with reflection using clever trick with
[CallerLineNumber]
attribute, letting compiler insert order into your properties for you:And then use reflection:
If you have to deal with partial classes, you can additionaly sort the properties using
[CallerFilePath]
.如果您要走属性路线,这是我过去使用过的方法;
然后像这样使用它;
在哪里;
如果您在所有属性上都没有可比属性的类型上运行该方法,该方法显然会呕吐,因此请小心它的使用方式,它应该足以满足要求。
我省略了 Order : Attribute 的定义,因为 Yahia 的 Marc Gravell 帖子链接中有一个很好的示例。
If you're going the attribute route, here's a method I've used in the past;
Then use it like this;
Where;
The method will barf if you run it on a type without comparable attributes on all of your properties obviously, so be careful how it's used and it should be sufficient for requirement.
I've left out the definition of Order : Attribute as there's a good sample in Yahia's link to Marc Gravell's post.
根据 MSDN
MetadataToken< /code> 在一个模块内是唯一的 - 没有任何说法可以保证任何顺序。
即使它确实按照您想要的方式运行,这也是特定于实现的,并且可能随时更改,恕不另行通知。
请参阅此旧的 MSDN 博客条目。
我强烈建议不要依赖此类实现细节 - 请参阅Marc Gravell 的回答。
如果您在编译时需要一些东西,您可以查看Roslyn(尽管还处于非常早期的阶段)。
According to MSDN
MetadataToken
is unique inside one Module - there is nothing saying that it guarantees any order at all.EVEN if it did behave the way you want it to that would be implementation-specific and could change anytime without notice.
See this old MSDN blog entry.
I would strongly recommend to stay away from any dependency on such implementation details - see this answer from Marc Gravell.
IF you need something at compile time you could take a look at Roslyn (although it is in a very early stage).
另一种可能性是使用 System.ComponentModel.DataAnnotations.DisplayAttribute
Order
属性。由于它是内置的,因此无需创建新的特定属性。
然后选择像这样的有序属性
并且类可以像这样呈现
Another possibility is to use the
System.ComponentModel.DataAnnotations.DisplayAttribute
Order
property.Since it is builtin, there is no need to create a new specific attribute.
Then select ordered properties like this
And class can be presented like this
我测试过的按 MetadataToken 排序的方法是有效的。
这里的一些用户声称这在某种程度上不是一个好的方法/不可靠,但我还没有看到任何证据 - 也许当给定的方法不起作用时你可以在这里发布一些代码片段?
关于向后兼容性 - 当您现在正在使用 .net 4 / .net 4.5 时 - Microsoft 正在制作 .net 5 或更高版本,因此您几乎可以假设这种排序方法将来不会被破坏。
当然,也许到 2017 年,当你升级到 .net9 时,你会遇到兼容性问题,但到那时微软的人可能会弄清楚“官方排序机制”。回去或破坏东西是没有意义的。
使用额外的属性进行属性排序也需要时间和实现 - 如果 MetadataToken 排序有效,为什么还要烦恼呢?
What I have tested sorting by MetadataToken works.
Some of users here claims this is somehow not good approach / not reliable, but I haven't yet seen any evidence of that one - perhaps you can post some code snipet here when given approach does not work ?
About backwards compatibility - while you're now working on your .net 4 / .net 4.5 - Microsoft is making .net 5 or higher, so you pretty much can assume that this sorting method won't be broken in future.
Of course maybe by 2017 when you will be upgrading to .net9 you will hit compatibility break, but by that time Microsoft guys will probably figure out the "official sort mechanism". It does not makes sense to go back or break things.
Playing with extra attributes for property ordering also takes time and implementation - why to bother if MetadataToken sorting works ?
您可以在 System.Component.DataAnnotations 中使用 DisplayAttribute,而不是自定义属性。无论如何,您的要求必须与显示有关。
You may use DisplayAttribute in System.Component.DataAnnotations, instead of custom attribute. Your requirement has to do something with display anyway.
如果您可以强制您的类型具有已知的内存布局,则可以依赖
StructLayout(LayoutKind.Sequential)
然后按内存中的字段偏移量排序。这样,您就不需要类型中每个字段的任何属性。
但也有一些严重的缺点:
输出:
如果您尝试添加任何禁止的字段类型,您将收到 System.ArgumentException:类型“TestRecord”无法封送为非托管结构;无法计算出有意义的大小或偏移量。
If you can enforce your type has a known memory layout, you can rely on
StructLayout(LayoutKind.Sequential)
then sort by the field offsets in memory.This way you don't need any attribute on each field in the type.
Some serious drawbacks though:
Outputs:
If you try to add any forbidden field types, you'll get
System.ArgumentException: Type 'TestRecord' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed.
我是这样做的:
属性声明如下:
I did it this way:
with the property declared as follows:
在上述接受的解决方案的基础上,要获得确切的索引,您可以使用类似
Given
Extensions
Usage
Note< /em>,没有错误检查或容错,可以加胡椒和盐调味
Building on the above accepted solution, to get the exact Index you could use something like this
Given
Extensions
Usage
Note, there is no error checking or fault tolerance, you can add pepper and salt to taste
如果您对额外的依赖项感到满意,Marc Gravell 的 Protobuf-Net 可用于执行此操作而不必担心实现反射和缓存等的最佳方法。只需使用
[ProtoMember]
装饰您的字段,然后使用以下命令按数字顺序访问字段:If you are happy with the extra dependency, Marc Gravell's Protobuf-Net can be used to do this without having to worry about the best way to implement reflection and caching etc. Just decorate your fields using
[ProtoMember]
and then access the fields in numerical order using:即使它是一个非常古老的线程,这是我基于 @Chris McAtackney 的工作解决方案
并且属性是这样的
使用这样
Even it's a very old thread, here is my working solution based on @Chris McAtackney
And the Attribute is like this
Use like this