2013-12-12 92 views
15

我在做類似Recursively Get Properties & Child Properties Of An Object的事情,但我想遞歸地使用反射來獲取每個屬性。我從Recursively Print the properties得到了代碼。遞歸獲取屬性和子類屬性

該代碼的問題是:它只能向下一級,我不知道如何自動獲取所有使用反射的屬性?我只是做了下面的樣品容器代碼:

public class Container 
{ 
    public Bottle MyBottle { get; set; } 
    public List<Address> Addresses { get; set; } 

    public Container() 
    { 
     Address a = new Address(); 
     a.AddressLine1 = "1 Main St"; 
     a.AddressLine2 = "2 Main St"; 
     Addresses = new List<Address>(); 
     Addresses.Add(a); 

     MyBottle = new Bottle(); 
     MyBottle.BottleName = "Big bottle"; 
     MyBottle.BottageAge = 2; 
    } 
} 

public class Bottle 
{ 
    public string BottleName { get; set; } 
    public int BottageAge { get; set; } 
} 

public class Address 
{ 
    public string AddressLine1 { get; set; } 
    public string AddressLine2 { get; set; } 
    public List<SpecialFolder> SpecialFolders { get; set; } 

    public Address() 
    { 
     SpecialFolders = new List<SpecialFolder>(); 
     SpecialFolder sf = new SpecialFolder(); 
     sf.TemplateFolder = Environment.SpecialFolder.Templates.ToString(); 
     sf.UserFolder = Environment.SpecialFolder.UserProfile.ToString(); 
     SpecialFolders.Add(sf); 
    } 
} 

public class SpecialFolder 
{ 
    public string TemplateFolder { get; set; } 
    public string UserFolder { get; set; } 
} 

在Main方法:

static void Main(string[] args) 
{ 
    Container c = new Container(); 
    PrintProperties(c); 
} 
public static void PrintProperties(object obj) 
{ 
    PrintProperties(obj, 0); 
} 
public static void PrintProperties(object obj, int indent) 
{ 

    if (obj == null) return; 
    string indentString = new string(' ', indent); 
    Type objType = obj.GetType(); 
    PropertyInfo[] properties = objType.GetProperties(); 
    foreach (PropertyInfo property in properties) 
    { 
     object propValue = property.GetValue(obj, null); 
     if (property.PropertyType.Assembly == objType.Assembly) 
     { 
      Console.WriteLine("{0}{1}:", indentString, property.Name); 

      PrintProperties(propValue, indent + 2); 
     } 
     else 
     { 
      Console.WriteLine("{0}{1}: {2}", indentString, property.Name, propValue); 
     } 
    } 
} 

我希望能得到:

MyBottle: 
     BottleName: Big bottle 
     BottageAge: 2 
Addresses: 
     AddressLine1: 1 Main St 
     AddressLine2: 2 Main St 
     SpecialFolders: 
      TemplateFolder: Templates 
      UserFolder: UserProfile 

結果我現在得到:

MyBottle: 
    BottleName: Big bottle 
    BottageAge: 2 
Addresses: System.Collections.Generic.List`1[TreeViewReflectionExample.Address] 

有人可以幫我機智h PrintProperties方法?非常感謝你。

回答

28

你有兩個問題與您的代碼:

  1. 因爲條件if (property.PropertyType.Assembly == objType.Assembly)你會忽略System.CollectionsList<>
  2. 你不區別對待propValue是集合。因此它將打印List屬性,而不是其元素屬性。

您可以更改成例如:

public void PrintProperties(object obj, int indent) 
{  
    if (obj == null) return; 
    string indentString = new string(' ', indent); 
    Type objType = obj.GetType(); 
    PropertyInfo[] properties = objType.GetProperties(); 
    foreach (PropertyInfo property in properties) 
    { 
     object propValue = property.GetValue(obj, null); 
     var elems = propValue as IList; 
     if (elems != null) 
     { 
      foreach (var item in elems) 
      { 
       PrintProperties(item, indent + 3); 
      } 
     } 
     else 
     { 
      // This will not cut-off System.Collections because of the first check 
      if (property.PropertyType.Assembly == objType.Assembly) 
      { 
       Console.WriteLine("{0}{1}:", indentString, property.Name); 

       PrintProperties(propValue, indent + 2); 
      } 
      else 
      { 
       Console.WriteLine("{0}{1}: {2}", indentString, property.Name, propValue); 
      } 
     } 
    } 
} 
+1

正是我一直在尋找的,不僅工作在這個例子中,但對於所有其他情況下,我不得不 – HoKy22

+0

這是有益的。我做了一個小的補充,確保在調用GetValue之前可以讀取該屬性,以防止在只寫屬性上發生異常 - 只需用「if(property.CanRead){...」包裝它即可。 – thephez

+0

C#6語法糖的一小竅門:'if(propValue是IList elems)'工作並保存一行。 – CaptainMarvel

7

要單獨處理基本類型和字符串,並在可枚舉,而不是隻考慮自己的ToString()值循環。因此,您的代碼可以更新爲:

public void PrintProperties(object obj, int indent) 
{ 

    if (obj == null) return; 
    string indentString = new string(' ', indent); 
    Type objType = obj.GetType(); 
    PropertyInfo[] properties = objType.GetProperties(); 
    foreach (PropertyInfo property in properties) 
    { 
     object propValue = property.GetValue(obj, null); 
     if(property.PropertyType.IsPrimitive || property.PropertyType == typeof(string)) 
      Console.WriteLine("{0}{1}: {2}", indentString, property.Name, propValue); 
     else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType)) 
     { 
      Console.WriteLine("{0}{1}:", indentString, property.Name); 
      IEnumerable enumerable = (IEnumerable)propValue; 
      foreach(object child in enumerable) 
       PrintProperties(child, indent + 2); 
     } 
     else 
     { 
      Console.WriteLine("{0}{1}:", indentString, property.Name); 
      PrintProperties(propValue, indent + 2); 
     } 
    } 
} 
+0

此代碼非常適用於本例中,我添加了一些更多的屬性和類,我有無限循環。我正在尋找一種通用的方式來獲得所有的屬性。 – HoKy22

+0

經過一些小的修改,這段代碼爲我工作。 – dee

1

它適用於所有情況,但propValue是string []。您將在行中得到「參數計數不匹配」異常: object propValue = property.GetValue(obj,null);

要解決這個問題,你可以使用此代碼一點點修復:

private void PrintProperties(object obj, int indent) 
    { 
     if (obj == null) return; 
     string indentString = new string(' ', indent); 
     Type objType = obj.GetType(); 
     PropertyInfo[] properties = objType.GetProperties(); 
     foreach (PropertyInfo property in properties) 
     { 
      object propValue = property.GetValue(obj, null); 

      var elems = propValue as IList; 
      if ((elems != null) && !(elems is string[])) 
      { 
       foreach (var item in elems) 
       { 
        PrintProperties(item, indent + 3); 
       } 
      } 
      else 
      { 
       // This will not cut-off System.Collections because of the first check 
       if (property.PropertyType.Assembly == objType.Assembly) 
       { 
        LogToWindow(String.Format("{0}{1}:", indentString, property.Name)); 
        PrintProperties(propValue, indent + 2); 
       } 
       else 
       { 
        if (propValue is string[]) 
        { 
         var str = new StringBuilder(); 
         foreach (string item in (string[])propValue) 
         { 
          str.AppendFormat("{0}; ", item); 
         } 
         propValue = str.ToString(); 
         str.Clear(); 
        } 
        LogToWindow(String.Format("{0}{1}: {2}", indentString, property.Name, propValue)); 
       } 
      } 
     } 
    } 
-1

我改變driis的代碼波紋管。這個對我有用。

public void PrintProperties(object obj, int indent) 
{ 
    if (obj == null) 
    { 
     return; 
    } 
    string indentString = new string(' ', indent); 
    Type objType = obj.GetType(); 
    PropertyInfo[] properties = objType.GetProperties(); 
    foreach (PropertyInfo property in properties) 
    { 
     object propValue = property.GetValue(obj, null); 
     if (IsSimpleType(property.PropertyType)) 
     { 
      Console.WriteLine("{0}{1}: {2}", indentString, property.Name, propValue); 
     } 
     else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType)) 
     { 
      if (property.PropertyType == typeof(string[])) 
      { 
       Console.WriteLine("{0}{1}: {2}", indentString, property.Name, string.Join(",", (string[])propValue)); 
      } 
      else 
      { 
       Console.WriteLine("{0}{1}:", indentString, property.Name); 
       IEnumerable enumerable = (IEnumerable)propValue; 
       foreach (object child in enumerable) 
       { 
        PrintProperties(child, indent + 2); 
       } 
      } 
     } 
     else 
     { 
      Console.WriteLine("{0}{1}:", indentString, property.Name); 
      PrintProperties(propValue, indent + 2); 
     } 
    } 
} 

public static bool IsSimpleType(Type type) 
{ 
    return 
     type.IsValueType || 
     type.IsPrimitive || 
     new Type[] 
     { 
      typeof(String), 
      typeof(Decimal), 
      typeof(DateTime), 
      typeof(DateTimeOffset), 
      typeof(TimeSpan), 
      typeof(Guid) 
     }.Contains(type) || 
     Convert.GetTypeCode(type) != TypeCode.Object; 
}