2012-09-03 66 views
6

不知道這是否是C#4+特定的,但只是注意到了這一點。重載分辨率奇怪

考慮下面的類:

class Base 
{ 
    protected void Foo(object bar, DayOfWeek day) 
    { 
    } 
} 

class Program : Base 
{ 
    protected void Foo(object bar, object baz) 
    { 
    } 

    void Bar(DayOfWeek day) 
    { 
    Foo(new { day }, day); 
    } 
} 

BarFoo的調用,解析爲Foo(object, object)

雖然將其更改爲:在Bar

class Base 
{ 

} 

class Program : Base 
{ 
    protected void Foo(object bar, object baz) 
    { 
    } 

    protected void Foo(object bar, DayOfWeek day) 
    { 
    } 

    void Bar(DayOfWeek day) 
    { 
    Foo(new { day }, day); 
    } 
} 

Foo的調用,解析爲Foo(object, DayOfWeek)

我的理解是,它應該總是像第二個例子那樣解決。

這是一個'錯誤'還是我缺乏理解(或無知)?

更新:

感謝您的答案。正如我發現的那樣,可以使用base.來調用基類中的方法。但是,在混合中添加另一個派生類時,問題會回來。

class Base 
{ 
    protected void Foo(object bar, DayOfWeek day) 
    { 
    } 
} 

class Program : Base 
{ 
    protected void Foo(object bar, object baz) 
    { 
    } 

    void Bar(DayOfWeek day) 
    { 
    base.Foo(new { day }, day); 
    } 
} 

class Derived : Program 
{ 
    void Baz(DayOfWeek day) 
    { 
    base.Foo(new { day }, day); 
    } 
} 

base.通話作品Program,但隨後解析爲Foo(object, object)Derived

如何從Derived調用Foo(object,DayOfWeek)然後不必在Program中創建「冗餘」方法?

回答

6

我認爲對於解析方法調用它首先在它的類中看起來,因爲DayOfWeek可以作爲object類型傳遞,它調用類自己的方法,而不是基類中的方法。

在第二種情況下,方法調用解析爲更具體的類型參數,因此調用Foo(object bar, DayOfWeek day)

從MSDN - Method resolution.

方法在基類不是候選如果在派生 類的任何方法都是適用(第7.5.5.1)。

  • 給定一組可用的候選函數成員,找到該集合中最好的函數成員。
  • 如果該集合只包含一個函數成員,那麼該函數成員是最好的函數成員。
  • 否則,最好功能部件是一個功能構件,其比所有其他函數成員相對於所述給定 參數列表更好,條件是每個功能部件相對於使用中的規則所有 其他函數成員第7.4.2.2節。
  • 如果沒有一個函數成員比所有其他函數成員都好,那麼函數成員調用是 不明確,併發生編譯時錯誤。
+0

我明白它看起來正在嘗試做什麼,但這意味着具有'object'參數的參數將否決所有其他重載,並且無法在基類中調用任何特定的重載,從而使繼承非常有限並容易出錯。 – leppie

+0

謝謝,發現了'法律'。 *如果派生類中的任何方法適用,基類中的方法不是候選*。所以這是正常的...:*( – leppie

+0

@lippie,我正要從相同的鏈接引用:) – Habib

4

你需要籤埃裏克利珀的博客 - 尤其是這篇文章:http://blogs.msdn.com/b/ericlippert/archive/2007/09/04/future-breaking-changes-part-three.aspx

切實雖然,重載解析算法搜索當前類可稱爲過載,只搜索基類如果在當前班級中找不到一種替代方案。

在第一種情況下,Foo(object, object)過載是適用的,因此不執行進一步的搜索。

在第二種情況下,Foo(object DayOfWeek)更好,所以它被使用。

詳細閱讀Eric的文章。

1

我認爲規範是有道理的。派生類程序員不需要知道方法在基類中的實現方式。否則,一個不吉利的人寫了一個比它的基類更不兼容的方法(並且這個人不知道基類的細節),然後調用基類中的方法。

+0

我的問題更多地在於如何實際調用(或特定的)基本實現(根據指定的分辨率)。 'base.'在某些情況下有幫助,但在其他派生類中失敗。 – leppie

1

正如其他人所指出的,問題與重載解析的方式有關。我想補充到:

如果Base.Foopublic,那麼你可以做到這一點去Base.FooDerived(假設Base.Foo是未覆蓋):

((Base)this).Foo(new { day }, day); 

此外,你必須壓倒一切的選項(如果你可以改變Base.Foovirtual)或明確隱藏Base.FooProgram所以當你打電話base.FooDerived,但它仍然呼籲Base.Foo

class Program : Base 
{ 
    protected void Foo(object bar, object baz) 
    { 
    } 

    protected new void Foo(object bar, DayOfWeek baz) 
    { 
    base.Foo(bar, baz); 
    } 

    void Bar(DayOfWeek day) 
    { 
    base.Foo(new { day }, day); 
    } 
} 

作爲附帶說明: 一般,派生類提供重載用更具體參數類型比它們的基類重載(例如Object.Equals(object),String.Equals(string))。

即使有更少的(或相等)特定參數類型的情況下,他們會要麼倍率基方法(例如Object.Equals(object)String.Equals(object) - >覆蓋Object.Equals(object)),或他們只是給它一個不同的方法的名稱。

+0

「公共」上有趣的發現。我想知道爲什麼即使我在派生類中,它受到保護時仍然無法工作。 – leppie

+1

@leppie因爲編譯器沒有智能知道'((Base)this)'引用的對象是'Derived'。據它所知,它可能指的是'Program2'的一個實例,它也來自'Base'。如果是這樣的話,您將不會被允許在Derived中訪問'Program2.Foo'。請參閱[這裏](http://msdn.microsoft.com/en-us/library/s9zta243.aspx)。 –