如何迭代C#对象,查找特定类型的所有实例,以便构建这些实例的单独列表
本文关键字:实例 构建 列表 单独 迭代 何迭代 查找 对象 类型 | 更新日期: 2023-09-27 18:26:32
我有一个需求与这个问题有点相似,只是它需要对源对象进行更深入的探索。
这是一个代码示例:
public class Target {};
public class Analyzed
{
public Target EasyOne { get; set; }
public IList<Target> ABitMoreTricky { get; set; }
public IList<Tuple<string, Target>> Nightmare { get; set; }
}
从Analyzed
的一个实例中,我想提取所有的Target
实例。
为了便于探索,我们可以假设如下:
- 仅浏览属性
- 不存在无限引用循环
目前,EasyOne
…很简单,但我正在寻找一些策略,以使所有Target
实例在更棘手的结构中丢失。
下面的内容如何:
public List<T> FindAllInstances<T>(object value) where T : class
{
HashSet<object> exploredObjects = new HashSet<object>();
List<T> found = new List<T>();
FindAllInstances(value, exploredObjects, found);
return found;
}
private void FindAllInstances<T>(object value, HashSet<object> exploredObjects, List<T> found) where T : class
{
if (value == null)
return;
if (exploredObjects.Contains(value))
return;
exploredObjects.Add(value);
IEnumerable enumerable = value as IEnumerable;
if (enumerable != null)
{
foreach(object item in enumerable)
{
FindAllInstances<T>(item, exploredObjects, found);
}
}
else
{
T possibleMatch = value as T;
if (possibleMatch != null)
{
found.Add(possibleMatch);
}
Type type = value.GetType();
PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetProperty);
foreach(PropertyInfo property in properties)
{
object propertyValue = property.GetValue(value, null);
FindAllInstances<T>(propertyValue, exploredObjects, found);
}
}
private void TestIt()
{
Analyzed analyzed = new Analyzed()
{
EasyOne = new Target(),
ABitMoreTricky = new List<Target>() { new Target() },
Nightmare = new List<Tuple<string, Target>>() { new Tuple<string, Target>("", new Target()) }
};
List<Target> found = FindAllInstances<Target>(analyzed);
MessageBox.Show(found.Count.ToString());
}
你可以采用反思的方式,对你知道的所有容器(IEnumerable、IDictionary、所有元组,以及谁知道其他的)进行特殊处理,或你可以实际实现@Adrian Iftode在评论中开玩笑地说的话。
我不认为你真的想序列化到XML然后解析它。它会起作用,但它要求你的所有对象都是XML可序列化的,如果我没有错的话,这要求所有序列化的数据都是公共的。
您应该使用普通的序列化,但定义自己的自定义格式化程序,它只跟踪您要查找的对象。下面是一个简单的自定义格式化程序的示例。
您可以使用反射来完成此操作。有两项任务需要解决:
-
获取一个类型的所有属性。
Type.GetProperties()
会这么做。 -
确定属性类型是
Target
类型还是以Target
作为类型参数的泛型类型。您可以使用Type.IsGenericType
来测试类型是否为泛型,使用Type.GetGenericArguments
来获取实际的类型参数。如果找到匹配,则应从1开始递归此泛型类型,并执行2中描述的匹配。
因此,通过对泛型类型使用反射和递归,应该能够实现您想要的功能。
Dim tTargets()={来自{New Target("Joe"),NewTarget("Bob")},New Target("Veronica"),New Tuple(Of String,Target,DateTime,Target)("A Tuple",New Target("Henry"),DateTime.Now,New目标("执事")}
Sub ShowMeTheTargets(ByVal tRoot As Object, ByVal tLevel As Int32)
Dim tCount As Int64 = 0
Dim tCountName As String = "Length"
If Nothing Is tRoot Then
Exit Sub
End If
If tRoot.GetType Is GetType(Target) Then
RTB.AppendText("Found: " & CType(tRoot, Target).Name & vbCrLf)
'
' Assume Target is not a Target container.
'
Exit Sub
End If
If LEVEL_MAX = tLevel Then
'
' We don't want to scan this object graph any deeper
'
Exit Sub
End If
If (Nothing Is tRoot.GetType.GetInterface("IEnumerable")) Then
For Each tProperty As PropertyInfo In tRoot.GetType.GetProperties
ShowMeTheTargets(tProperty.GetValue(tRoot, Nothing), tLevel + 1)
Next
Else
For Each tObject As Object In tRoot
ShowMeTheTargets(tObject, tLevel + 1)
Next
RTB.AppendText(tCount & vbCrLf)
End If
End Sub