簡體   English   中英

解析程序集中的類型層次結構

[英]parse type hierarchy in assembly

請考慮程序集中的以下類型:BusinessPartnerList,BusinessPartner,PrivateData,CompanyData,AddressList,地址

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[]
}

我想通用解析類型層次結構,並在樹中表示它們,例如簡單節點

BusinessPartnerList [] BusinessPartner PrivateData CompanyData AddressList []地址

做這個的最好方式是什么?

不幸的是,您沒有對示例數據使用正確的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<T>您希望獲取有關類型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<char> (我將在后面進行處理)。

.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<Type>而不是返回List<Type> ,而應將其重命名為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<char> ),您可以在第一次調用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;
    }

    ...
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM