Если вам нужно что-то, что будет работать в общем случае (любая иерархия классов), тогда вы можете сделать следующее:
Вам понадобится рекурсивный алгоритм (функция). Алгоритм будет зацикливаться на элементах, добавляя их типы в список (если они еще не добавлены), а затем возвращая этот список, комбинируя с типами членов типа, только что добавленных в список, - здесь вы делаете рекурсивный вызов. Условия прекращения будут:
- Тип члена является примитивным (чтобы проверить это использование
Type.IsPrimitive
).
- Отражаемый тип определен в другой сборке. Вы можете проверить определяющую сборку, используя
Type.Assembly
.
Если вам нужно что-то более простое, используйте вышеописанную технику, но просто используйте оператор if в цикле.
Дайте мне знать, если вы этого хотите или нет, и тогда я опубликую пример кода для вас, когда у меня будет больше времени.
Обновление: Ниже приведен пример кода, показывающий, как вы можете обработать тип и рекурсивно получить все типы, содержащиеся в нем. Вы можете назвать это так: List typesHere = GetTypes(myObject.GetType())
public static List<Type> GetTypes(Type t)
{
List<Type> list = new List<Type>();
if (t.IsPrimitive)
{
if (!list.Contains(t))
list.Add(t);
return list;
}
else if (!t.Assembly.Equals(System.Reflection.Assembly.GetExecutingAssembly()))
{
//if the type is defined in another assembly then we will check its
//generic parameters. This handles the List<Item> case.
var genArgs = t.GetGenericArguments();
if (genArgs != null)
foreach (Type genericArgumentType in genArgs)
{
if(!list.Contains(genericArgumentType))
list.AddRange(GetTypes(genericArgumentType));
}
return list;
}
else
{
//get types of props and gen args
var types = t.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).Select(pi => pi.PropertyType).ToList();
types.AddRange(t.GetGenericArguments());
foreach (System.Type innerType in types)
{
//get the object represented by the property to traverse the types in it.
if (!list.Contains(innerType))
list.Add(innerType);
else continue; //because the type has been already added and as thus its child types also has been already added.
var innerInnerTypes = GetTypes(innerType);
//add the types filtering duplicates
foreach (Type t1 in innerInnerTypes) //list.AddRange(innerTypes); //without filtering duplicates.
if (!list.Contains(t1))
list.Add(t1);
}
return list;
}
}
Поэтому, когда я запустил это для классов, которые вы опубликовали в исходном сообщении (элемент, имеющий два примитивных свойства, как показано ниже), я получил следующий список:
GetTypes(typeof(List<Item>))
Count = 3
[0]: {Name = "Item" FullName = "AssemblyNameXYZ.Item"}
[1]: {Name = "String" FullName = "System.String"}
[2]: {Name = "Int32" FullName = "System.Int32"}
GetTypes(typeof(Item))
Count = 2
[0]: {Name = "String" FullName = "System.String"}
[1]: {Name = "Int32" FullName = "System.Int32"}
Reflection.GetTypes(typeof(RootClass))
Count = 5
[0]: {Name = "List`1" FullName = "System.Collections.Generic.List`1[[AssemblyNameXYZ.Item, AssemblyNameXYZ, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]"}
[1]: {Name = "Item" FullName = "AssemblyNameXYZ.Item"}
[2]: {Name = "String" FullName = "System.String"}
[3]: {Name = "Int32" FullName = "System.Int32"}
[4]: {Name = "SubClass" FullName = "AssemblyNameXYZ.SubClass"}
Я не проводил всесторонних испытаний, но это должно, по крайней мере, указать вам правильное направление. Веселый вопрос. Мне весело отвечать.