【问题标题】:Constructing a navigation path between two objects在两个对象之间构建导航路径
【发布时间】:2011-11-17 05:56:07
【问题描述】:

给定任意对象图中(由属性和集合链接)中的两个 .NET 对象(根和叶),是否存在用于构造路径的现有 API 或示例(类似于 WPF 属性绑定路径或 XML XPath) 从一个到另一个? “源”(即要找出路径的对象)将是叶对象。

还必须支持索引位置。 (例如Foo.Bar[42].Baz["frog"].Quux)。

这主要用于错误报告——我想让对象记录一个错误,显示它们在对象模型中的位置,而不仅仅是它们的类型名称。 (这很重要,因为同一类型的对象可能包含在大量其他对象类型中,并且修复任何问题所需的用户操作将根据该位置而有所不同。)

我可以通过给每个对象一个对其父对象的引用并递归地询问每个父对象如何到达子对象来手动滚动一些东西。但在我这样做之前,我想知道是否有任何现有/更好的解决方案。 (如果有人忘记更新父链接,或者一个对象可以通过两条不同的路径到达,这很脆弱,尽管这种情况应该很少见。)

【问题讨论】:

  • 您的解决方案将是可行的方法。大多数图形路径实现。以这种方式工作(尽管也可以反过来)。我不确定一个对象能够通过两条不同的路径到达的问题是什么,这是图中的自然行为并且对路径没有任何影响。关于更新:您可以跟踪节点中的版本号(int),每次修改节点(子集合)时,将版本号增加一。现在在构建路径时,您可以复制当前版本号,并且在遍历时,您可以简单地检查版本号以进行更新
  • 大黄蜂:不,我不这么认为。对象之间的链接只是常规命名的 .NET 属性。这是一种分层实体模拟模型,如果有帮助的话。 :)
  • Polity:多个传入路径的唯一真正问题是每个对象都需要存储所有父对象的集合,然后你最终会得到一个结果路径的集合,而不是拥有一些东西很好,很明确。但这可能是不可避免的。不过,我不确定添加版本号会有什么帮助——这只是添加了一件忘记更新的东西。

标签: c# .net logging properties object-graph


【解决方案1】:

这是您如何开始的一些非常简化的变体,希望这会有所帮助...

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace ConsoleApplication2
{
    class Test2
    {
        public bool BoolProp { get; set; }

        public int[] ArrayProp { get; set; }
    }

    class Test1
    {
        public string StringProp { get; set; }

        public Dictionary<string, Test2> DictionaryProp { get; set; }
    }

    class Program
    {


        private static string ObjToPathKey(object key)
        {
            if (key == null) return "null";
            if (key is string) return string.Format("\"{0}\"", key);
            return key.ToString();
        }

        public static IEnumerable<KeyValuePair<string, object>> GetFullTree(object root, string currentPath)
        {
            if (root == null) yield break;

            yield return new KeyValuePair<string, object>(currentPath, root);

            var type = root.GetType();
            if (!type.IsClass || type == typeof(string)) yield break;

            if (root is IEnumerable)
            {
                if (root is IDictionary)
                {
                    IDictionary d = (IDictionary) root;
                    foreach (var key in d.Keys)
                    {
                        var child = d[key];
                        foreach (var subchildPair in GetFullTree(child, string.Format("{0}[{1}]", currentPath, ObjToPathKey(key))))
                        {
                            yield return subchildPair;
                        }
                    }
                    yield break;
                }

                int i = 0;
                if (root is IList)
                {
                    foreach (var child in (IEnumerable)root)
                    {
                        foreach (var subChildPair in GetFullTree(child, string.Format("{0}[{1}]", currentPath, i)))
                        {
                            yield return subChildPair;
                        }
                        ++i;
                    }
                    yield break;
                }

                throw new NotSupportedException();
            }

            if (type.IsClass)
            {
                foreach (PropertyInfo propertyInfo in type.GetProperties())
                {
                    //TODO: Add indexers support
                    object propertyValue = propertyInfo.GetValue(root, null);
                    foreach (var subChildPair in GetFullTree(propertyValue, string.Format("{0}.{1}", currentPath, propertyInfo.Name)))
                    {
                        yield return subChildPair;
                    }
                }
            }
        }

        static void Main(string[] args)
        {
            Test1 t = new Test1()
                          {
                              StringProp = "Some value",
                              DictionaryProp = new Dictionary<string, Test2>()
                                                   {
                                                       {
                                                           "key1", new Test2()
                                                                       {
                                                                           ArrayProp = new[] {1, 2, 3},
                                                                           BoolProp = true
                                                                       }
                                                           },
                                                       {
                                                           "key 2", new Test2()
                                                                        {
                                                                            ArrayProp = new[] {4, 5, 6, 7},
                                                                            BoolProp = false
                                                                        }
                                                           }
                                                   }
                          };

            foreach (var pair in GetFullTree(t, "t"))
            {
                Console.WriteLine("{0} = {1}", pair.Key, pair.Value);
            }

            Console.Read();

            /* Program output:
                t = ConsoleApplication2.Test1
                t.StringProp = Some value
                t.DictionaryProp = System.Collections.Generic.Dictionary`2[System.String,Console
                Application2.Test2]
                t.DictionaryProp["key1"] = ConsoleApplication2.Test2
                t.DictionaryProp["key1"].BoolProp = True
                t.DictionaryProp["key1"].ArrayProp = System.Int32[]
                t.DictionaryProp["key1"].ArrayProp[0] = 1
                t.DictionaryProp["key1"].ArrayProp[1] = 2
                t.DictionaryProp["key1"].ArrayProp[2] = 3
                t.DictionaryProp["key 2"] = ConsoleApplication2.Test2
                t.DictionaryProp["key 2"].BoolProp = False
                t.DictionaryProp["key 2"].ArrayProp = System.Int32[]
                t.DictionaryProp["key 2"].ArrayProp[0] = 4
                t.DictionaryProp["key 2"].ArrayProp[1] = 5
                t.DictionaryProp["key 2"].ArrayProp[2] = 6
                t.DictionaryProp["key 2"].ArrayProp[3] = 7
             */
        }
    }
}

【讨论】:

  • 为了防止无限递归,在 GetFullTree(...) 方法中添加一个 Stack 参数,并跟踪所有链接。要获得确切孩子的路径,请使用 .FirstOrDefault(x => x.Value == targetObject)。请记住,这个函数非常慢,所以不要在关键代码中使用它
  • 我最终按照我最初的描述做了:向每个节点添加父节点列表(通过公共基类)并使用元属性系统(类似于 DependencyProperties)来快速访问之间的链接对象。但我之所以选择这个解决方案,是因为它提供的代码看起来像是对原始问题的一种解释,尽管使用反射会出现一些性能问题。
【解决方案2】:

我没有在框架中看到任何接近的东西。

如果没有“父”属性,我认为您正在寻找“图中的最短路径”(http://en.wikipedia.org/wiki/Pathfinding),但由于复杂性和保持路径结构的内存要求,它看起来不是跟踪/错误日志记录的好选择.

【讨论】:

    猜你喜欢
    • 2017-03-29
    • 2011-08-08
    • 2012-04-14
    • 2016-07-10
    • 1970-01-01
    • 1970-01-01
    • 2020-04-30
    • 2011-10-07
    • 1970-01-01
    相关资源
    最近更新 更多