2010-11-19 24 views
11

Ok, así que al principio pensé que esto era bastante fácil, y tal vez lo es y estoy demasiado cansado, pero esto es lo que estoy tratando de hacer. Decir que tengo los siguientes objetos:Obtiene recursivamente propiedades y propiedades secundarias de un objeto

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

Lo que necesito para ser capaz de hacer, es 'aplanar' nombres de propiedades Contenedores en una cadena (incluyendo todas las propiedades secundarias y propiedades niño de propiedades secundarias) que sería algo de esta manera:

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

¿Tiene sentido? Parece que no puedo envolverlo en mi cabeza.

+0

Tienes que tener en claro cómo determinarás qué es una propiedad infantil. Aquí está asumiendo que el tipo List se aplanará como tipo T. Qué pasa si hay una propiedad pública Telephone Number {get; conjunto; } (en lugar de una lista )? ¿Sería eso manejado de manera diferente? ¿Sus propiedades serán siempre tipos primitivos o List donde T es un tipo complejo? – mellamokb

+0

posible duplicado de [Clases anidadas y recursión] (http://stackoverflow.com/questions/811098/nested-classes-and-recursion) – nawfal

Respuesta

12

sugiero que marque todas las clases, tiene que agarrar, con el atributo personalizado después de que se podía hacer algo como esto

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

¡Gracias! Trabajó por primera vez – Wayne

+0

@The_Smallest: Hola ... Necesito algo similar a esto. Pero tengo el atributo personalizado agregado a las propiedades. Necesito obtener una lista de todas las propiedades que tienen un atributo personalizado. No quiero usar ningún atributo en el nivel del objeto (Extraer nombre). Estoy tratando de usar tu código ur y hacer algunas modificaciones. Pero no hay éxito. Por favor ayuda. – Shetty

+0

@The_Smallest: Hola ... Necesito algo similar a esto. Pero tengo el atributo personalizado agregado a las propiedades. Necesito obtener una lista de todas las propiedades que tienen un atributo personalizado. Como los tipos están anidados, también quiero obtener las propiedades en todos los tipos internos. Estoy tratando de usar tu código ur y hacer algunas modificaciones. Pero no hay éxito. Por favor ayuda. – Shetty

0

por mi comentario, podría utilizar algo como esto si siempre lo hará ser un tipo de Lista genérico que quiera vincular a un tipo de niño. IteratePropertiesRecursively es un iterador sobre las propiedades del tipo dado, que enumerará recursivamente las propiedades del tipo y todos los tipos secundarios vinculados a través de una lista genérica.

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: Este código no se controla correctamente un tipo que incluye de forma recursiva a sí mismo como un tipo de una de sus propiedades. Intentar iterar sobre dicho tipo dará como resultado un StackOverflowException, como lo señala @Dementic (¡gracias!).

+0

esto arroja un StackOverflow. – Dementic

+0

@Dementic: lo intenté de nuevo usando las clases originales de OP. Funciona bien en LINQPad y genera 'Container.Name, Container.Address.AddressLine1, Container.Address.AddressLine2, Container.Address.Telephone.CellPhone'. ¿Puede explicar dónde obtiene el SO? ¿Lo estaba probando en un tipo de clase que hace referencia recursiva a sí mismo? De ser así, ese sería un caso de uso en el que no se debería usar este código. – mellamokb

+0

sí, la clase que he intentado, obtuve un SO, debe corregir su código para que los visitantes de la función no lo reciban (es decir, cubrir todas las bases) – Dementic

Cuestiones relacionadas