2010-11-19 307 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

你必须清楚你将如何确定什么是儿童财产。在这里,你假设列表类型将被扁平化为类型T.如果有一个属性公共电话号码{get;组; }(而不是列表)?这会被区别对待吗?你的属性总是或者是原始类型或者列表其中T是一个复杂类型? – mellamokb 2010-11-19 01:22:33

+0

[嵌套类和递归]的可能重复(http://stackoverflow.com/questions/811098/nested-classes-and-recursion) – nawfal 2013-04-25 09:12:47

回答

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 2010-11-19 15:16:00

+0

@The_Smallest:你好..我需要类似的东西。但我已将自定义属性添加到属性。我需要获取具有自定义属性的所有属性的列表。我不想在对象级别使用任何属性(Extract Name)。我正在尝试使用我们的代码并进行一些修改,但没有成功。请帮忙。 – Shetty 2013-06-26 14:11:22

+0

@The_Smallest:你好..我需要类似这样的东西。但我已将自定义属性添加到属性。我需要获取具有自定义属性的所有属性的列表。由于类型是嵌套的,我想获得所有内部类型的属性。我正在尝试使用我们的代码并进行一些修改,但没有成功。请帮忙。 – Shetty 2013-06-26 14:40:24

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

注:此代码将不能正确地处理一个类型的递归包括本身作为一个类型的属性之一。正如@Dementic指出的那样,尝试遍历这种类型将导致StackOverflowException(谢谢!)。

+0

这将引发一个StackOverflow。 – Dementic 2012-11-15 15:21:09

+0

@Dementic:我刚刚使用OP的原始类尝试过。在LINQPad中工作正常,并输出'Container.Name,Container.Address.AddressLine1,Container.Address.AddressLine2,Container.Address.Telephone.CellPhone'。你能解释你在哪里得到SO吗?你是否在递归引用自己的类类型上测试它?如果是这样,那将是不应使用此代码的一种用例。 – mellamokb 2012-11-15 18:01:11

+0

是的,我已经尝试过的类,得到了一个SO,你应该修复你的代码,所以功能访问者不会得到(即覆盖所有基地) – Dementic 2012-11-15 20:38:12