2011-11-17 53 views
1

給定兩個.NET對象(根和葉)中的任意的對象圖(由屬性和集合連接)之間的導航路徑,有一個現有的API或例如構建的路徑(像一個WPF屬性綁定路徑或XML XPath)從一個到另一個? 「源」(即想要找出路徑的對象)將是葉子對象。構建兩個對象

索引的位置也必須支持。 (例如,Foo.Bar[42].Baz["frog"].Quux)。

這是主要用於錯誤報告 - 我想,讓對象記錄顯示錯誤,他們是在對象模型,而不是僅僅通過自己的類型名稱。 (這很重要,因爲相同類型的對象可能被大量其他對象類型包含,並且修復任何問題所需的用戶操作將根據該位置而變化。)

我可以手動滾動某些東西通過爲每個對象提供對父對象的引用並遞歸地詢問每位父對象如何到達該對象來做到這一點。但在我去之前,我想知道是否有任何現有的/更好的解決方案。 (如果有人忘記更新父鏈接,或者如果一個對象可以通過兩種不同的路徑到達,雖然這應該是罕見的,這是脆弱的。)

+0

可能是這個副本http://stackoverflow.com/questions/66893/tree-data結構中的c-sharp – Illuminati

+1

您的解決方案將是最佳選擇。大多數圖路徑impl。以這種方式工作(儘管它也可能是相反的方式)。我不確定一個對象能夠通過兩條不同的路徑達到什麼問題,這是圖中的自然行爲,並且對Path沒有任何影響。關於更新:您可以跟蹤節點中的版本號(int),並且每次修改節點(子集合)時,都將版本號增加1。現在,在建立路徑時,您可以複製當前版本號,並且在遍歷時,您可以簡單地檢查版本號以進行更新 – Polity

+0

Bumble Bee:不,我不這麼認爲。對象之間的鏈接只是名爲.NET的屬性。這是一種分層的實體仿真模型,如果有幫助的話。 :) – Miral

回答

0

這是如何開始一些非常簡化字,希望這將有助於...

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 
      */ 
     } 
    } 
} 
+0

爲防止無窮大遞歸,請將堆棧參數添加到GetFullTree(...)方法中,並跟蹤所有鏈接。要得到一個精確的孩子的路徑,請使用.FirstOrDefault(x => x.Value == targetObject)。請記住,這個功能是很慢的,所以不要在關鍵代碼 –

+0

我最後做它,因爲我原來描述的使用方法:將父母的列表,每個節點(通過一個共同的基類),並使用元屬性系統(類似於DependencyProperties)來快速訪問對象之間的鏈接。但是我正在勾選這個解決方案,因爲它提供的代碼看起來像是可以解釋原始問題的代碼,儘管使用反射會造成一些性能問題。 – Miral

0

我還沒有看到任何接近的框架。如果沒有「父」屬性,我認爲你正在尋找「圖中最短路徑」(http://en.wikipedia.org/wiki/Pathfinding),但由於複雜性和內存要求不足以保留路徑結構,因此它看起來不是追蹤/錯誤記錄的好選擇。