`Type.GetProperties` 属性顺序
简短版本
Type.GetProperties 的 MSDN 文档
声明它返回的集合不保证按字母顺序或声明顺序排列,尽管运行一个简单的测试表明通常它是按声明顺序返回的。您是否知道有哪些具体情况并非如此?除此之外,建议的替代方案是什么?
详细版本
我了解Type的MSDN文档.GetProperties
指出:
GetProperties 方法不返回特定属性 顺序,例如字母顺序或声明顺序。你的代码一定不能 取决于返回属性的顺序,因为 顺序各不相同。
因此不能保证该方法返回的集合将以任何特定方式排序。根据一些测试,我发现相反,返回的属性按照它们在类型中定义的顺序出现。
示例:
class Simple
{
public int FieldB { get; set; }
public string FieldA { get; set; }
public byte FieldC { get; set; }
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Simple Properties:");
foreach (var propInfo in typeof(Simple).GetProperties())
Console.WriteLine("\t{0}", propInfo.Name);
}
}
输出:
Simple Properties:
FieldB
FieldA
FieldC
一种仅略有不同的情况是,当相关类型的父级也具有属性时:
class Parent
{
public int ParentFieldB { get; set; }
public string ParentFieldA { get; set; }
public byte ParentFieldC { get; set; }
}
class Child : Parent
{
public int ChildFieldB { get; set; }
public string ChildFieldA { get; set; }
public byte ChildFieldC { get; set; }
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Parent Properties:");
foreach (var propInfo in typeof(Parent).GetProperties())
Console.WriteLine("\t{0}", propInfo.Name);
Console.WriteLine("Child Properties:");
foreach (var propInfo in typeof(Child).GetProperties())
Console.WriteLine("\t{0}", propInfo.Name);
}
}
输出:
Parent Properties:
ParentFieldB
ParentFieldA
ParentFieldC
Child Properties:
ChildFieldB
ChildFieldA
ChildFieldC
ParentFieldB
ParentFieldA
ParentFieldC
这意味着 GetProperties
方法在发现时自下而上沿着继承链向上走的属性。这很好,可以这样处理。
问题:
- 是否存在我所错过的所描述的行为有所不同的特定情况?
- 如果不建议依赖于顺序,那么推荐的方法是什么??
一种看似显而易见的解决方案是定义一个自定义属性,该属性指示属性应该出现的顺序(类似于DataMember
上的Order
属性> 属性)。类似于:
public class PropOrderAttribute : Attribute
{
public int SeqNbr { get; set; }
}
然后实现诸如:
class Simple
{
[PropOrder(SeqNbr = 0)]
public int FieldB { get; set; }
[PropOrder(SeqNbr = 1)]
public string FieldA { get; set; }
[PropOrder(SeqNbr = 2)]
public byte FieldC { get; set; }
}
但正如许多人发现的那样,如果您的类型有 100 个属性并且您需要在前 2 个属性之间添加一个属性,这将成为一个严重的维护问题。
更新
此处显示的示例仅用于演示目的。在我的特定场景中,我使用类定义消息格式,然后迭代该类的属性并获取它们的属性以查看应如何对消息中的特定字段进行解组。消息中字段的顺序很重要,因此我的类中属性的顺序也必须很重要。
目前,它仅通过迭代 GetProperties
的返回集合来工作,但由于文档声明不建议这样做,所以我想了解为什么以及我还有什么其他选择?
Short Version
The MSDN documentation for Type.GetProperties
states that the collection it returns is not guaranteed to be in alphabetical or declaration order, though running a simple test shows that in general it is returned in declaration order. Are there specific scenarios that you know of where this is not the case? Beyond that, what is the suggested alternative?
Detailed Version
I realize the MSDN documentation for Type.GetProperties
states:
The GetProperties method does not return properties in a particular
order, such as alphabetical or declaration order. Your code must not
depend on the order in which properties are returned, because that
order varies.
so there is no guarantee that the collection returned by the method will be ordered any specific way. Based on some tests, I've found to the contrary that the properties returned appear in the order they're defined in the type.
Example:
class Simple
{
public int FieldB { get; set; }
public string FieldA { get; set; }
public byte FieldC { get; set; }
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Simple Properties:");
foreach (var propInfo in typeof(Simple).GetProperties())
Console.WriteLine("\t{0}", propInfo.Name);
}
}
Output:
Simple Properties:
FieldB
FieldA
FieldC
One such case that this differs only slightly is when the type in question has a parent who also has properties:
class Parent
{
public int ParentFieldB { get; set; }
public string ParentFieldA { get; set; }
public byte ParentFieldC { get; set; }
}
class Child : Parent
{
public int ChildFieldB { get; set; }
public string ChildFieldA { get; set; }
public byte ChildFieldC { get; set; }
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Parent Properties:");
foreach (var propInfo in typeof(Parent).GetProperties())
Console.WriteLine("\t{0}", propInfo.Name);
Console.WriteLine("Child Properties:");
foreach (var propInfo in typeof(Child).GetProperties())
Console.WriteLine("\t{0}", propInfo.Name);
}
}
Output:
Parent Properties:
ParentFieldB
ParentFieldA
ParentFieldC
Child Properties:
ChildFieldB
ChildFieldA
ChildFieldC
ParentFieldB
ParentFieldA
ParentFieldC
Which means the GetProperties
method walks up the inheritance chain from bottom up when discovering the properties. That's fine and can be handled as such.
Questions:
- Are there specific situations where the described behavior would differ that I've missed?
- If depending on the order is not recommended then what is the recommended approach?
One seemingly obvious solution would be to define a custom attribute which indicates the order in which the properties should appear (Similar to the Order
property on the DataMember
attribute). Something like:
public class PropOrderAttribute : Attribute
{
public int SeqNbr { get; set; }
}
And then implement such as:
class Simple
{
[PropOrder(SeqNbr = 0)]
public int FieldB { get; set; }
[PropOrder(SeqNbr = 1)]
public string FieldA { get; set; }
[PropOrder(SeqNbr = 2)]
public byte FieldC { get; set; }
}
But as many have found, this becomes a serious maintenance problem if your type has 100 properties and you need to add one between the first 2.
UPDATE
The examples shown here are simply for demonstrative purposes. In my specific scenario, I define a message format using a class, then iterate through the properties of the class and grab their attributes to see how a specific field in the message should be demarshaled. The order of the fields in the message is significant so the order of the properties in my class needs to be significant.
It works currently by just iterating over the return collection from GetProperties
, but since the documentation states it is not recommended I was looking to understand why and what other option do I have?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
根本无法保证订单;无论发生什么......都会发生。
它可能会改变的明显情况:
,但更微妙的情况是:部分类。如果一个类分为多个文件,则根本没有定义它们的使用顺序。请参阅跨部分类的“文本顺序”是否正式定义?< /a>
当然,即使是单个(非部分)定义也没有定义;p
但是想象一下
File 1
File 2
A 和 B 之间没有正式的声明顺序。请参阅链接的帖子以了解它是如何实现的不过,往往会发生。
重新编辑;最好的方法是单独指定元帅信息;一种常见的方法是使用采用数字顺序的自定义属性,并用它来装饰成员。然后您可以根据这个数字进行订购。 protobuf-net 做了非常相似的事情,坦率地说,我建议在这里使用现有的序列化库:
其中“n”是一个整数。特别是在 protobuf-net 的情况下,还有一个 API 可以单独指定这些数字,这在类型不受您直接控制时非常有用。
The order simply isn't guaranteed; whatever happens.... Happens.
Obvious cases where it could change:
But a more subtle case: partial classes. If a class is split over multiple files, the order of their usage is not defined at all. See Is the "textual order" across partial classes formally defined?
Of course, it isn't defined even for a single (non-partial) definition ;p
But imagine
File 1
File 2
There is no formal declaration order here between A and B. See the linked post to see how it tends to happen, though.
Re your edit; the best approach there is to specify the marshal info separately; a common approach would be to use a custom attribute that takes a numeric order, and decorate the members with that. You can then order based on this number. protobuf-net does something very similar, and frankly I'd suggest using an existing serialization library here:
Where "n" is an integer. In the case of protobuf-net specifically, there is also an API to specify these numbers separately, which is useful when the type is not under your direct control.
就其价值而言,按 MetadataToken 排序似乎对我有用。
原始文章(链接已损坏,仅在此处列出以供归属):
http://www.sebastienmahe.com/v3/seb.blog/2010/03/08/c-reflection-getproperties-kept-in-declaration-order/
For what it's worth, sorting by MetadataToken seemed to work for me.
Original Article (broken link, just listed here for attribution):
http://www.sebastienmahe.com/v3/seb.blog/2010/03/08/c-reflection-getproperties-kept-in-declaration-order/
我使用自定义属性自己添加必要的元数据(它与类似 REST 的服务一起使用,该服务使用并返回 CRLF 分隔的 Key=Value 对。
首先,自定义属性:
然后,装饰您的类:
将 PropertyInfo 转换为sortable int:
更好的是,write 是一个扩展:
最后,您现在可以使用以下方式查询您的 Type 对象:
I use custom attributes to add the necessary metadata myself (it's used with a REST like service which consumes and returns CRLF delimited Key=Value pairs.
First, a custom attribute:
Then, decorate your classes:
A handy method for converting a PropertyInfo into a sortable int:
Even better, write is as an extension:
Finally you can now query your Type object with:
1:
我花了最后一天时间对 MVC 3 项目中的一个问题进行故障排除,这一切都归结为这个特定问题。它基本上依赖于整个会话期间属性顺序相同,但在某些情况下,一些属性交换了位置,从而弄乱了站点。
首先,调用
Type.GetProperties()
的代码在动态 jqGrid 表中定义列名称,在本例中,每个page_load
都会发生一次。随后调用Type.GetProperties()
方法是为了填充表的实际数据,在极少数情况下,属性会交换位置并完全搞乱了演示。在某些情况下,站点依赖于分层子网格的其他属性被切换,即您无法再看到子数据,因为 ID 列包含错误数据。换句话说:是的,这肯定会发生。提防。2:
如果您需要整个系统会话的顺序一致,但所有会话的顺序不一定完全相同,解决方法非常简单:存储
PropertyInfo[]< /code> 数组,您从
Type.GetProperties()
获取,作为网络缓存中或以类型(或类型名称)作为缓存/字典键的字典中的值。随后,每当您要执行Type.GetProperties()
时,请将其替换为HttpRuntime.Cache.Get(Type/Typename)
或Dictionary。 TryGetValue(类型/类型名称,out PropertyInfo[])
。这样,您就可以保证始终收到您第一次遇到的订单。如果您总是需要相同的顺序(即对于所有系统会话),我建议您将上述方法与某种类型的配置机制结合起来,即在 web.config/app.config 中指定顺序,对 PropertyInfo[] 进行排序 你按照指定的顺序从
Type.GetProperties()
获取的数组,然后将其存储在缓存/静态字典中。1:
I've spent the last day troubleshooting a problem in an MVC 3 project, and it all came down to this particular problem. It basically relied on the property order being the same throughout the session, but on some occations a few of the properties switched places, messing up the site.
First the code called
Type.GetProperties()
to define column names in a dynamic jqGrid table, something that in this case occurs once perpage_load
. Subsequent times theType.GetProperties()
method was called was to populate the actual data for the table, and in some rare instances the properties switched places and messed up the presentation completely. In some instances other properties that the site relied upon for a hierarchical subgrid got switched, i.e. you could no longer see the sub data because the ID column contained erroneous data. In other words: yes, this can definitely happen. Beware.2:
If you need consistent order throughout the system session but not nessecarily exactly the same order for all sessions the workaround is dead simple: store the
PropertyInfo[]
array you get fromType.GetProperties()
as a value in the webcache or in a dictionary with the type (or typename) as the cache/dictionary key. Subsequently, whenever you're about to do aType.GetProperties()
, instead substitute it forHttpRuntime.Cache.Get(Type/Typename)
orDictionary.TryGetValue(Type/Typename, out PropertyInfo[])
. In this way you'll be guaranteed to always get the order you encountered the first time.If you always need the same order (i.e. for all system sessions) I suggest you combine the above approach with some type of configuration mechanism, i.e. specify the order in the web.config/app.config, sort the
PropertyInfo[]
array you get fromType.GetProperties()
according to the specified order, and then store it in cache/static dictionary.依赖明确记录为无法保证的实现细节会导致灾难。
“推荐的方法”会根据您拥有这些属性后想要对它们执行的操作而有所不同。只是将它们显示在屏幕上? MSDN 文档按成员类型(属性、字段、函数)进行分组,然后在组内按字母顺序排列。
如果您的消息格式依赖于字段的顺序,那么您需要:
在某种消息定义中指定预期的顺序。如果我记得的话,Google 协议缓冲区就是这样工作的 - 在这种情况下,消息定义是从.proto 文件转换为代码文件,以便在您使用的任何语言中使用。
依赖于可以独立生成的顺序,例如字母顺序。
Relying on an implementation detail that is explicitly documented as being not guaranteed is a recipe for disaster.
The 'recommended approach' would vary depending on what you want to do with these properties once you have them. Just displaying them on the screen? MSDN docs group by member type (property, field, function) and then alphabetize within the groups.
If your message format relies on the order of the fields, then you'd need to either:
Specify the expected order in some sort of message definition. Google protocol buffers works this way if I recall- the message definition is compiled in that case from a .proto file into a code file for use in whatever language you happen to be working with.
Rely on an order that can be independently generated, e.g. alphabetical order.