2011-08-16 37 views
2

的順序我有一個定義返回一個IList<PropertyInfo>一個方法的接口:保證順序相匹配它們出現在代碼文件

public interface IWriteable 
{ 
    IList<PropertyInfo> WriteableProperties(); 
} 



它在不同的(不同的)類實現以下方式:

public abstract class Foo 
{ 
    private IList<PropertyInfo> _props; 

    protected Foo() 
    { 
     this._props = new List<PropertyInfo>(); 

     foreach (PropertyInfo p in this.GetType().GetProperties()) 
     { 
      if (Attribute.IsDefined(p, typeof(WriteableAttribute))) 
       this._props.Add(p); 
     } 
    } 

    #region IWriteable Members 

    public IList<PropertyInfo> WriteableProperties() 
    { 
     return this._props; 
    } 

    #endregion 
} 

public class Bar : Foo 
{ 
    public string A 
    { 
     get { return "A"; } 
    } 

    [Writeable()] 
    public string B 
    { 
     get { return "B"; } 
    } 

    [Writeable()] 
    public string C 
    { 
     get { return "C"; } 
    } 

    // Snip 
} 

請注意標記幾個屬性的屬性,因爲這些是將添加到列表中的屬性。這個IList然後將在某些文件寫入操作期間用於別處。

他們是在它們出現在代碼文件的順序列表排列這對我很重要。

然而,MSDN狀態:

以特定順序

的的GetProperties方法不返回的屬性,如字母或聲明順序。您的代碼不得 取決於返回屬性的順序,因爲 順序不同。

那麼,什麼是確保每個PropertyInfo被添加到我想要的順序?

(我也使用.NET2.0,所以我不能使用任何LINQ的善良,應該有任何能幫助,雖然這將是有趣的。)

+2

你在做什麼,其中的順序是有意義? –

+0

爲第三方創建的文件需要採用特定的格式。 – Andy

回答

6

信息添加到屬性有關排序,然後你可以用它來保證順序,如:

[Writeable(Order = 1)] 

所以以下屬性:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] 
public class WriteableAttribute : Attribute 
{ 
    public int Order { get; set; } 
} 

你可以得到屬性的有序選擇如下:

private readonly List<PropertyInfo> _props; 

protected Foo() 
{ 
    _props = new List<PropertyInfo>(); 

    var props = new Dictionary<int, PropertyInfo>(); 

    foreach (PropertyInfo p in GetType().GetProperties()) 
    { 
     if (Attribute.IsDefined(p, typeof(WriteableAttribute))) 
     { 
      var attr = (WriteableAttribute)p 
       .GetCustomAttributes(typeof(WriteableAttribute))[0]; 

      props.Add(attr.Order, p); 
     } 
    } 

    _props.AddRange(props.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value)); 
} 

NB對於生產代碼我建議緩存(每類型例如)的屬性信息,因爲這將是如果對於每個實例進行相對較慢。

更新 - 緩存

財產查找和排序的一些示例緩存:

public static class PropertyReflector 
{ 
    private static readonly object SyncObj = new object(); 

    private static readonly Dictionary<Type, List<PropertyInfo>> PropLookup = 
     new Dictionary<Type, List<PropertyInfo>>(); 

    public static IList<PropertyInfo> GetWritableProperties(Type type) 
    { 
     lock (SyncObj) 
     { 
      List<PropertyInfo> props; 

      if (!PropLookup.TryGetValue(type, out props)) 
      { 
       var propsOrder = new Dictionary<int, PropertyInfo>(); 

       foreach (PropertyInfo p in type.GetProperties()) 
       { 
        if (Attribute.IsDefined(p, typeof(WriteableAttribute))) 
        { 
         var attr = (WriteableAttribute)p.GetCustomAttributes(
          typeof(WriteableAttribute), inherit: true)[0]; 

         propsOrder.Add(attr.Order, p); 
        } 
       } 

       props = new List<PropertyInfo>(propsOrder 
        .OrderBy(kvp => kvp.Key) 
        .Select(kvp => kvp.Value)); 

       PropLookup.Add(type, props); 
      } 

      return props; 
     } 
    } 
} 

更新 - 沒有LINQ的

您可以替換用下面的代碼LINQ的部分要訂購這些屬性並將它們添加到緩存中:

List<int> order = new List<int>(propsOrder.Keys); 
order.Sort(); 

props = new List<PropertyInfo>(); 

order.ForEach(i => props.Add(propsOrder[i])); 

PropLookup.Add(type, props); 

更新 - 完整的Linq

,並使用完全Linq的解決方案:

static IList<PropertyInfo> GetWritableProperties(Type type) 
{ 
    lock (SyncObj) 
    { 
     List<PropertyInfo> props; 

     if (!PropLookup.TryGetValue(type, out props)) 
     { 
      props = type.GetProperties() 
       .Select(p => new { p, Atts = p.GetCustomAttributes(typeof(WriteableAttribute), inherit: true) }) 
       .Where(p => p.Atts.Length != 0) 
       .OrderBy(p => ((WriteableAttribute)p.Atts[0]).Order) 
       .Select(p => p.p) 
       .ToList(); 

      PropLookup.Add(type, props); 
     } 

     return props; 
    } 
} 
+0

@chibacity:很好的答案,謝謝。雖然在我使用.NET2.0的時候,我不能使用.Select擴展方法,所以不得不重寫它。另外,你是什麼意思緩存屬性信息?它已經在私人列表中緩存了,不是嗎,還是你的意思是別的? – Andy

+0

@Andy在我的示例中,將爲每個類類型緩存proprty信息,而不是針對您創建的每個實例。效率更高。 –

+0

@Andy我將示例代碼重構爲一個實用類,因此您不必強制進入繼承層次結構以獲取所需的行爲。我還包括Linq和非Liq的例子。 –

1

前一陣子,當我有我寫了同樣的問題,一個輔助類基於該Order屬性進行排序屬性的屬性。我使用了內置的DisplayAttribute,但您只需將Order屬性添加到您編寫的任何屬性即可。

class FieldSorter : IComparer, IComparer<DisplayAttribute>, IEqualityComparer<DisplayAttribute> 
{ 
    public int Compare(object x, object y) 
    { 
     return Compare((DisplayAttribute)x, (DisplayAttribute)y); 
    } 
    public int Compare(DisplayAttribute x, DisplayAttribute y) 
    { 
     return x.Order.CompareTo(y.Order); 
    } 
    public bool Equals(DisplayAttribute x, DisplayAttribute y) 
    { 
     return Compare(x, y) == 0; 
    } 
    public int GetHashCode(DisplayAttribute obj) 
    { 
     return obj.GetHashCode(); 
    } 

    public static SortedList<DisplayAttribute, PropertyInfo> GetSortedFields(Type type) 
    { 
     PropertyInfo[] props = type.GetProperties(); 
     var sortedProps = new SortedList<DisplayAttribute, PropertyInfo>(props.Length, new FieldSorter()); 
     object[] atts; 
     int assignedOrder = 1000; // anything without pre-assigned order gets a ridiculously high order value. same for duplicates. 
     foreach (var prop in props) 
     { 
      atts = prop.GetCustomAttributes(typeof(DisplayAttribute), true); 
      if (atts.Length > 0) 
      { 
       var att = (DisplayAttribute)atts[0]; 
       if (!att.GetOrder().HasValue || sortedProps.Keys.Contains(att, new FieldSorter())) 
        att.Order = assignedOrder++; 
       sortedProps.Add(att, prop); 
      } 
     } 
     return sortedProps; 
    } 
} 

這給你一個SortedList其中關鍵是屬性和值是的PropertyInfo。這是因爲我仍然需要訪問屬性的其他屬性。

用法示例:

 public class Stats 
     { 
      [Display(Name = "Changes", Description = "Changed records.", Order = 8)] 
      public int RecordsWithChanges { get; set; } 
      [Display(Name = "Invalid", Description = "Number of invalid records analyzed.", Order = 4)] 
      public int InvalidRecordCount { get; set; } 
      [Display(Name = "Valid", Description = "Number of valid records.", Order = 6)] 
      public int ValidRecordCount { get; set; } 
      [Display(Name = "Cost", Description = "Number of records with a Cost value.", Order = 10)] 
      public int RecordsWithCost { get; set; } 
      public Stats(int changed, int valid, int invalid, int cost) 
      { 
       RecordsWithChanges = changed; 
       ValidRecordCount = valid; 
       InvalidRecordCount = invalid; 
       RecordsWithCost = cost; 
      } 
     } 

     class Program 
     { 
      static void Main(string[] args) 
      { 
       var foo = new Stats(123, 456, 7, 89); 
       var fields = FieldSorter.GetSortedFields(foo.GetType()); 
       foreach (DisplayAttribute att in fields.Keys) 
        Console.WriteLine("{0}: {1} ({2}) == {3}", 
         att.Order, att.Name, att.Description, fields[att].GetValue(foo, null)); 
null)); 

      } 
     } 

輸出:

4: Invalid (Number of invalid records analyzed.) -- 7 
6: Valid (Number of valid records.) -- 456 
8: Changes (Changed records.) -- 123 
10: Cost (Number of records with a Cost value.) -- 89 
相關問題