繁体   English   中英

`Type.GetProperties` 属性顺序

[英]`Type.GetProperties` property order

精简版

Type.GetProperties的 MSDN 文档指出,它返回的集合不能保证按字母顺序或声明顺序排列,但运行一个简单的测试表明它通常按声明顺序返回。 是否有您知道的特定场景不是这种情况? 除此之外,建议的替代方案是什么?

详细版本

我意识到Type.GetProperties的 MSDN 文档指出:

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方法在发现属性时会自下而上地沿着继承链向上走。 那很好,可以这样处理。

问题:

  1. 是否存在我错过的描述行为会有所不同的特定情况?
  2. 如果没有根据建议的顺序又什么建议的方法?

一个看似显而易见的解决方案是定义一个自定义属性,该属性指示属性出现的Order (类似于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的返回集合来工作,但由于文档指出不建议我想了解为什么以及我还有什么其他选择?

根本无法保证订单; 该发生的总会发生。

可能发生变化的明显情况:

  • 任何实现 ICustomTypeDescriptor 的东西
  • 任何带有 TypeDescriptionProvider 的东西

但更微妙的情况是:部分类。 如果一个类被分成多个文件,它们的使用顺序根本没有定义。 请参阅是否正式定义了跨部分类的“文本顺序”?

当然,即使是单个(非部分)定义也没有定义它;p

但是想象一下

文件 1

partial class Foo {
     public int A {get;set;}
}

档案 2

partial class Foo {
    public int B {get;set:}
}

没有正式的声明顺序这里A和B之间看到链接后,看它如何趋于发生,虽然。


重新编辑; 最好的方法是单独指定元帅信息; 一种常见的方法是使用一个接受数字顺序的自定义属性,并用它来装饰成员。 然后,您可以根据此编号进行订购。 protobuf-net 做了一些非常相似的事情,坦率地说,我建议在这里使用现有的序列化库:

[ProtoMember(n)]
public int Foo {get;set;}

其中“n”是一个整数。 特别是在 protobuf-net 的情况下,还有一个 API 可以单独指定这些数字,这在类型不受您直接控制时很有用。

就其价值而言,按 MetadataToken 排序似乎对我有用。

GetType().GetProperties().OrderBy(x => x.MetadataToken)

原始文章(断开的链接,仅在此处列出用于归属): http : //www.sebastienmahe.com/v3/seb.blog/2010/03/08/c-reflection-getproperties-kept-in-declaration-order/

我使用自定义属性来自己添加必要的元数据(它与类似 REST 的服务一起使用,该服务使用并返回 CRLF 分隔的 Key=Value 对。

首先,自定义属性:

class ParameterOrderAttribute : Attribute
{
    public int Order { get; private set; }
    public ParameterOrderAttribute(int order)
    {
        Order = order;
    }
}

然后,装饰你的类:

class Response : Message
{
    [ParameterOrder(0)]
    public int Code { get; set; }
}

class RegionsResponse : Response 
{
    [ParameterOrder(1)]
    public string Regions { get; set; }
}

class HousesResponse : Response
{
    public string Houses { get; set; }
}

将 PropertyInfo 转换为可排序 int 的便捷方法:

    private int PropertyOrder(PropertyInfo propInfo)
    {
        int output;
        var orderAttr = (ParameterOrderAttribute)propInfo.GetCustomAttributes(typeof(ParameterOrderAttribute), true).SingleOrDefault();
        output = orderAttr != null ? orderAttr.Order : Int32.MaxValue;
        return output;
    }

更好的是, write 是一个扩展:

static class PropertyInfoExtensions
{
    private static int PropertyOrder(this PropertyInfo propInfo)
    {
        int output;
        var orderAttr = (ParameterOrderAttribute)propInfo.GetCustomAttributes(typeof(ParameterOrderAttribute), true).SingleOrDefault();
        output = orderAttr != null ? orderAttr.Order : Int32.MaxValue;
        return output;
    }
}

最后,您现在可以使用以下命令查询您的 Type 对象:

        var props = from p in type.GetProperties()
                    where p.CanWrite
                    orderby p.PropertyOrder() ascending
                    select p;

依赖明确记录为无法保证的实现细节是灾难的根源。

“推荐的方法”将根据您拥有这些属性后想要做什么而有所不同。 只是在屏幕上显示它们? MSDN 文档按成员类型(属性、字段、函数)分组,然后在组内按字母顺序排列。

如果您的消息格式依赖于字段的顺序,那么您需要:

  1. 在某种消息定义中指定预期的顺序。 如果我记得的话,Google协议缓冲区就是这样工作的——在这种情况下,消息定义从 .proto 文件编译成代码文件,以用于您碰巧使用的任何语言。

  2. 依赖可以独立生成的顺序,例如字母顺序。

1:

我花了最后一天对 MVC 3 项目中的问题进行故障排除,这一切都归结为这个特定问题。 它基本上依赖于整个会话中的属性顺序是相同的,但是在某些情况下,一些属性会切换位置,从而弄乱了站点。

首先是调用Type.GetProperties()的代码来定义动态 jqGrid 表中的列名,在这种情况下,每个page_load发生一次。 随后调用Type.GetProperties()方法是为了填充表的实际数据,在极少数情况下,属性会切换位置并完全弄乱演示文稿。 在某些情况下,站点依赖于分层子网格的其他属性被切换,即您无法再看到子数据,因为 ID 列包含错误数据。 换句话说:是的,这肯定会发生 谨防。

2:

如果您需要在整个系统会话中保持一致的顺序,但并非所有会话的顺序都完全相同,则解决方法非常简单:将从Type.GetProperties()获得的PropertyInfo[]数组存储为 webcache 中的值或字典中的值类型(或类型名称)作为缓存/字典键。 随后,每当您要执行Type.GetProperties() ,请将其替换为HttpRuntime.Cache.Get(Type/Typename)Dictionary.TryGetValue(Type/Typename, out PropertyInfo[]) 通过这种方式,您将确保始终获得第一次遇到的订单。

如果您总是需要相同的顺序(即对于所有系统会话),我建议您将上述方法与某种类型的配置机制结合起来,即在 web.config/app.config 中指定顺序,对您得到的PropertyInfo[]数组进行排序从Type.GetProperties()按照指定的顺序,然后将其存储在缓存/静态字典中。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM