【问题标题】:parse type hierarchy in assembly在程序集中解析类型层次结构
【发布时间】:2013-10-05 00:43:41
【问题描述】:

考虑程序集中的以下类型:BusinessPartnerList、BusinessPartner、PrivateData、CompanyData、AddressList、Address

Type BusinessPartnerList 
{ 
    BusinessPartner[] 
}

Type BusinessPartner 
{
   PrivateData
   CompanyData
   AddressList
}

Type PrivateData
{
    System.String FirstName
    System.String SurName
}

Type PrivateData
{
    System.String CompanName1
    System.String CompanName2
}

Type AddressList
{
  Address[]
}

我想通用解析类型层次结构,并在树中表示它们,例如简单节点

业务合作伙伴列表[] 生意伙伴 私人数据 公司数据 地址列表[] 地址

最好的方法是什么?

【问题讨论】:

    标签: c# reflection types hierarchy


    【解决方案1】:

    很遗憾,您没有为示例数据使用正确的 C# 语法。所以我必须做一些假设:

    • Type 实际上是class(或struct)。

    • 类型(BusinessPartnerPrivateDataCompanyData等)的内容代表了一些公共属性的类型。

    要解析类型层次结构,您可以使用反射。查找给定类型的所有公共属性并返回它们的类型。由于您只需要类型,因此您可以使用 HashSet,它只包含不同的类型:

    public static HashSet<Type> GetPropertyTypes(Type type)
    {
        return new HashSet<Type>(type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
                                     .Select(prop => prop.PropertyType));
    }
    

    但是,您似乎不想获得有关数组的信息,而是想获得有关数组元素类型的信息。列表也是如此。所以如果一个类型实现了IEnumerable&lt;T&gt;,你想获取T类型的信息:

    private static Type GetElementType(Type type)
    {
        Type enumerableType = type.GetInterfaces().FirstOrDefault(IsGenericEnumerable);
    
        if (enumerableType != null)
        {
            Type[] genericArguments = enumerableType.GetGenericArguments();
    
            return genericArguments[0];
        }
    
        // return 'object' for a non-generic IEnumerable
        return typeof(IEnumerable).IsAssignableFrom(type) ? typeof(object) : type;
    }
    
    private static bool IsGenericEnumerable(Type type)
    {
        return type.IsGenericType &&
               type.GetGenericTypeDefinition() == typeof(IEnumerable<>);
    }
    

    请注意,对于System.String 类型,这将返回char,因为string 实现了IEnumerable&lt;char&gt;(稍后我会解决这个问题)。

    .NET 框架没有开箱即用的树形结构。所以需要自己实现:

    public class Node<T>
    {
        public Node(T value, IEnumerable<Node<T>> children)
        {
            Value = value;
            Children = children.ToList();
        }
    
        public T Value
        {
            get;
            private set;
        }
    
        public List<Node<T>> Children
        {
            get;
            private set;
        }
    }
    

    这是一个非常基本的实现,仅用于演示目的。

    现在GetPropertyTypes 方法可以返回Node&lt;Type&gt;,而不是返回List&lt;Type&gt;,它应该重命名为CreateTypeNode

    public static Node<Type> CreateTypeNode(Type type)
    {
        var children = type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
                           .Select(prop => GetElementType(prop.PropertyType))
                           .Select(CreateTypeNode);
    
        return new Node<Type>(type, children);
    }
    

    此方法使用递归来为给定类型创建完整的树。

    还有一个问题:如果A 类型引用B 类型,反之亦然怎么办?这将导致无限递归循环。而且:如果一个类型已经被访问过,那么就不需要再这样做了。

    我们需要的是已经访问过的类型的缓存。如果一个类型在缓存中,我们使用缓存中的信息:

    private static readonly Dictionary<Type, Node<Type>> _visitedTypes = new Dictionary<Type, Node<Type>>();
    
    public static Node<Type> CreateTypeNode(Type type)
    {
        Node<Type> node;
        if (_visitedTypes.TryGetValue(type, out node))
        {
            return node;
        }
    
        // add the key to the cache to prevent infinite recursion; the value will be set later
        // if this type will be found again in a recursive call CreateTypeNode returns null
        // (null will be filtered out then)
        _visitedTypes.Add(type, null);
    
        var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
    
        var types = new HashSet<Type>(properties.Select(prop => GetElementType(prop.PropertyType)));
    
        var children = types.Select(CreateTypeNode).Where(n => n != null);
    
        node = new Node<Type>(type, children);
        _visitedTypes[type] = node;
    
        return node;
    }
    

    我不希望string 类型被报告为char(因为string 实现IEnumerable&lt;char&gt;)您可以在调用GetOrCreateTypeNode 之前将string 的节点添加到缓存中第一次:

    _visitedTypes.Add(typeof(string), new Node<Type>(typeof(string), new List<Node<Type>>()));
    

    然后在GetElementType方法中检查缓存:

    private static Type GetElementType(Type type)
    {
        if (_visitedTypes.ContainsKey(type))
        {
            return type;
        }
    
        ...
    }
    

    【讨论】:

    • 哇,谢谢,我没想到会有完整源代码的解释。我的语法有什么问题?当我查看程序集时,我只看到类型而不是类(当然我可以询问它们是否是类)。代码就像一个魅力......我的问题是,我失去了 Propertynames,当我有十个字符串属性时,我只看到十次 System.String,这没有任何意义。所以我认为我必须重新编码它以使用 PropertyInfo 而不是类型。但是非常感谢!
    • 我试图让它与 propertyinfo 一起工作,但我很困惑。递归调用还可以在 Propertyinfo 中找到公共属性等等……我只需要类型和属性名。
    猜你喜欢
    • 2011-06-13
    • 1970-01-01
    • 2012-08-25
    • 2013-03-18
    • 2013-12-25
    • 2014-03-18
    • 1970-01-01
    • 2019-07-05
    • 1970-01-01
    相关资源
    最近更新 更多