2010-11-19 3 views
11

Итак, сначала я подумал, что это достаточно легко, и, возможно, это так, и я просто слишком устал, но вот что я пытаюсь сделать. Скажем, у меня есть следующие объекты:Рекурсивно Получить свойства и детские свойства объекта

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

Что мне нужно, чтобы быть в состоянии сделать, это «придавить» Контейнеры имен свойств и строки (включая все дочерние свойства и ребенок свойство дочерних объектов), которые будут выглядеть например:

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

В этом смысл? Кажется, я не могу обернуть его вокруг головы.

+0

Вы должны четко понимать, как вы определяете, что такое дочерняя собственность. Здесь вы предполагаете, что тип List будет сплющен как тип T. Что, если бы существовал общественный номер телефона {get; задавать; } (вместо списка )? Будет ли это по-другому? Будут ли ваши свойства всегда быть либо примитивными типами, либо List , где T - сложный тип? – mellamokb

+0

Возможный дубликат [Вложенные классы и рекурсия] (http://stackoverflow.com/questions/811098/nested-classes-and-recursion) – nawfal

ответ

12

Я предлагаю вам помечать все классы, вам нужно захватить с пользовательским атрибутом после этого вы могли бы сделать что-то вроде этого

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

Спасибо! Работал первый раз – Wayne

+0

@The_Smallest: Привет .. Мне нужно что-то похожее на это. Но у меня есть атрибут custom, добавленный к свойствам. Мне нужно получить список всех свойств, которые имеют пользовательский атрибут. Я не хочу использовать какой-либо атрибут на уровне объекта (Extract Name). Я пытаюсь использовать код ur и внести некоторые изменения. Но никакого успеха. Пожалуйста помоги. – Shetty

+0

@The_Smallest: Привет .. Мне нужно что-то похожее на это. Но у меня есть атрибут custom, добавленный к свойствам. Мне нужно получить список всех свойств, которые имеют пользовательский атрибут. Поскольку типы вложены, я хочу получить свойства во всех внутренних типах. Я пытаюсь использовать код ur и внести некоторые изменения. Но никакого успеха. Пожалуйста помоги. – Shetty

0

За мой комментарий, вы могли бы использовать что-то вроде этого, если он будет всегда быть общим типом списка, который вы хотите связать с дочерним типом. IteratePropertiesRecursively является итератором по свойствам данного типа, который будет рекурсивно перечислять свойства типа и всех дочерних типов, связанных через общий список.

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

Примечание: Этот код не будет правильно обрабатывать тип, который рекурсивно включает в себя как тип одного из его свойств. Попытка итерации по такому типу приведет к StackOverflowException, как отметил @Dementic (спасибо!).

+0

это вызывает StackOverflow. – Dementic

+0

@Dementic: Я просто попробовал это снова, используя оригинальные классы OP. Работает нормально в LINQPad и выводит 'Container.Name, Container.Address.AddressLine1, Container.Address.AddressLine2, Container.Address.Telephone.CellPhone'. Можете ли вы объяснить, где вы получаете SO? Вы тестировали его по типу класса, который рекурсивно ссылается на себя? Если это так, это будет один случай использования, когда этот код не должен использоваться. – mellamokb

+0

Да, класс, который я пробовал, получил SO, вы должны исправить свой код, так что посетители не получат его (то есть, охватывают все базы) – Dementic

Смежные вопросы