残念ながら、サンプル データに適切な C# 構文を使用していませんでした。したがって、いくつかの仮定を立てる必要があります。
型階層を解析するには、リフレクションを使用できます。指定された型のすべてのパブリック プロパティを検索し、それらの型を返します。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
返されることに注意してください(後で対処します)。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;
}
タイプが( implementsであるため)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;
}
...
}