簡體   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