2010-11-19 46 views
11

Ok, so dachte ich zuerst, das war einfach genug, und vielleicht ist es, und ich bin einfach zu müde - aber hier ist, was ich versuche zu tun. Sagen, dass ich die folgenden Objekte:Rekursiv Eigenschaften und untergeordnete Eigenschaften eines Objekts

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

Was ich in der Lage sein müssen, ist ‚flach‘ Container Eigenschaftsnamen in einen String (einschließlich aller Kind Eigenschaften und Kind Eigenschaften der Kinder Eigenschaften), die etwas aussehen würde wie folgt:

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

Macht das irgendeinen Sinn? Ich kann es nicht um meinen Kopf wickeln.

+0

Sie müssen klar darüber sein, wie Sie bestimmen, was eine Kind-Eigenschaft ist. Hier gehen Sie davon aus, dass die Liste als Typ T abgeflacht wird. Was wäre, wenn es eine Eigenschaft öffentliche Telefonnummer {get; einstellen; } (anstelle einer Liste )? Würde das anders gehandhabt? Sind Ihre Eigenschaften immer entweder primitive Typen oder Liste wo T ein komplexer Typ ist? – mellamokb

+0

mögliche Duplikate von [Verschachtelte Klassen und Rekursion] (http://stackoverflow.com/questions/811098/nested-classes-and-recursion) – nawfal

Antwort

12

Ich schlage vor, Sie alle Klassen zu markieren, müssen Sie greifen, mit benutzerdefinierten Attribut nach, dass Sie so etwas wie dieses

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

Vielen Dank! Arbeitete erstmals – Wayne

+0

@The_Smallest: Hallo .. Ich brauche etwas ähnliches dazu. Aber ich habe das benutzerdefinierte Attribut zu den Eigenschaften hinzugefügt. Ich brauche eine Liste aller Eigenschaften, die ein eigenes Attribut haben. Ich möchte kein Attribut auf Objektebene (Name extrahieren) verwenden. Ich versuche, ur Code zu verwenden und einige Änderungen vorzunehmen. Aber kein Erfolg. Bitte helfen Sie. – Shetty

+0

@The_Smallest: Hallo .. Ich brauche etwas ähnliches. Aber ich habe das benutzerdefinierte Attribut zu den Eigenschaften hinzugefügt. Ich brauche eine Liste aller Eigenschaften, die ein eigenes Attribut haben. Da Typen verschachtelt sind, möchte ich die Eigenschaften auch in allen inneren Typen erhalten. Ich versuche, ur Code zu verwenden und einige Änderungen vorzunehmen. Aber kein Erfolg. Bitte helfen Sie. – Shetty

0

Per mein Kommentar tun, könnte man so etwas wie dieses, wenn es wird immer verwenden könnte ein generischer Listentyp sein, den Sie mit einem Kindtyp verknüpfen möchten. IteratePropertiesRecursive ist ein Iterator für die Eigenschaften des angegebenen Typs, der die Eigenschaften des Typs und aller untergeordneten Typen, die über eine generische Liste verknüpft sind, rekursiv aufzählt.

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

Hinweis: Dieser Code wird nicht korrekt eine Art behandeln, die sich selbst rekursiv als eine Art eine seiner Eigenschaften enthält. Der Versuch, über einen solchen Typ zu iterieren, führt zu einem StackOverflowException, wie von @Dementic (thanks!) Hervorgehoben.

+0

löst dies einen StackOverflow. – Dementic

+0

@Dementic: Ich habe es gerade wieder mit den ursprünglichen Klassen des OP versucht. Funktioniert gut in LINQPad und gibt 'Container.Name, Container.Address.AddressLine1, Container.Address.AddressLine2, Container.Address.Telephone.CellPhone' aus. Können Sie erklären, wo Sie die SO bekommen? Haben Sie es an einem Klassentyp getestet, der sich rekursiv referenziert? Wenn dies der Fall wäre, wäre dies ein Anwendungsfall, in dem dieser Code nicht verwendet werden sollte. – mellamokb

+0

ja, die Klasse, die ich versucht habe, bekam eine SO, sollten Sie Ihren Code so Feature Besucher nicht bekommen, dass (das heißt, decken alle Basen) – Dementic

Verwandte Themen