如何迭代一个C#对象,查找特定类型的所有实例,以便构建这些实例的单独列表?

如何迭代一个C#对象,查找特定类型的所有实例,以便构建这些实例的单独列表?,c#,reflection,properties,enumeration,C#,Reflection,Properties,Enumeration,我有一个类似的需求,除了它需要对源对象进行更深入的探索 下面是一个代码示例: public class Target {}; public class Analyzed { public Target EasyOne { get; set; } public IList<Target> ABitMoreTricky { get; set; } public IList<Tuple<string, Target>> Nightmare

我有一个类似的需求,除了它需要对源对象进行更深入的探索

下面是一个代码示例:

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; }
}
公共类目标{};
公共类分析
{
公共目标EasyOne{get;set;}
公共IList ABITMORE{get;set;}
公共IList{get;set;}
}
从分析的
实例
,我想提取所有
目标
实例

为了便于勘探,我们可以假设:

  • 只浏览属性
  • 没有无限的参考循环

  • 目前,
    EasyOne
    是。。。简单,但我正在寻找一些策略,以使所有
    目标
    实例在更复杂的结构中丢失。

    您可以采用反射方式,对您知道的所有容器(IEnumerable、IDictionary、所有元组,谁知道还有什么)进行特殊处理,或者你可以真正实现@Adrian Iftode在评论中开玩笑说的话

    我不认为您真的想要序列化为XML然后解析它。它可以工作,但它要求所有对象都是XML可序列化的,如果我没有弄错的话,这要求所有序列化的数据都是公共的


    您应该使用普通的序列化,但定义自己的自定义格式化程序,该格式化程序只跟踪要查找的对象。下面是一个例子,大致如下:

        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());
        }
    
    公共列表findallinstance(对象值),其中T:class
    {
    HashSet exploredObjects=新HashSet();
    找到的列表=新列表();
    FindAllInstances(值、探索对象、已发现);
    发现退货;
    }
    私有void FindAllInstances(对象值、哈希集exploredObject、找到的列表),其中T:class
    {
    如果(值==null)
    返回;
    if(exploredObjects.Contains(value))
    返回;
    exploredObjects.Add(value);
    IEnumerable enumerable=作为IEnumerable的值;
    if(可枚举!=null)
    {
    foreach(可枚举中的对象项)
    {
    FindAllInstances(项目,探索对象,已发现);
    }
    }
    其他的
    {
    T possibleMatch=值为T;
    if(可能匹配!=null)
    {
    找到。添加(可能匹配);
    }
    Type Type=value.GetType();
    PropertyInfo[]properties=type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetProperty);
    foreach(属性中的PropertyInfo属性)
    {
    object propertyValue=property.GetValue(值,null);
    FindAllInstances(propertyValue,ExploredObject,found);
    }
    }
    私有无效测试()
    {
    已分析=新分析()
    {
    EasyOne=新目标(),
    ABITMORE=new List(){new Target()},
    噩梦=新列表(){new Tuple(“,new Target())}
    };
    找到的列表=FindAllInstances(已分析);
    Show(found.Count.ToString());
    }
    
    您可以使用反射来完成此操作。有两个任务需要解决:

  • 获取类型的所有属性。
    type.GetProperties()
    将完成此操作

  • 确定属性类型是属于
    Target
    类型,还是属于泛型类型,并将
    Target
    作为类型参数。可以使用
    type.IsGenericType
    来测试类型是否为泛型和
    类型。GetGenericArguments
    来获取实际的类型参数。如果找到匹配项,则应从开始递归此泛型类型1并执行2中描述的匹配

  • 因此,通过对泛型类型使用反射和递归,您应该能够做您想要做的事情

    Dim tTargets()={New Target(“Joe”)中的{New List(目标),New Target(“Bob”)},新Target(“Veronica”),新元组(字符串,Target, DateTime,Target)(“一个元组”,新目标(“Henry”),DateTime。现在,新目标 目标(“执事”)}


    泛型参数本身没有实例。对于@M.Babcock的观点,我假设您指的是存储在IList
    噩梦中的每个元组中的目标实例?我不明白,您的示例有什么问题。使类可序列化,将其序列化为xml,提取目标节点,反序列化它们。这很简单一个笑话:P@AdrianIftode,我也很喜欢你的解决方案:DThanks a lot,RQDQ,这完美地回答了我的问题。我没有太多期望…你甚至管理了无限引用循环。如果你有索引属性(这个[int index]),行
    object propertyValue=property.GetValue(value,null);
    将失败,很容易检查,但是你跳过了内容。我重写了它以使用FieldInfo,这和属性一样简单。嗨@Jaap,你介意解释一下“我重写它以使用FieldInfo”吗?干杯。我使用了字段而不是属性。在一些自动序列化场景中,字段更合适(当然也有很多缺点)。而不是op type.GetProperties,当时type.GetFields工作得很好。还有
    string
    实现了IEnumerable。应该跳过stringsThanks来回答你的问题,Boo。它看起来像RQDQ(即使我不太习惯读VB)。是的,RQDQ比我快了。但既然我做了这项工作,我还是发布了它。
    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