2013-12-12 14 views
15

Stavo facendo qualcosa come Recursively Get Properties & Child Properties Of An Object, ma volevo usare la riflessione in modo ricorsivo per ottenere ciascuna proprietà. E ho ricevuto il codice da Recursively Print the properties.Acquisisci in modo ricorsivo proprietà e proprietà figlio di una classe

Il problema con il codice è: scende solo di un livello, mi chiedo come si possono ottenere automaticamente tutte le proprietà utilizzando il riflesso? Ho appena fatto il seguente codice di esempio Contenitore:

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; } 
} 

Nel metodo 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); 
     } 
    } 
} 

Sto sperando di ottenere:

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

Il risultato che ottengo ora:

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

Qualcuno può aiutarmi h il metodo PrintProperties? Grazie mille.

risposta

28

avete due problemi con il tuo codice:

  1. a causa delle condizioni if (property.PropertyType.Assembly == objType.Assembly) si omettere System.Collections come List<>
  2. non trattare in modo diverso propValue che sono collezioni. Quindi stamperà le proprietà List, non le proprietà degli elementi.

È possibile cambiare la situazione, ad esempio in:

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

Esattamente quello che stavo cercando, non solo ha funzionato per questo esempio, ma per tutto l'altro caso ho avuto – HoKy22

+0

Questo è stato utile. Ho fatto una piccola aggiunta per assicurarmi che la proprietà possa essere letta prima di chiamare GetValue per prevenire un'eccezione su una proprietà write only - l'ho appena racchiusa con "if (property.CanRead) {...". – thephez

+0

Nice little tidbit of C# 6 sugar sintattico: 'if (propValue is IList elems)' funziona e salva una riga. – CaptainMarvel

7

si desidera gestire tipi primitivi e stringhe a parte, e un ciclo su enumerables invece di solo prendendo il loro valore() ToString. Quindi il tuo codice potrebbe essere aggiornato a:

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

questo codice funziona molto bene per questo esempio, quando ho aggiunto alcune più proprietà e classi, Ho un ciclo infinito. Stavo cercando un modo generico per ottenere tutte le proprietà. – HoKy22

+0

Dopo alcune piccole modifiche questo codice ha funzionato per me. – dee

1

Funziona per tutti i casi tranne propValue è stringa []. Otterrete l'eccezione "Parameter Count Mismatch" in linea: oggetto propValue = property.GetValue (obj, null);

Per risolvere questo problema è possibile utilizzare questo codice con un po 'di correzione:

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

ho cambiato il codice muggito delle driis. Per me funziona.

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; 
} 
Problemi correlati