2014-01-21 35 views
2

我正在開發一個應用程序來讀取Beckhoff PLC的值。 PLC有一個.net庫,我可以用它來連接我的程序和PLC。用C#讀取遞歸集合

PLC上的每個變量都是TcAdsSymbolInfo類型的符號。這個類有幾個成員,其中一個是TcAdsSymbolInfo的集合,等等......你可以看到這是怎麼回事。基本上我有一個樹形結構,有很多符號,每個符號都可以有子符號。 Beckhoff

我想要做的是,爲每個符號讀取所有的子符號,這個工作如果我手工做,只是嘗試看第一個子符號級別,但我真的需要所有這些。

我試圖創建接收一個TcAdsSymbolInfo和結束通話本身,而是這將引發一個堆棧溢出異常

private void ReadSubsymbols(TcAdsSymbolInfo t) 
    { 
     if (t.SubSymbolCount > 0) 
     { 
      foreach (TcAdsSymbolInfo subsymbol in t.SubSymbols) 
      { 
       if (!symbols.ContainsKey(subsymbol.Name)) 
        symbols.Add(subsymbol.Name, subsymbol); 
       try 
       { 
        ReadSubsymbols(subsymbol); 
       } 
       catch (Exception Ex) 
       { 
        Console.WriteLine(Ex.ToString() + " - " + Ex.Message); 
       } 
      } 
     } 
    } 

符號就是在那裏我存儲符號名稱和符號字典的遞歸函數本身。

請嘗試從PLC部分摘要,因爲我認爲這只是一個純粹的邏輯/編程問題。在與PLC進行通信或讀取和寫入數值時,我沒有任何問題。唯一的問題是閱讀這個結構。

我可以使用一段時間或任何其他類型的循環做任何seguegestion?任何不會拋出異常的東西?

在此先感謝。

+0

你或許應該尋找樹的遍歷算法。 – Magus

回答

1

這是一個簡單的樹行走。基本邏輯是從根開始:

  • 如果根爲空,樹是空的:我們完成了。
  • 如果root不爲null,
    • 訪問它
    • 然後遞歸參觀它的每個孩子。

簡單。大多數情況下:D當你在圖中遇到循環時(例如,當一個子節點鏈接回它自己的父節點時),你會遇到麻煩的地方。如果你有周期,你必須跟蹤你訪問過的節點(就像我的例子那樣)並且檢查週期(我的例子不這麼做)。

鑑於一類是這樣的:

class SymbolInfo 
{ 
    public string Name { get ; set ; } 
    public SortedSet<SymbolInfo> Subsymbols { get ; set ; } 

    public SymbolInfo(string name) 
    { 
    this.Name = name ; 
    this.Subsymbols = new SortedSet<SymbolInfo>(new SymbolInfo.Comparer()) ; 
    } 

    public override string ToString() 
    { 
    return this.Name ?? "-null-" ; 
    } 

    private class Comparer : IComparer<SymbolInfo> 
    { 
    public int Compare(SymbolInfo x , SymbolInfo y) 
    { 
     return string.Compare(x.Name,y.Name,StringComparison.InvariantCultureIgnoreCase) ; 
    } 
    } 
} 

樹步行看起來是這樣的:

public static IEnumerable<string> TreeWalk(SymbolInfo root , List<SymbolInfo> visited) 
{ 
    if (root != null) 
    { 
    visited.Add(root) ; 
    yield return string.Join(" -> " , visited) ; 
    foreach (SymbolInfo child in root.Subsymbols) 
    { 
     foreach (string childPath in TreeWalk(child , visited)) 
     { 
     yield return childPath ; 
     } 
    } 
    visited.RemoveAt(visited.Count-1) ; 
    } 
} 

而鑑於這樣構建的樹:

private static SymbolInfo LoadTree() 
{ 
    SymbolInfo a = new SymbolInfo("A") ; 
    SymbolInfo b = new SymbolInfo("B") ; 
    SymbolInfo c = new SymbolInfo("C") ; 
    SymbolInfo d = new SymbolInfo("D") ; 
    SymbolInfo e = new SymbolInfo("E") ; 
    SymbolInfo f = new SymbolInfo("F") ; 
    SymbolInfo g = new SymbolInfo("G") ; 
    SymbolInfo h = new SymbolInfo("H") ; 
    SymbolInfo i = new SymbolInfo("I") ; 

    a.Subsymbols.Add(b) ; 
    a.Subsymbols.Add(c) ; 
    a.Subsymbols.Add(d) ; 

    b.Subsymbols.Add(e) ; 

    c.Subsymbols.Add(f) ; 
    c.Subsymbols.Add(g) ; 

    f.Subsymbols.Add(h) ; 
    f.Subsymbols.Add(i) ; 

    return a ; 
} 

我們可以調用它像這樣:

SymbolInfo root = LoadTree() ; 

foreach (string path in TreeWalk(root , new List<SymbolInfo>())) 
{ 
    Console.WriteLine(path) ; 
} 

要產生以下的輸出:

A 
A -> B 
A -> B -> E 
A -> C 
A -> C -> F 
A -> C -> F -> H 
A -> C -> F -> I 
A -> C -> G 
A -> D 
+0

感謝您的幫助。我遵循你的樣本,並能解決我的問題。 –

2

你可以使用迭代樹遍歷。 但是在嘗試這個之前,請確保堆棧溢出是由於遞歸變得太深。很有可能這是一個編碼錯誤允許遞歸無限下降。

你可以不喜歡下面的僞代碼:

Push root node to stack 

While (stack is not empty) 
{ 
    current node = pop from stack 
    process current node (and other processing goes here) 

    add all children with nodes to stack 
} 

見這個例子中,解決了類似的問題與目錄: http://msdn.microsoft.com/en-us/library/bb513869.aspx

+2

另外到什麼喬治說,你可能要注意你是否真的有隻,還是在現實中是一個週期性的圖形(又名有其祖先節點中的一個作爲子節點),這也解釋了一棵樹堆棧耗盡。 – elgonzo

+2

這是一個很好的迭代示例。而且,我同意,您必須確保堆棧溢出是遞歸調用的直接結果。否則,迭代方法仍將無限期地運行。機會在某處有一個循環。您可以通過存儲和檢查訪問節點的引用來避免循環。 – Xenolightning

0
public IEnumerable<TcAdsSymbolInfo> GetSymbols(IEnumerable<TcAdsSymbolInfo> set) 
{ 
    if (!set.Any()) 
     return Enumerable.Empty<TcAdsSymbolInfo>(); 

    return set.SelectMany(sym => GetSymbols(sym.SubSymbols))); 
} 

您可以將結果然後轉換成你的字典並檢查被騙等。