0

Consider the following types in an assembly: 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[]
}

I want to generic parse the type hierarchy, and represent them in a tree e.g. simple nodes

BusinessPartnerList[] BusinessPartner PrivateData CompanyData AddressList[] Address

What is the best way to do this?

4

1 に答える 1

1

残念ながら、サンプル データに適切な C# 構文を使用していませんでした。したがって、いくつかの仮定を立てる必要があります。

  • Type実際にはclass(またはstruct) です。

  • タイプ ( BusinessPartner、など) の内容はPrivateDataCompanyDataいくつかのパブリック プロパティのタイプを表します。

型階層を解析するには、リフレクションを使用できます。指定された型のすべてのパブリック プロパティを検索し、それらの型を返します。HashSetタイプのみが必要なため、異なるタイプのみを含むa を使用できます。

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返されることに注意してください(後で対処します)。charstringIEnumerable<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;
    }
}

これは、デモンストレーションのみを目的とした非常に基本的な実装です。

メソッドを返す代わりに、返すList<Type>ことができるようになりました。名前を次のように変更する必要があります。GetPropertyTypesNode<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;
}

タイプが( implementsであるため)stringとして報告されたくない場合は、最初に呼び出す前にノードをキャッシュに追加するだけです:charstringIEnumerable<char>stringGetOrCreateTypeNode

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

    ...
}
于 2013-10-05T06:01:45.530 に答える