2010-08-05 44 views
4

好的,我有一些從基類派生的一些不同的對象,我把它們放在一個列表中。我想遍歷列表並將每個方法推送到一個方法。我對每個人的簽名都有單獨的方法,但編譯器正在抱怨。有人能解釋爲什麼嗎?這是一個使用泛型的機會,如果是這樣,如何?C#派生類,重載決議

class Base { } 
class Level1 : Base { } 
class Level2 : Level1 { } 

...

List<Base> oList = new List<Base>(); 
oList.Add(new Level1()); 
oList.Add(new Level2()); 

...

... 
foreach(Base o in oList) 
{ 
    DoMethod(o); 
} 

...

void DoMethod(Level1 item) { } 
void DoMethod(Level2 item) { } 

我在做什麼錯?

+0

我不確定它很清楚,我認爲如果'DoMethod'實際上是Base,Level1,Level2上的一種方法,那麼會有誤解?你能澄清嗎? – 2010-08-05 20:42:45

+0

@ Will P 對,對不起;我在外部處理對象,因此,不,DoMethod不是這些類的成員。 – 2010-08-05 20:49:32

回答

6

過載已解決,編譯時間爲 - 並且您沒有DoMethod(Base item)方法 - 因此無法解析調用。離開列表和循環出來的東西,你寫有效:

Base o = GetBaseFromSomewhere(); 
DoMethod(o); 

編譯器必須找到一個名爲DoMethod方法,該方法適用於Base類型的一個參數。沒有這種方法,因此失敗了。

有幾個選擇這裏:

  • 作爲馬科斯說,你可以使用C#4動態類型使C#編譯器在執行所適用超載使用實際類型的對象時間o指的是。
  • 可以使用Visitor Pattern有效地獲得雙重分派(我從來沒有真正喜歡這個)
  • 您可以使用asis

    Level1 x = o as Level2; 
    if (x != null) 
    { 
        DoMethod(x); // Resolves to DoMethod(Level1) 
    } 
    else 
    { 
        Level2 y = o as Level2; 
        if (y != null) 
        { 
         DoMethod(y); // Resolves to DoMethod(Level2) 
        } 
    } 
    

    再次,這是很醜陋

  • 重新設計你正在做的事情,以便能夠使用正常的繼承,如果可能的話
2

Overloading am ethod使用變量的靜態類型而不是運行時類型。

你想使用繼承和覆蓋

class Base { public virtual void DoMethod() { /* ... */ } } 
class Level1 : Base { public override void DoMethod() { /* ... */ } } 
class Level2 : Level1 { public override void DoMethod() { /* ... */ } } 
1

在編譯時間而不是運行時中確定調用哪個方法,因此編譯器無法知道要調用哪一個。您有2個選項: 切換對象的類型並調用適當的方法,或者如果您使用的是.NET 4,請使用 ,使用動態類型。

foreach(dynamic o in oList) 
{ 
    DoMethod(o); 
} 
+0

使用'dynamic'會對性能產生一些影響。更重要的是,我認爲這是一個良好的老式多態的地方。 – 2010-08-05 20:40:28

+0

@Steven:我們當然不知道。把'DoMethod'的邏輯放在'Level1'和'Level2'中可能是完全不合適的。 – 2010-08-05 20:42:29

+0

@Jon:當然,我考慮的一種可能性是這可能需要某種形式的雙重派遣。不過,我們應該能夠在沒有'switch'語句的情況下解決這個問題。 – 2010-08-05 21:03:13

1

您沒有DoMethod(Base item)方法。重載不是多態的。這通常是通過使用虛擬方法來完成:

class Base { 
    public virtual void DoMethod() {...} 
} 
class Level1 : Base { 
    public override void DoMethod() {...} 
} 
// etc.. 

foreach(Base o in oList) 
{ 
    o.DoMethod(); 
} 
1

在你的foreach循環,o的類型爲Base,而本DoMethod重載的需要Base實例。如果可能的話,你應該移到DoMethodBase和覆蓋它的兩個子類:

public class Base 
{ 
    public virtual void DoMethod() { ... } 
} 
1

要在馬克的回答擴大,DoMethod應該是基本的虛擬方法,你在列表中的每個項目調用。

+0

漢斯和馬克用幾分鐘的時間打敗了我,所以信用就交給了他們。我也扔了他們upvotes。 – 2010-08-05 21:04:29

0

我不知道所有的細節,但如果這是一種情況,它確實不適合繼承,您可以使用接口。

聲明接口,在每個類上實現它,然後你就可以直接轉換到接口並從那裏運行該函數。我的C#是有點不穩,但類似,

Interface IMethodizable 
{ 
    void DoMethod(); 
} 

class Level1 : IMethodizable { 
    void DoMethod(){ 
    //insert code here 
    } 
} 

class Level2 : IMethodizable { 
    void DoMethod(){ 
    //insert code here 
    } 
} 

這工作得特別好,如果類具有共同的唯一的事情是方法。這與在基類中使用虛擬化方法並重寫它非常相似。所以這種模式只有在不應該繼承的情況下才會更好,否則DoMethod也必須在其他不從基類繼承的對象上運行,等等。

+0

這也適用於基類,但對於OP,它可能不合適(請參閱上面的討論這個答案)。我其實有一個類似的問題,在我的情況下,它是不合適的 - 我的方法將取決於調用類,所以這是實現應該在哪裏。我可能不得不使用一個大的switch語句,但是聽起來我應該閱讀動態內容,因爲我足夠幸運能夠對C#4進行編碼。(也就是說,對我來說,這是一個很好的例子,Google在Google上查找信息問題和討論堆棧溢出!) – winwaed 2011-01-09 22:37:33