2013-10-05 4 views
0

Рассмотрим следующие типы в сборке: 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 [] Адрес

Что такое лучший способ сделать это?

ответ

1

К сожалению, вы не использовали правильный синтаксис C# для своих данных образца. Так что я должен сделать некоторые предположения:

  • Type фактически class (или struct).

  • Содержимое типов (BusinessPartner, PrivateData, CompanyData и т. Д.) Представляет типы некоторых публичных объектов.

Чтобы проанализировать иерархию типов, вы можете использовать отражение. Найдите все общедоступные свойства заданного типа и верните их типы. Так как вы хотите только типы вы можете использовать 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 framework не имеет древовидной структуры, которую вы можете использовать из коробки. Поэтому вам необходимо реализовать его самостоятельно:

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> метод GetPropertyTypes теперь может вернуться Node<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>) вы можете просто добавить узел для string в кэш, прежде чем позвонить GetOrCreateTypeNode впервые:

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

    ... 
} 
+0

Ничего себе, спасибо, я не ожидал объяснений с полным исходным кодом. Что не так с моим синтаксисом? Когда я смотрю в сборке, я вижу только типы, а не классы (конечно, я могу спросить их, являются ли они классами). Код работает как шарм ... Моя проблема в том, что я теряю PropertyNames, когда у меня есть десять свойств строки, я вижу только десять раз System.String, и это не имеет смысла. Поэтому я должен перекодировать его, чтобы использовать PropertyInfo вместо типов, я думаю. Но большое спасибо! – JohnnyBravo75

+0

Я попытался заставить его работать с propertyinfo, но я смущен. Рекурсивные вызовы также обнаруживают общедоступные свойства в Propertyinfo и т. Д. Мне нужен только тип и имя свойства. – JohnnyBravo75

Смежные вопросы