C# 递归地获取属性&;对象的子属性

C# 递归地获取属性&;对象的子属性,c#,reflection,collections,C#,Reflection,Collections,好的,一开始我觉得这很容易,也许是这样,我太累了,但我想做的就是这样。假设我有以下对象: public class Container { public string Name { get; set; } public List<Address> Addresses { get; set; } } public class Address { public string AddressLine1 { get; set; } public str

好的,一开始我觉得这很容易,也许是这样,我太累了,但我想做的就是这样。假设我有以下对象:

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

这有什么意义吗?我似乎不能把它放在我的头上。

我建议你标记所有的类,你需要抓取,使用自定义属性,然后你可以这样做

 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
{ }
类程序
{
静态void Main(字符串[]参数)
{
var lines=ExtractHelper.iterateOps(typeof(Container)).ToArray();
foreach(行中的var行)
控制台写入线(行);
Console.ReadLine();
}
}
静态类提取器助手
{
公共静态IEnumerable迭代器操作(类型baseType)
{
返回IteratePropsInner(baseType,baseType.Name);
}
私有静态IEnumerable IteratePropsInner(类型baseType,字符串baseName)
{
var props=baseType.GetProperties();
foreach(props中的var属性)
{
var name=property.name;
var type=ListArgumentOrSelf(property.PropertyType);
如果(标记为(类型))
foreach(IteratePropsInner中的var信息(类型、名称))
返回string.Format(“{0}.{1}”,baseName,info);
其他的
返回string.Format(“{0}.{1}”,baseName,property.Name);
}
}
静态布尔标记(类型)
{
返回type.GetCustomAttributes(typeof(ExtractNameAttribute),true).Any();
}
公共静态类型ListArgumentOrSelf(类型)
{
如果(!type.IsGenericType)
返回类型;
if(type.GetGenericTypeDefinition()!=typeof(列表))
抛出新异常(“仅允许列表”);
返回类型。GetGenericArguments()[0];
}
}
[姓名]
公营货柜
{
公共字符串名称{get;set;}
公共列表地址{get;set;}
}
[姓名]
公共课堂演讲
{
公共字符串AddressLine1{get;set;}
公共字符串AddressLine2{get;set;}
公用电话列表{get;set;}
}
[姓名]
公共电话
{
公共字符串{get;set;}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct,Inherited=true,AllowMultiple=true)]
公共密封类ExtractNameAttribute:属性
{ }

根据我的评论,如果您希望链接到子类型的列表类型始终是通用类型,则可以使用类似的内容。IterateProperties Recursive是给定类型属性的迭代器,它将递归枚举该类型的属性以及通过泛型列表链接的所有子类型

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;
        }
    }
}
protectedvoid测试()
{
类型t=类型(容器);
string propertyList=string.Join(“,”,递归地迭代属性(“,t).ToArray());
//用propertyList做些什么
}
递归保护IEnumerable IterateProperties(字符串前缀,类型t)
{
如果(!string.IsNullOrEmpty(prefix)&&!prefix.EndsWith(“.”)prefix+=”;
前缀+=t.名称+”;
//枚举类型的属性
foreach(t.GetProperties()中的PropertyInfo p)
{
类型pt=p.PropertyType;
//如果属性是泛型列表
如果(pt.Name==“列表'1”)
{
类型genericType=pt.GetGenericArguments()[0];
//然后枚举泛型子类型
foreach(迭代属性中的字符串propertyName递归(前缀,genericType))
{
收益率返回属性名称;
}
}
其他的
{
//否则,枚举前缀为的属性
收益返回前缀+p.名称;
}
}
}

注意:此代码将无法正确处理递归地将自身作为其属性之一的类型的类型。正如@Dementic(谢谢!)指出的那样,尝试迭代此类类型将导致
StackOverflowException

您必须清楚如何确定什么是子属性。这里假设列表类型将被展平为类型T。如果有属性公共电话号码{get;set;}(而不是列表),该怎么办?这会有不同的处理方式吗?您的属性将始终是基元类型还是列表,其中T是复杂类型?可能的重复会引发StackOverflow。@Dementic:我刚刚使用OP的原始类重试了一次。在LINQPad中工作正常,并输出
Container.Name、Container.Address.AddressLine1、Container.Address.AddressLine2、Container.Address.Telephone.phone
。你能解释一下你从哪里得到的SO吗?您是否在递归引用自身的类类型上测试它?是的,我试过的类得到了一个so,你应该修复你的代码,这样功能访问者就不会得到这个(也就是说,涵盖所有的基础)@Dementic:我在帖子中添加了一个注释,以澄清答案的范围。在我的帖子中,我通常只回答OP的问题,不一定为所有用例提供通用代码。我也不认为这是回答这个问题的意图。这是作为问答和通用编程参考的网站面临的挑战之一。如果您确实对处理递归情况的代码有修复,请随意建议进行编辑。@the_:Hello。。我需要类似的东西。但是我在属性中添加了自定义属性。我需要得到所有具有自定义属性的属性的列表。我不想在对象级别使用任何属性(提取名称)。我正在尝试使用你的代码并做一些修改,但没有成功。请帮忙。@U最小的:你好。。我需要类似的东西。但是我在属性中添加了自定义属性。我需要得到所有具有自定义属性的属性的列表。因为类型是嵌套的,所以我想得到t
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;
        }
    }
}