2014-10-31 79 views
1

我目前正在C#中實現訪問者模式,以在大學進行演講。目前它工作正常,但我有一個問題需要我們去做。 目前,我有這樣的事情: 作爲參數傳遞給重載方法時區分基類和派生類

public class TreeStructure<T> 
{ 
    protected TreeStructure<T> _left; 
    protected TreeStructure<T> _right; 
    public T myValue; 

    public TreeStructure(TreeStructure<T> left, T value ,TreeStructure<T> right) 
    { 
    this._left = left; 
    this._right = right; 
    this.myValue = value; 
    } 
    public virtual void InOrder(MyVisitor<T> visitor) 
    { 
    if(!IsEmpty(this._left)) 
    { 
     _left.InOrder(visitor); 
    } 
    visitor.Visit(this); 
    if(!IsEmpty(this._right)) 
    { 
     _right.InOrder(visitor); 
    } 
    } 
    protected bool IsEmpty(TreeStructure<T> node) 
    { 
    return node == null; 
    } 
} 

public class SpecialTree<T> : TreeStructure<T> 
    { 
    public SpecialTree(TreeStructure<T> left, T value, TreeStructure<T> right) 
    { 
     this._left = left; 
     this._right = right; 
     this.myValue = value; 
    } 

    public override void InOrder(MyVisitor<T> visitor) 
    { 
     if(!IsEmpty(this._left)) 
     { 
     _left.InOrder(visitor); 
     } 
     visitor.Visit(this); 
     Console.WriteLine("Hallo"); 
     if(!IsEmpty(this._right)) 
     { 
     _right.InOrder(visitor); 
     } 
    } 
    } 

現在,我們應該實施一些遊客。這是沒有問題的,直到我們應該區分一位遊客的樹種。所以,我現在有一個訪問者這樣看:

public interface MyVisitor<T> 
    { 
    void Visit(TreeStructure<T> tree); 
    } 

    public class CountVisitor<T> : MyVisitor<T> 
    { 
    public int count { get; set;} 
    public CountVisitor() 
    { 
     count = 0; 
    } 

    public void Visit(TreeStructure<T> tree) 
    { 
     count++; 
    } 

    public void Visit(SpecialTree<T> specialTree) 
    { 
     count+=2; 
    } 
    } 

所以問題還是,這顯然是工作在Java中,但不是在C#我有問題。 Visit()方法SpecialTree永遠不會被調用。所以我知道我可以檢查像這裏的對象類型:What is the best way to differentiate between derived classes of a base class? 但有人可以向我解釋,爲什麼它不適用於重載的方法?或者我在這裏有一個錯誤?

編輯:這裏是樹的一個測試初始化​​:

TreeStructure<int> tree1 = new TreeStructure<int>(new TreeStructure<int> (null,3,null),1,new SpecialTree<int>(null,2,null)); 
     CountVisitor<int> visitor2 = new CountVisitor<int>(); 
     tree1.InOrder(visitor2); 
     Console.WriteLine("Nodecount: " + visitor2.count.ToString()); 
+1

什麼'MyVisitor '? – Dirk 2014-10-31 10:50:56

+3

一個簡短但完整的例子證明了這個問題(最好沒有別的 - 它根本不需要使用樹木或訪客)真的會有所幫助。我強烈*懷疑Java中的等效代碼不起作用,但我們確實需要看到一個完整的示例以告訴... – 2014-10-31 10:53:38

+1

哦,對不起,我忘了,它只是一個自制界面。我會將其添加到開始帖子。 – 2014-10-31 10:54:07

回答

0

這是高度依賴於MyVisitor<T>實施。在你的評論中你說它是一個界面。你確定這是整個例子嗎?您在問題中顯示的代碼不會編譯,因爲SpecialTree<T>未調用其父構造函數。

如果MyVisitor<T>未聲明Visit(SpecialTree<T> specialTree),由InOrder完成的接口調度永遠不會達到SpecialTree的情況。

更新

考慮更新的初始化代碼,並假定該接口被聲明爲這樣的:

interface MyVisitor<T> 
{ 
    void Visit(TreeStructure<T> treeStructure); 
    void Visit(SpecialTree<T> tree); 
} 

CountVisitor<T>給了我4的結果,符合市場預期。

它爲什麼不起作用?

我會試着解釋爲什麼你的例子不起作用。類別SpecialTree中的方法InOrder具有類型MyVisitor的參數。當方法VisitMyVisitor上被調用時,沒有超載需要SpecialTree,但是有一個過載需要TreeStructure。在尋找正確的調用方法時,C#不考慮接口的實現。因此,將調用TreeStructure作爲參數的方法,而不是接受SpecialTree參數的方法。

如果我不在乎它是什麼樣的樹結構呢?

在回答您的評論,你可以創建一個將樹的所有實現調用的方法:

class DoNotCareVisitor<T> : MyVisitor<T> { 
    void GenericVisit(TreeStructure<T> tree) { 
     //.. do whatever 
    } 
    public void Visit(TreeStructure<T> tree) { 
     GenericVisit(tree); 
    } 
    public void Visit(SpecialTree<T> tree) { 
     GenericVisit(treeStructure); 
    } 
} 
+0

OP已經用接口聲明更新了他的代碼,並且你是對的,它只有一個超載,所以更新它到你的例子將工作(我認爲)。 – Chris 2014-10-31 11:02:55

+0

但是後來我有一個遊客的問題,不需要區分基類和派生類!? – 2014-10-31 11:04:52

+0

@RolandM已更新回答 – Bas 2014-10-31 11:10:25

0

你的方法Visit(SpecialTree<T> specialTree)不是你MyVisitor<T>接口(順便說一句的一部分 - 它是一個很好的做法,用大寫字母I作爲前綴,因此IMyVisitor<T>會提高可讀性),所以沒有理由將其調用。

換句話說,當你打電話給visitor.Visit(this)時,你的訪問變量的類型是MyVisitor<T>,所以此時編譯器並不知道你打算在那裏使用一個特定的類,這可能會有更好的重載你的功能。

最簡單的方法 - 假設你不是故意寫這樣的(並使其工作,你需要使用運行時類型檢查) - 將是添加第二個方法到你的界面,與以下簽名:void Visit(SpecialTree<T> specialTree)

0

所有你需要做的是檢查樹之間的區別在public void Visit(TreeStructure<T> tree),像這樣:

public class CountVisitor<T> : MyVisitor<T> 
{ 
    public int count { get; set;} 
    public CountVisitor() 
    { 
     count = 0; 
    } 

    public void Visit(TreeStructure<T> tree) 
    { 
     if (tree is SpecialTree<T>) 
      count += 2; 
     else 
      count++; 
    } 
} 
0

你不需要的兩種棵樹類型之間進行區分。訪問者的目的是遍歷結構並以一般方式收集某種度量。

訪問者應該是這樣的:

public class CountVisitor<T> : MyVisitor<T> 
{ 
    public int count { get; set;} 

    public CountVisitor() 
    { 
     count = 0; 
    } 

    public void Visit(TreeStructure<T> tree) 
    { 
     count++; 
    } 
} 

就是這樣。

這裏有您需要在您的SpecialTree<T>類的事:

public override void InOrder(MyVisitor<T> visitor) 
{ 
    if(!IsEmpty(this._left)) 
    { 
     _left.InOrder(visitor); 
    } 
    visitor.Visit(this); 
    visitor.Visit(this); 
    if(!IsEmpty(this._right)) 
    { 
     _right.InOrder(visitor); 
    } 
} 

因此,基本上,它在SpecialTree<T>類重寫調用.Visit方法兩次的目的。

現在,當我運行測試代碼,我得到:

Nodecount: 4 
+0

是不是這樣,訪問對象不關心自己的類型,訪問者應該檢查?或者是這樣,訪問者不應該在意嗎? – 2014-10-31 11:25:08

+0

@RolandM - 使用訪問者的要點是它與它所遍歷的對象結構是分開的。所以'CountVisitor'類的唯一目的是對節點進行計數。它不需要知道'SpecialTree'有一個特殊的屬性,每個節點都有兩個計數。如果明天你決定「SpecialTree」中的每個節點都值3,那麼你不需要改變「CountVisitor」 - 你只需要在'InOrder'中爲訪問者增加一個額外的呼叫。這就是它所屬的地方。 – Enigmativity 2014-10-31 11:29:40