3 回答

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

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

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