繁体   English   中英

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

[英]How to iterate a C# object, looking for all instances of a specific type, in order to build a separate list of those instances?

我需要有点类似于这个问题 ,除了它需要更深入地探索源对象。

这是一个代码示例:

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实例。

为了便于探索,我们可以假设以下内容:

  1. 仅浏览属性。
  2. 没有无限的参考循环。

就目前而言, 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可序列化的,如果我没有弄错,它需要所有序列化数据都是公共的。

您应该使用普通的序列化,但是定义自己的自定义格式化程序,除了跟踪您正在查找的对象之外什么都不做。 这是一个简单的自定义格式化程序的示例

你可以使用反射来做到这一点。 有两个任务需要解决:

  1. 获取类型的所有属性。 Type.GetProperties()会这样做。

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

因此,通过对泛型类型使用反射和递归应该能够做到你想要的。

Dim tTargets()= {New List(Of Target)来自{New Target(“Joe”),New Target(“Bob”)},New Target(“Veronica”),New Tuple(Of String,Target,DateTime,Target) )(“A Tuple”,New Target(“Henry”),DateTime.Now,New Target(“Deacon”))}

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM