2011-11-11 9 views
5

Sto tentando di creare una routine ricorsiva che recuperi PropertyInfos per tutti i membri in un oggetto specificato (in .NET 3.5). Tutto per i membri immediati funziona, ma deve anche analizzare le classi annidate (e le loro classi annidate, ecc.).Procedura ricorsiva per ottenere PropertyInfo

Non capisco come gestire la sezione che analizza le classi annidate. Come scriveresti questa parte del codice?

public class ObjectWalkerEntity 
{ 
    public object Value { get; set; } 
    public PropertyInfo PropertyInfo { get; set; } 
} 


public static class ObjectWalker 
{ 
    // This will be the returned object 
    static List<ObjectWalkerEntity> objectList = new List<ObjectWalkerEntity>(); 

    public static List<ObjectWalkerEntity> Walk(object o) 
    { 
     objectList.Clear(); 
     processObject(o); 
     return objectList; 
    } 

    private static void processObject(object o) 
    { 
     if (o == null) 
     { 
      return; 
     } 

     Type t = o.GetType(); 

     foreach (PropertyInfo pi in t.GetProperties()) 
     { 
      if (isGeneric(pi.PropertyType)) 
      { 
       // Add generic object 
       ObjectWalkerEntity obj = new ObjectWalkerEntity(); 
       obj.PropertyInfo = pi; 
       obj.Value = pi.GetValue(o, null); 
       objectList.Add(obj); 
      } 
      else 
      { 
       ////// TODO: Find a way to parse the members of the subclass... 
       // Parse each member of the non-generic object 
       foreach (Object item in pi.PropertyType) 
       { 
        processObject(item); 
       } 
      } 
     } 

     return; 
    } 

    private static bool isGeneric(Type type) 
    { 
     return 
      Extensions.IsSubclassOfRawGeneric(type, typeof(bool)) || 
      Extensions.IsSubclassOfRawGeneric(type, typeof(string)) || 
      Extensions.IsSubclassOfRawGeneric(type, typeof(int)) || 
      Extensions.IsSubclassOfRawGeneric(type, typeof(UInt16)) || 
      Extensions.IsSubclassOfRawGeneric(type, typeof(UInt32)) || 
      Extensions.IsSubclassOfRawGeneric(type, typeof(UInt64)) || 
      Extensions.IsSubclassOfRawGeneric(type, typeof(DateTime)); 
    } 

Edit: Ho usato alcuni dei suggerimenti del Harlam, e si avvicinò con una soluzione di lavoro. Questo gestisce sia le classi nidificate che gli elenchi.

ho sostituito il mio ciclo precedente attraverso il PropertyInfo con il seguente

foreach (PropertyInfo pi in t.GetProperties()) 
{ 
    if (isGeneric(pi.PropertyType)) 
    { 
     // Add generic object 
     ObjectWalkerEntity obj = new ObjectWalkerEntity(); 
     obj.PropertyInfo = pi; 
     obj.Value = pi.GetValue(o, null); 
     objectList.Add(obj); 
    } 
    else if (isList(pi.PropertyType)) 
    { 
     // Parse the list 
     var list = (IList)pi.GetValue(o, null); 
     foreach (object item in list) 
     { 
      processObject(item); 
     } 
    } 
    else 
    { 
     // Parse each member of the non-generic object 
     object value = pi.GetValue(o, null); 
     processObject(value); 
    } 
} 

Ho anche aggiunto un nuovo controllo per vedere se qualcosa è una lista.

private static bool isList(Type type) 
{ 
    return 
     IsSubclassOfRawGeneric(type, typeof(List<>)); 
} 

Grazie per l'aiuto!

risposta

5

Penso che questo funzionerà per voi. L'idea qui è di restituire un oggetto enumerabile da ogni chiamata a ProcessObject() e quindi spostare quelle chiamate nei chiamanti List<ObjectWalkerEntity>.

public class ObjectWalkerEntity 
{ 
    public object Value { get; set; } 
    public PropertyInfo PropertyInfo { get; set; } 
} 

public static class ObjectWalker 
{ 
    public static List<ObjectWalkerEntity> Walk(object o) 
    { 
     return ProcessObject(o).ToList(); 
    } 

    private static IEnumerable<ObjectWalkerEntity> ProcessObject(object o) 
    { 
     if (o == null) 
     { 
     // nothing here, just return an empty enumerable object 
     return new ObjectWalkerEntity[0]; 
     } 

     // create the list to hold values found in this object 
     var objectList = new List<ObjectWalkerEntity>(); 

     Type t = o.GetType(); 
     foreach (PropertyInfo pi in t.GetProperties()) 
     { 
     if (IsGeneric(pi.PropertyType)) 
     { 
      // Add generic object 
      var obj = new ObjectWalkerEntity(); 
      obj.PropertyInfo = pi; 
      obj.Value = pi.GetValue(o, null); 
      objectList.Add(obj); 
     } 
     else 
     { 
      // not generic, get the property value and make the recursive call 
      object value = pi.GetValue(o, null); 
      // all values returned from the recursive call get 
      // rolled up into the list created in this call. 
      objectList.AddRange(ProcessObject(value)); 
     } 
     } 

     return objectList.AsReadOnly(); 
    } 

    private static bool IsGeneric(Type type) 
    { 
     return 
      IsSubclassOfRawGeneric(type, typeof(bool)) || 
      IsSubclassOfRawGeneric(type, typeof(string)) || 
      IsSubclassOfRawGeneric(type, typeof(int)) || 
      IsSubclassOfRawGeneric(type, typeof(UInt16)) || 
      IsSubclassOfRawGeneric(type, typeof(UInt32)) || 
      IsSubclassOfRawGeneric(type, typeof(UInt64)) || 
      IsSubclassOfRawGeneric(type, typeof(DateTime)); 
    } 

    private static bool IsSubclassOfRawGeneric(Type generic, Type toCheck) 
    { 
     while (toCheck != typeof(object)) 
     { 
     var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck; 
     if (generic == cur) 
     { 
      return true; 
     } 
     toCheck = toCheck.BaseType; 
     } 
     return false; 
    } 
} 
+0

È possibile utilizzare il rendimento restituito e inoltre dovrebbe contenere un elenco di oggetti già elaborati per le proprietà di riferimento posteriori. – riezebosch

+0

Funziona quasi, ma sta incontrando un problema con le liste. – Rethic

0

Dai uno sguardo allo Devscribe. È open source e utilizza Reflection per iterare ogni tipo di riflessione, inclusa la gestione dei generici.

Problemi correlati