Something like this
private static IEnumerable<(object, object)> FindFleetId(object rootObj, int fleetId)
{
//if the rootObj is null, return an empty list
if (rootObj == null)
Enumerable.Empty<(object, object)>();
//call a local method to keep track of the rootObj and the currentObj
return Parse(rootObj, rootObj, fleetId);
}
private static IEnumerable<(object, object)> Parse(object rootObj, object currentObj, int fleetId)
{
//break the method if currentObj is null
if (currentObj == null)
yield break;
var type = currentObj.GetType();
//check if the type of the object is IEnumerable if it is enumerate the children, a string is an IEnumerable of char, so ignore a string
var enumerable = currentObj as IEnumerable;
if (enumerable != null && type != typeof(string))
{
//enumerate the list
foreach (var item in enumerable)
{
//incase the rootObj is an IEnumerable, change the rootObj to the item inside the IEnumerable
var newRootObj = object.ReferenceEquals(rootObj, currentObj) ? item : rootObj;
foreach (var nestedObj in Parse(newRootObj, item, fleetId))
yield return nestedObj;
}
yield break;
}
//get all properties
var properties = type.GetProperties();
foreach (var propertyInfo in properties)
{
//get the value of the property
var propValue = propertyInfo.GetValue(currentObj);
//check if the name is FleetId
if (propertyInfo.Name == "FleetId")
{
//cast the value to int, and compare it with the pased parameter, return if true
if (propValue is int fId && fId == fleetId)
yield return (rootObj, currentObj);
break;
}
//call method recursivly
foreach (var nestedObj in Parse(rootObj, propValue, fleetId))
yield return nestedObj;
}
}
The return type for this idea is IEnumerable<(object, object)>
where the Item1
of the tuple is the root object (Level 0, ground zero) of the chain, and Item2
of the tuple is the object that actually had the member FleetId
.
The code uses recursion to parse the levels/depths of the object.
To find the member and get the value it uses reflection, the member has to be public.
Complete code, with test cases:
class Program
{
static void Main(string[] args)
{
int fleetId = 1;
var a = FindFleetId(TestA(), fleetId);
foreach (var x in a)
Console.WriteLine(x.Item1.ToString() + " | " + x.Item2.ToString());
var b = FindFleetId(TestB(), fleetId);
foreach (var x in b)
Console.WriteLine(x.Item1.ToString() + " | " + x.Item2.ToString());
var c = FindFleetId(TestC(), fleetId);
foreach (var x in c)
Console.WriteLine(x.Item1.ToString() + " | " + x.Item2.ToString());
var d = FindFleetId(TestD(), fleetId);
foreach (var x in d)
Console.WriteLine(x.Item1.ToString() + " | " + x.Item2.ToString());
Console.ReadKey();
}
private static IEnumerable<(object, object)> FindFleetId(object rootObj, int fleetId)
{
if (rootObj == null)
Enumerable.Empty<(object, object)>();
return Parse(rootObj, rootObj, fleetId);
}
private static IEnumerable<(object, object)> Parse(object rootObj, object currentObj, int fleetId)
{
if (currentObj == null)
yield break;
var type = currentObj.GetType();
var enumerable = currentObj as IEnumerable;
if (enumerable != null && type != typeof(string))
{
foreach (var item in enumerable)
{
var newRootObj = object.ReferenceEquals(rootObj, currentObj) ? item : rootObj;
foreach (var nestedObj in Parse(newRootObj, item, fleetId))
yield return nestedObj;
}
yield break;
}
var properties = type.GetProperties();
foreach (var propertyInfo in properties)
{
var propValue = propertyInfo.GetValue(currentObj);
if (propertyInfo.Name == "FleetId")
{
if (propValue is int fId && fId == fleetId)
yield return (rootObj, currentObj);
break;
}
foreach (var nestedObj in Parse(rootObj, propValue, fleetId))
yield return nestedObj;
}
}
private static A1[] TestA()
{
return new[]
{
new A1() {A2 = new A2() {A3 = new A3() {FleetId = 1}}},
new A1() {A2 = new A2() {A3 = new A3() {FleetId = 2}}},
};
}
private static B1[] TestB()
{
return new[]
{
new B1() {FleetId = 1},
new B1() {FleetId = 2},
};
}
private static C1[] TestC()
{
return new[]
{
new C1() { C2 = new List<C2>() { new C2() { C3 = new C3(){FleetId = 1}}}},
new C1() { C2 = new List<C2>() { new C2() { C3 = new C3(){FleetId = 2}}}},
};
}
private static D1[] TestD()
{
return new[]
{
new D1() { D2 = new List<D2>() {new D2(){FleetId = 1}}},
new D1() { D2 = new List<D2>() {new D2(){FleetId = 2}}},
};
}
}
public class A1
{
public A2 A2 { get; set; }
public override string ToString()
{
return "A1." + A2;
}
}
public class A2
{
public A3 A3 { get; set; }
public override string ToString()
{
return "A2." + A3;
}
}
public class A3
{
public int FleetId { get; set; }
public override string ToString()
{
return "A3." + FleetId;
}
}
public class B1
{
public int FleetId { get; set; }
public override string ToString()
{
return "B1." + FleetId;
}
}
public class C1
{
public List<C2> C2 { get; set; }
public override string ToString()
{
return "C1." + string.Join("|", C2);
}
}
public class C2
{
public C3 C3 { get; set; }
public override string ToString()
{
return "C2." + C3;
}
}
public class C3
{
public int FleetId { get; set; }
public override string ToString()
{
return "C3." + FleetId;
}
}
public class D1
{
public List<D2> D2 { get; set; }
public override string ToString()
{
return "D1." + string.Join("|", D2);
}
}
public class D2
{
public int FleetId { get; set; }
public override string ToString()
{
return "D2." + FleetId;
}
}
See it in action:
https://dotnetfiddle.net/mqWCVk