К сожалению, вы не использовали правильный синтаксис 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;
}
...
}
Ничего себе, спасибо, я не ожидал объяснений с полным исходным кодом. Что не так с моим синтаксисом? Когда я смотрю в сборке, я вижу только типы, а не классы (конечно, я могу спросить их, являются ли они классами). Код работает как шарм ... Моя проблема в том, что я теряю PropertyNames, когда у меня есть десять свойств строки, я вижу только десять раз System.String, и это не имеет смысла. Поэтому я должен перекодировать его, чтобы использовать PropertyInfo вместо типов, я думаю. Но большое спасибо! – JohnnyBravo75
Я попытался заставить его работать с propertyinfo, но я смущен. Рекурсивные вызовы также обнаруживают общедоступные свойства в Propertyinfo и т. Д. Мне нужен только тип и имя свойства. – JohnnyBravo75