I have a class which has nested objects. I wanna get all the Guid properties and their values with the name "Id". This is simple version of my classes:
public class Main
{
public Guid Id { get; set; }
public ICollection<Child> Children { get; set; }
}
public class Child
{
public Guid Id { get; set; }
public ICollection<GrandChild> GrandChildren { get; set; }
}
public class GrandChild
{
public Guid Id { get; set; }
}
I am gonna have Main class object and wanna retrieve all Guid properties with their values for all nested and etc. for further logic.I need to use reflection. Main class is an example. I can have generic types at runtime. Any suggestions?
CodePudding user response:
Use a recursive function to traverse the hierarchy. Something like this:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp34
{
public class Main
{
public Guid Id { get; set; }
public ICollection<Child> Children { get; set; }
}
public class Child
{
public Guid Id { get; set; }
public ICollection<GrandChild> GrandChildren { get; set; }
}
public class GrandChild
{
public Guid Id { get; set; }
}
class Program
{
static void Main(string[] args)
{
var main = new Main()
{
Children = new List<Child>()
{
new Child()
{
Id = Guid.NewGuid(),
GrandChildren = new List<GrandChild>()
{
new GrandChild() {Id=Guid.NewGuid() } ,
new GrandChild() {Id=Guid.NewGuid() } ,
new GrandChild() {Id=Guid.NewGuid() } ,
new GrandChild() {Id=Guid.NewGuid() }
}
},
new Child()
{
Id = Guid.NewGuid(),
GrandChildren = new List<GrandChild>()
{
new GrandChild() {Id=Guid.NewGuid() } ,
new GrandChild() {Id=Guid.NewGuid() } ,
new GrandChild() {Id=Guid.NewGuid() } ,
new GrandChild() {Id=Guid.NewGuid() }
}
}
}
};
foreach (var val in GetIds(main))
{
Console.WriteLine($"{val.Item1.GetType().Name} Id {val.Item2}");
}
}
internal static IEnumerable<(object,Guid)> GetIds(Object o)
{
var t = o.GetType();
foreach (var pId in t.GetProperties().Where( p => p.Name == "Id" && p.PropertyType == typeof(Guid)))
{
yield return (o, (Guid)pId.GetValue(o));
}
foreach (var pChild in t.GetProperties().Where(p => p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition() == typeof(ICollection<>)))
{
var children = (IEnumerable)pChild.GetValue(o);
foreach (var child in children)
{
foreach (var val in GetIds(child))
{
yield return val;
}
}
}
}
}
}
outputs
Main Id 00000000-0000-0000-0000-000000000000
Child Id 971310cb-b1f6-4d27-91de-f1f4edcb1a3b
GrandChild Id 160ce573-e8bc-4cd6-9569-86d82e50d531
GrandChild Id 3524d68e-cbba-453f-a62c-b7530923f937
GrandChild Id 205ef5ac-c97c-45fc-b07a-aead02bd047e
GrandChild Id f79aff79-d3ef-46e0-a637-089bb4b8d9ec
Child Id dd789259-b1cb-4d12-b109-6318afbbc33f
GrandChild Id e909ef22-abf7-4aa7-bf07-8e82ea941203
GrandChild Id 16243269-1994-4d6e-b18e-1661c7e08867
GrandChild Id fba632c9-0450-4d42-86e6-b2b64479bc52
GrandChild Id 2ccb1f39-f046-41fe-b9db-68492c0d9010
CodePudding user response:
In this case I've used a Dictionary to define how to find the descendants. But this may be done directly by searching generic Type. And this one do not use anonymous type as a result. Nevertheless, the result is the same, you have to recursively traverse the hierarchy.
private static Dictionary<Type, Func<Type>> childTypeAccessor = new Dictionary<Type, Func<Type>>()
{
{ typeof(Main), () => typeof(Child) },
{ typeof(Child), () => typeof(GrandChild) }
};
public class TypeDef
{
public Type Type {get; set;}
public string PropertyName { get; set; }
}
private IEnumerable<TypeDef> SearchTypeProperties(Type type, Type searchType)
{
var properties = type.GetProperties().Where(p => p.PropertyType == searchType).Select(p =>
new TypeDef { Type = type, PropertyName = p.Name }
);
if (childTypeAccessor.ContainsKey(type))
{
var subproperties = SearchTypeProperties(childTypeAccessor[type](), searchType);
return properties.Concat(subproperties).ToList();
}
return properties.ToList();
}