3 回答

TA貢獻2019條經驗 獲得超9個贊
這里需要注意的一件事是,每次都會調用覆蓋的版本。將替代更改為:
public override void MyMethod(string s = "bbb")
{
Console.Write("derived: ");
base.MyMethod(s);
}
輸出為:
derived: bbb
derived: aaa
類中的方法可以執(zhí)行以下一項或多項操作:
它定義了其他代碼調用的接口。
它定義了一個在調用時執(zhí)行的實現(xiàn)。
它可能不會兩者都做,因為抽象方法只會做前者。
在BBB調用MyMethod()調用方法定義在AAA。
由于中存在覆蓋BBB,因此調用該方法將導致實現(xiàn)BBB被調用。
現(xiàn)在,中的定義AAA將兩件事告知調用代碼(嗯,還有一些無關緊要的地方)。
簽名void MyMethod(string)。
(對于支持該語言的語言)單個參數的默認值為"aaa",因此,MyMethod()如果在找不到該方法匹配的形式的形式的代碼時MyMethod(),可以用對MyMethod(“ aaa”)的調用來替換它。
因此,這就是調用的BBB作用:編譯器看到對的調用MyMethod(),未找到方法,MyMethod()但確實找到了方法MyMethod(string)。它還可以看到在定義它的地方有一個默認值“ aaa”,因此在編譯時它將其更改為對的調用MyMethod("aaa")。
從內部BBB,即使在中被覆蓋,也AAA被認為AAA是定義方法的地方BBB,以便可以覆蓋它們。
在運行時,MyMethod(string)使用參數“ aaa”調用。因為存在一個覆蓋的形式,所以該形式被調用,但是不使用“ bbb”來調用它,因為該值與運行時實現(xiàn)無關,而與編譯時定義無關。
添加this.更改將檢查哪個定義,從而更改調用中使用的參數。
編輯:為什么這對我來說似乎更直觀。
就個人而言,由于我所說的是直觀的東西,因此只能是個人的,我發(fā)現(xiàn)它更直觀,原因如下:
如果我正在編碼,BBB那么無論是調用還是覆蓋MyMethod(string),我都認為這是“正在做的AAA事情”,這是BBB在“正在做的AAA事情” 上做的事情,但實際上AAA都是在做事情。因此,無論是調用還是覆蓋,我都將意識到它就是AAA定義的事實MyMethod(string)。
如果我要調用使用過的代碼BBB,我會想到“使用BBB東西”。我可能不太了解最初是在中定義的AAA,因此我可能會認為這只是實現(xiàn)細節(jié)(如果我也沒有使用AAA附近的接口)。
編譯器的行為符合我的直覺,這就是為什么當初讀這個問題時,我覺得Mono有一個錯誤。經考慮,我看不到任何一個如何比另一個更好地實現(xiàn)指定的行為。
盡管如此,盡管如此,在保持個人水平的同時,我絕不會將可選參數與抽象,虛擬或重寫方法一起使用,并且如果要覆蓋其他人使用的參數,我會匹配它們。

TA貢獻1783條經驗 獲得超4個贊
您可以通過以下方式消除歧義:
this.MyMethod();
(在中MyMethod2())
它是否是一個漏洞是棘手的。它看起來確實不一致。Resharper會警告您根本不要更改覆蓋中的默認值,如果這樣做會有所幫助; p當然,resharper 還會告訴您this.多余,并愿意為您刪除它……這會改變行為-因此,resharper也并不完美。
看起來確實有可能成為編譯器錯誤,我將授予您。我需要看真仔細,以確保...這里的埃里克·當你需要他,是吧?
編輯:
這里的重點是語言規(guī)范;讓我們看看第7.5.3節(jié):
例如,用于方法調用的候選集不包括標記為重寫的方法(第7.4節(jié)),并且如果派生類中的任何方法均適用(第7.6.5.1節(jié)),則基類中的方法也不是候選者。
(實際上,第7.4節(jié)顯然沒有override考慮方法)
這里有一些沖突...。它指出如果派生類中有適用的方法,則不使用基本方法-這將導致我們進入派生方法,但與此同時,它表示標記override的方法沒有考慮過的。
但是,第7.5.1.1節(jié)指出:
對于在類中定義的虛擬方法和索引器,參數列表是從函數成員的最特定的聲明或重寫中選取的,從接收方的靜態(tài)類型開始,并搜索其基類。
然后第7.5.1.2節(jié)解釋了在調用時如何評估值:
在函數成員調用(第7.5.4節(jié))的運行時處理期間,參數列表的表達式或變量引用按從左到右的順序求值,如下所示:
...(剪斷)...
當從具有相應可選參數的函數成員中省略參數時,將隱式傳遞函數成員聲明的默認參數。由于這些參數始終是恒定的,因此它們的評估不會影響其余參數的評估順序。
這明確突出表明它正在查看參數列表,該列表先前在§7.5.1.1中定義為來自最具體的聲明或重寫。似乎這是第7.5.1.2節(jié)中提到的“方法聲明”是合理的,因此傳遞的值應該是從最派生的到靜態(tài)類型。
這可能表明:csc有一個錯誤,并且應該使用派生版本(“ bbb bbb”),除非它被限制(通過base.或強制轉換為基本類型)查看基本方法聲明(第7.6.8節(jié))。 )。

TA貢獻1829條經驗 獲得超9個贊
在我看來,這似乎是個錯誤。我相信它已被明確指定,并且其行為應與您使用顯式this前綴調用該方法的行為相同。
我已將示例簡化為僅使用單個虛擬方法,并顯示了調用了哪種實現(xiàn)以及參數值是什么:
using System;
class Base
{
public virtual void M(string text = "base-default")
{
Console.WriteLine("Base.M: {0}", text);
}
}
class Derived : Base
{
public override void M(string text = "derived-default")
{
Console.WriteLine("Derived.M: {0}", text);
}
public void RunTests()
{
M(); // Prints Derived.M: base-default
this.M(); // Prints Derived.M: derived-default
base.M(); // Prints Base.M: base-default
}
}
class Test
{
static void Main()
{
Derived d = new Derived();
d.RunTests();
}
}
因此,我們需要擔心的是RunTests中的三個調用。前兩個調用的規(guī)范的重要部分是7.5.1.1節(jié),該節(jié)討論在查找相應參數時要使用的參數列表:
對于在類中定義的虛擬方法和索引器,參數列表是從函數成員的最特定的聲明或重寫中選取的,從接收方的靜態(tài)類型開始,并搜索其基類。
以及7.5.1.2節(jié):
當從具有相應可選參數的函數成員中省略參數時,將隱式傳遞函數成員聲明的默認參數。
“相應的可選參數”是將7.5.2關聯(lián)到7.5.1.1的位。
對于M()和this.M(),參數列表都應該是Derived接收者的靜態(tài)類型Derived,實際上,您可以告訴編譯器將其視為編譯早期的參數列表,就好像您在中都將參數強制設置為一樣。調用失敗-因此調用要求參數在中具有默認值,但隨后將其忽略!Derived.M()M()Derived
確實,情況變得更糟:如果您在中提供參數的默認值,Derived但在中將Base其設為必需,則調用 M()最終將null用作參數值。如果沒有別的,我想證明這是一個錯誤:該null值不能來自任何有效的地方。(這是null由于它是string類型的默認值;它始終僅將默認值用于參數類型。)
與base.M(),它表示,該規(guī)范交易部分7.6.8 ,以及作為非虛擬行為,表達被視為((Base) this).M(); 因此,使用基本方法確定有效參數列表是完全正確的。這意味著最后一行是正確的。
只是為了使想要查看上述真正奇怪的錯誤的人更輕松,其中使用了未在任何地方指定的值:
using System;
class Base
{
public virtual void M(int x)
{
// This isn't called
}
}
class Derived : Base
{
public override void M(int x = 5)
{
Console.WriteLine("Derived.M: {0}", x);
}
public void RunTests()
{
M(); // Prints Derived.M: 0
}
static void Main()
{
new Derived().RunTests();
}
}
- 3 回答
- 0 關注
- 541 瀏覽
添加回答
舉報