递归获取属性&对象的子属性

发布于 2024-10-03 23:39:45 字数 689 浏览 1 评论 0原文

好吧,一开始我认为这很容易,也许确实如此,我只是太累了 - 但这就是我想做的。假设我有以下对象:

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

这使得任何感觉?我似乎无法将它包裹在我的头上。

Ok so at first I thought this was easy enough, and maybe it is and I'm just too tired - but here's what I'm trying to do. Say I have the following objects:

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

What I need to be able to do, is 'flatten' Containers property names in to a string (including ALL child properties AND child properties of child properties) that would look something like this:

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

Does that make any sense? I can't seem to wrap it around my head.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

爱,才寂寞 2024-10-10 23:39:45

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

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

I suggest you to mark all the classes, you need to grab, with custom attribute after that you could do something like this

 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
{ }
幽梦紫曦~ 2024-10-10 23:39:45

根据我的评论,如果它始终是您想要链接到子类型的通用列表类型,您可以使用类似的东西。 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 。

Per my comment, you could use something like this if it will always be a generic List type that you want to link to a child type. IteratePropertiesRecursively is an iterator over the properties of the given type, that will recursively enumerate the properties of the type and all child types linked through a generic List.

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

Note: This code will not correctly handle a type that recursively includes itself as a type of one of its properties. Trying to iterate over such a type will result in a StackOverflowException, as pointed out by @Dementic (thanks!).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文