不幸的是,您没有为示例数据使用正确的 C# 语法。所以我必须做一些假设:
要解析类型层次结构,您可以使用反射。查找给定类型的所有公共属性并返回它们的类型。由于您只需要可以使用的类型,因此 aHashSet
只包含不同的类型:
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;
}
}
这是一个非常基本的实现,仅用于演示目的。
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
implements )您可以在第一次调用之前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;
}
...
}