2010-11-19 27 views
11

Ok, donc au début, je pensais que c'était assez facile, et peut-être qu'il est et je suis trop fatigué - mais voici ce que je suis en train de faire. Dire que j'ai les objets suivants:Récursivement Obtenir les propriétés et les propriétés des enfants d'un objet

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

Ce que je dois pouvoir le faire, est « plaquer » les noms de propriétés conteneurs dans une chaîne (y compris toutes les propriétés de l'enfant et propriétés enfant de propriétés enfant) qui ressemblerait à quelque chose comme ceci:

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

Est-ce que cela a un sens? Je n'arrive pas à l'enrouler autour de ma tête.

+0

Vous devez être clair sur la façon dont vous allez déterminer ce qu'est une propriété enfant. Ici, vous supposez que le type Liste sera aplati en tant que type T. Et s'il y avait un numéro de téléphone public de la propriété {get; ensemble; } (au lieu d'une liste )? Cela serait-il traité différemment? Vos propriétés seront-elles toujours soit des types primitifs, soit une liste où T est un type complexe? – mellamokb

+0

duplication possible de [Classes imbriquées et récursion] (http://stackoverflow.com/questions/811098/nested-classes-and-recursion) – nawfal

Répondre

12

Je vous suggère de marquer toutes les classes, vous devez saisir, avec l'attribut personnalisé après que vous pourriez faire quelque chose comme ça

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

Merci! A travaillé la première fois – Wayne

+0

@The_Smallest: Bonjour .. J'ai besoin de quelque chose de similaire à cela. Mais j'ai l'attribut personnalisé ajouté aux propriétés. J'ai besoin d'obtenir la liste de toutes les propriétés qui ont un attribut personnalisé. Je ne veux utiliser aucun attribut au niveau de l'objet (Extract Name). J'essaie d'utiliser votre code ur et de faire quelques modifications. Mais pas de succès. S'il vous plaît aider. – Shetty

+0

@The_Smallest: Bonjour .. J'ai besoin de quelque chose de similaire à cela. Mais j'ai l'attribut personnalisé ajouté aux propriétés. J'ai besoin d'obtenir la liste de toutes les propriétés qui ont un attribut personnalisé. Puisque les types sont imbriqués, je veux aussi obtenir les propriétés dans tous les types internes. J'essaie d'utiliser votre code ur et de faire quelques modifications. Mais pas de succès. S'il vous plaît aider. – Shetty

0

Par mon commentaire, vous pouvez utiliser quelque chose comme ça si elle sera toujours être un type Liste générique que vous voulez lier à un type enfant. IteratePropertiesRecursively est un itérateur sur les propriétés du type donné, qui énumérera récursivement les propriétés du type et de tous les types enfants liés via une liste générique.

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

Note: Ce code ne sera pas gérer correctement un type qui se comprend récursive comme un type de l'une de ses propriétés. Essayer d'itérer sur un tel type se traduira par un StackOverflowException, comme l'a souligné @Dementic (merci!).

+0

cela lance un StackOverflow. – Dementic

+0

@Dementic: Je viens de l'essayer à nouveau en utilisant les classes d'origine de l'OP. Fonctionne bien dans LINQPad, et renvoie 'Container.Name, Container.Address.AddressLine1, Container.Address.AddressLine2, Container.Address.Telephone.CellPhone'. Pouvez-vous expliquer où vous obtenez le SO? L'avez-vous testé sur un type de classe qui se réfère récursivement à lui-même? Si c'est le cas, ce serait un cas d'utilisation où ce code ne devrait pas être utilisé. – mellamokb

+0

oui, la classe j'ai essayé, a obtenu un SO, vous devez corriger votre code afin que les visiteurs ne reçoivent pas cela (c'est-à-dire, couvrir toutes les bases) – Dementic