2010-11-19 7 views
11

OK, więc na początku myślałem, że to dość łatwe, a może jest i jestem po prostu zbyt zmęczony - ale oto, co próbuję zrobić. Że mam następujące obiekty:Rekursywnie uzyskiwanie właściwości i właściwości dziecka obiektu

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

Co muszę być w stanie zrobić, to „spłaszczyć” nazwy właściwości pojemników w celu sznurku (w tym wszystkie właściwości dziecka i właściwości podrzędnych właściwości podrzędnych), które mogłyby wyglądać tak:

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

Czy to ma jakiś sens? Nie mogę owinąć go wokół mojej głowy.

+0

Musisz jasno określić, w jaki sposób określisz, co jest własnością dziecka. Tutaj zakładasz, że typ listy zostanie spłaszczony jako typ T. Co jeśli istnieje właściwość public Telephone Number {get; zestaw; } (zamiast listy )? Czy byłoby inaczej? Czy twoje właściwości zawsze będą albo typami pierwotnymi, albo Listą, gdzie T jest typem złożonym? – mellamokb

+0

możliwy duplikat [zagnieżdżonych klas i rekurencji] (http://stackoverflow.com/questions/811098/nested-classes-and-recytion) – nawfal

Odpowiedz

12

Proponuję, aby zaznaczyć wszystkie klasy, trzeba chwycić, z atrybutu niestandardowego po tym można zrobić coś takiego

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

Dziękuję! Pracowałem po raz pierwszy – Wayne

+0

@ The_Smallest: Hello .. Potrzebuję czegoś podobnego do tego. Ale mam atrybut niestandardowy dodany do właściwości. Potrzebuję uzyskać listę wszystkich właściwości, które mają atrybut niestandardowy. Nie chcę używać żadnego atrybutu na poziomie obiektu (nazwa wyciągu). Próbuję użyć kodu ur i wprowadzam pewne modyfikacje. Ale bez powodzenia. Proszę pomóż. – Shetty

+0

@The_Smallest: Hello .. Potrzebuję czegoś podobnego do tego. Ale mam atrybut niestandardowy dodany do właściwości. Potrzebuję uzyskać listę wszystkich właściwości, które mają atrybut niestandardowy. Ponieważ typy są zagnieżdżone, chcę również uzyskać właściwości we wszystkich typach wewnętrznych. Próbuję użyć kodu ur i wprowadzam pewne modyfikacje. Ale bez powodzenia. Proszę pomóż. – Shetty

0

Per mojego komentarza, można użyć coś takiego, jeżeli to będzie zawsze być typowym typem listy, który chcesz połączyć z typem podrzędnym. IteratePropertiesRecursive to iterator nad właściwościami danego typu, który rekursywnie wyliczy właściwości typu i wszystkich typów potomnych powiązanych za pomocą ogólnej listy.

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

Uwaga: Kod ten nie będzie prawidłowo obsługiwać typ, który zawiera się rekurencyjnie jako rodzaj jednej z jego właściwości. Próba iteracji tego typu spowoduje, że StackOverflowException, jak wskazano przez @Dementic (dzięki!).

+0

powoduje to przepływ StackOverflow. – Dementic

+0

@Dementic: Próbowałem go ponownie używając oryginalnych klas OP. Działa dobrze w LINQPad i wypisuje 'Container.Name, Container.Address.AddressLine1, Container.Address.AddressLine2, Container.Address.Telephone.CellPhone'. Czy możesz wyjaśnić, skąd bierzesz SO? Czy testowałeś go na typie klasy, który rekurencyjnie się odwołuje? Jeśli tak, byłby to jeden przypadek użycia, w którym ten kod nie powinien być używany. – mellamokb

+0

Tak, klasa, którą wypróbowałem, dostałem SO, powinieneś naprawić swój kod, aby odwiedzający go nie dostali (czyli pokryć wszystkie bazy). – Dementic