2010-11-19 13 views
11

Ok così all'inizio ho pensato che fosse abbastanza semplice, e forse lo è e sono solo troppo stanco - ma ecco cosa sto cercando di fare. Dire che ho i seguenti oggetti:Ottieni ricorsivamente proprietà e proprietà figlio di un oggetto

public class Container 
{ 
    public string Name { get; set; } 
    public List<Address> Addresses { get; set; } 
} 
public class Address 
{ 
    public string AddressLine1 { get; set; } 
    public string AddressLine2 { get; set; } 
    public List<Telephone> Telephones { get; set; } 
} 
public class Telephone 
{ 
    public string CellPhone { get; set; } 
} 

Che cosa ho bisogno di essere in grado di fare, è 'appiattire' nomi di proprietà contenitori in una stringa (tra cui tutte le proprietà bambino e proprietà secondarie proprietà secondarie), che apparirebbe qualcosa così:

Container.Name, Container.Addresses.AddressLine1, Container.Addresses.AddressLine2, Container.Addresses.Telephones.CellPhone 

Ha senso? Non riesco a avvolgerlo in testa.

+0

Devi essere chiaro su come determinerai cos'è una proprietà figlio. Qui si presuppone che il tipo List verrà appiattito come tipo T. Cosa accadrebbe se ci fosse una proprietà pubblica Numero di telefono {get; impostato; } (invece di un elenco )? Sarebbe gestito diversamente? Le tue proprietà saranno sempre di tipo primitivo o Elenco dove T è un tipo complesso? – mellamokb

+0

possibile duplicato di [Classi e ricorsi annidati] (http://stackoverflow.com/questions/811098/nested-classes-and-recursion) – nawfal

risposta

12

vi consiglio di contrassegnare tutte le classi, è necessario afferrare, con l'attributo personalizzato dopo che si potrebbe fare qualcosa di simile

class Program 
{ 
    static void Main(string[] args) 
    { 
     var lines = ExtractHelper.IterateProps(typeof(Container)).ToArray(); 

     foreach (var line in lines) 
      Console.WriteLine(line); 

     Console.ReadLine(); 
    } 
} 

static class ExtractHelper 
{ 

    public static IEnumerable<string> IterateProps(Type baseType) 
    { 
     return IteratePropsInner(baseType, baseType.Name); 
    } 

    private static IEnumerable<string> IteratePropsInner(Type baseType, string baseName) 
    { 
     var props = baseType.GetProperties(); 

     foreach (var property in props) 
     { 
      var name = property.Name; 
      var type = ListArgumentOrSelf(property.PropertyType); 
      if (IsMarked(type)) 
       foreach (var info in IteratePropsInner(type, name)) 
        yield return string.Format("{0}.{1}", baseName, info); 
      else 
       yield return string.Format("{0}.{1}", baseName, property.Name); 
     } 
    } 

    static bool IsMarked(Type type) 
    { 
     return type.GetCustomAttributes(typeof(ExtractNameAttribute), true).Any(); 
    } 


    public static Type ListArgumentOrSelf(Type type) 
    { 
     if (!type.IsGenericType) 
      return type; 
     if (type.GetGenericTypeDefinition() != typeof(List<>)) 
      throw new Exception("Only List<T> are allowed"); 
     return type.GetGenericArguments()[0]; 
    } 
} 

[ExtractName] 
public class Container 
{ 
    public string Name { get; set; } 
    public List<Address> Addresses { get; set; } 
} 

[ExtractName] 
public class Address 
{ 
    public string AddressLine1 { get; set; } 
    public string AddressLine2 { get; set; } 
    public List<Telephone> Telephones { get; set; } 
} 

[ExtractName] 
public class Telephone 
{ 
    public string CellPhone { get; set; } 
} 

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = true, AllowMultiple = true)] 
public sealed class ExtractNameAttribute : Attribute 
{ } 
+0

Grazie! Ha funzionato la prima volta – Wayne

+0

@The_Smallest: Ciao .. Ho bisogno di qualcosa di simile a questo. Ma ho aggiunto l'attributo personalizzato alle proprietà. Devo ottenere l'elenco di tutte le proprietà con attributo personalizzato. Non voglio utilizzare alcun attributo a livello di oggetto (Nome estratto). Sto cercando di utilizzare il codice ur e apportare alcune modifiche. Ma non ha successo. Per favore aiuto. – Shetty

+0

@The_Smallest: Ciao .. Ho bisogno di qualcosa di simile a questo. Ma ho aggiunto l'attributo personalizzato alle proprietà. Devo ottenere l'elenco di tutte le proprietà con attributo personalizzato. Poiché i tipi sono nidificati, voglio ottenere le proprietà anche in tutti i tipi interni. Sto cercando di utilizzare il codice ur e apportare alcune modifiche. Ma non ha successo. Per favore aiuto. – Shetty

0

Per il mio commento, si potrebbe usare qualcosa di simile, se lo farà sempre essere un tipo di elenco generico che si desidera collegare a un tipo di figlio. IteratePropertiesRecursively è un iteratore sulle proprietà del tipo specificato, che enumera ricorsivamente le proprietà del tipo e tutti i tipi di figlio collegati tramite un Elenco generico.

protected void Test() 
{ 
    Type t = typeof(Container); 
    string propertyList = string.Join(",", IteratePropertiesRecursively("", t).ToArray<string>()); 
    // do something with propertyList 
} 

protected IEnumerable<string> IteratePropertiesRecursively(string prefix, Type t) 
{ 
    if (!string.IsNullOrEmpty(prefix) && !prefix.EndsWith(".")) prefix += "."; 
    prefix += t.Name + "."; 

    // enumerate the properties of the type 
    foreach (PropertyInfo p in t.GetProperties()) 
    { 
     Type pt = p.PropertyType; 

     // if property is a generic list 
     if (pt.Name == "List`1") 
     { 
      Type genericType = pt.GetGenericArguments()[0]; 
      // then enumerate the generic subtype 
      foreach (string propertyName in IteratePropertiesRecursively(prefix, genericType)) 
      { 
       yield return propertyName; 
      } 
     } 
     else 
     { 
      // otherwise enumerate the property prepended with the prefix 
      yield return prefix + p.Name; 
     } 
    } 
} 

Nota: Questo codice non gestire correttamente un tipo che si include ricorsivamente come un tipo di una delle sue proprietà. Cercando di ripetere questo tipo si otterrà un StackOverflowException, come sottolineato da @Dementic (grazie!).

+0

questo genera uno StackOverflow. – Dementic

+0

@Dementic: l'ho appena provato di nuovo usando le classi originali dell'OP. Funziona perfettamente con LINQPad e restituisce 'Container.Name, Container.Address.AddressLine1, Container.Address.AddressLine2, Container.Address.Telephone.CellPhone'. Puoi spiegare dove stai ottenendo la SO? Lo stavi testando su un tipo di classe che si riferiva ricorsivamente a se stesso? Se è così, quello sarebbe un caso d'uso in cui questo codice non dovrebbe essere usato. – mellamokb

+0

sì, la classe che ho provato, ha ottenuto un SO, dovresti correggere il tuo codice in modo che i visitatori delle caratteristiche non lo ottengano (cioè, coprono tutte le basi) – Dementic

Problemi correlati