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