第 85 講:C# 2 之訪(fǎng)問(wèn)器上的訪(fǎng)問(wèn)修飾符
今天我們繼續(xù)來(lái)看語(yǔ)法新拓展。
Part 1 引例
盡管目前 C# 的封裝機(jī)制做得已經(jīng)很優(yōu)秀了,但仍然有極少數(shù)情況下,目前的語(yǔ)法做不到。
假設(shè)我有一個(gè)形狀的抽象類(lèi)型 Shape
,然后派生出 Circle
圓形、Rectangle
矩形等等形狀的類(lèi)型。
假設(shè)我有一個(gè)屬性 Area
表示面積,用來(lái)計(jì)算面積數(shù)值。從另外一個(gè)層面,我們可以通過(guò)構(gòu)造器初始化 Area
來(lái)達(dá)到賦值的過(guò)程,就不用運(yùn)行時(shí)使用屬性的時(shí)候才來(lái)計(jì)算了:
可以看到,這樣的實(shí)現(xiàn)機(jī)制可以更加靈活地使用屬性:我們?yōu)?Area
屬性的 setter 設(shè)置了 protected
訪(fǎng)問(wèn)修飾符后,這個(gè)方法就只能用在當(dāng)前類(lèi)和它的派生類(lèi)型里使用賦值操作了。
在原來(lái),我們?nèi)绻@么書(shū)寫(xiě)代碼,就非常不合理:因?yàn)?setter 是 public
的,所以你完全可以隨便篡改數(shù)值。我們擁有了限制 setter 的訪(fǎng)問(wèn)修飾符,就可以多樣化使用屬性達(dá)到更深層次的封裝效果了。
Part 2 語(yǔ)法規(guī)則和細(xì)節(jié)
可以從前文的例子里看出,getter 和 setter 不必只通過(guò)屬性的訪(fǎng)問(wèn)修飾符單純作限制,而這個(gè)語(yǔ)法可以允許我們直接在 get
和 set
關(guān)鍵字上訪(fǎng)問(wèn)修飾符。那么,這個(gè)語(yǔ)法有什么細(xì)節(jié)上的規(guī)則和限制呢?
2-1 不能同時(shí)使用兩次訪(fǎng)問(wèn)修飾符到兩個(gè)訪(fǎng)問(wèn)器上
試想一下這樣書(shū)寫(xiě)的代碼是什么意思:
訪(fǎng)問(wèn)器
即直接把這個(gè)訪(fǎng)問(wèn)修飾符放在屬性上去。這不是一樣的嗎?
2-2 訪(fǎng)問(wèn)器設(shè)置的訪(fǎng)問(wèn)修飾符必須小于屬性本身的訪(fǎng)問(wèn)修飾符
這個(gè)說(shuō)法有點(diǎn)不好理解,舉個(gè)例子你就懂了。
假設(shè)我給屬性設(shè)置的是 protected
訪(fǎng)問(wèn)修飾符,那么它的 getter 或者 setter 的訪(fǎng)問(wèn)修飾符上就不能使用 public
這種修飾符。因?yàn)?public
意味著任何時(shí)候都可以使用,而 protected
僅用于派生類(lèi)型上。
假設(shè)這個(gè)語(yǔ)法合理的話(huà):
從兩個(gè)角度來(lái)說(shuō),public get
都沒(méi)有意義:
第一,public
比 protected
范圍還大,那么這么限制顯然就沒(méi)有意義,因?yàn)閷傩员旧砭拖拗扑目稍L(fǎng)問(wèn)級(jí)別是只能派生類(lèi)型和自己類(lèi)型里可以看到。那么你設(shè)置 public
最多也就只能在這個(gè)范圍里面隨便使用這個(gè) getter。說(shuō)白了,getter 的訪(fǎng)問(wèn)級(jí)別是受到屬性這個(gè)語(yǔ)法本身的制約的。可問(wèn)題是這么做不就跟 protected
一樣嗎?那么寫(xiě)不寫(xiě)就無(wú)所謂了,因此 public get
沒(méi)有意義;
第二,public
public get
也沒(méi)有意義。
總之,設(shè)置了一個(gè)超過(guò)屬性訪(fǎng)問(wèn)級(jí)別的訪(fǎng)問(wèn)修飾符到訪(fǎng)問(wèn)器上,是沒(méi)有意義的。因此,C# 并不允許你這么做。
2-3 訪(fǎng)問(wèn)器只有一個(gè)的時(shí)候,不能這樣加訪(fǎng)問(wèn)修飾符
這個(gè)比較好理解。如果一個(gè)屬性單純只有 getter 或者 setter 的時(shí)候,那么你往 getter 或 setter 上加訪(fǎng)問(wèn)修飾符的邏輯,就完全和在屬性上直接修改訪(fǎng)問(wèn)修飾符是等價(jià)的。所以,你這種時(shí)候去追加訪(fǎng)問(wèn)修飾符沒(méi)有任何意義,因此 C# 也不允許我們這么做。
比如這樣的語(yǔ)法,就是不合適的。
Part 3 帶不同修飾符的訪(fǎng)問(wèn)器的屬性的繼承和接口實(shí)現(xiàn)機(jī)制
屬性的語(yǔ)法到現(xiàn)在算是有了一個(gè)比較多樣化的升級(jí),那么它能否用于繼承和派生呢?答案是可以的。不過(guò)這里就需要注意一點(diǎn)細(xì)節(jié)了。下面我們來(lái)說(shuō)說(shuō),跟繼承派生相關(guān)的這種語(yǔ)法的用法和細(xì)節(jié)。
3-1 不能在提供重寫(xiě)的虛屬性和抽象屬性里出現(xiàn) private
訪(fǎng)問(wèn)器
考慮一種情況。假設(shè)我有這樣兩個(gè)類(lèi)型 C
和 D
:
先不考慮 setter 怎么實(shí)現(xiàn)代碼,只關(guān)注于 setter 的訪(fǎng)問(wèn)修飾符的話(huà),你覺(jué)得,這個(gè) setter 的 private
修飾符合理嗎?可能你覺(jué)得,好像還蠻合理的,因?yàn)槟阒匦屡缮蛯?shí)現(xiàn)是在派生類(lèi)型里,而抽象屬性上的 setter 有 private
,也只是邏輯上的一個(gè)限制。
實(shí)際上,不是這樣。屬性的任務(wù)是給字段提供賦值和取值的模式的。那么,屬性就只有三種狀態(tài):
只有 getter:這種屬性多用于模擬和代替表示一個(gè)無(wú)參方法,比如
Shape
類(lèi)型給一個(gè)Area
方法來(lái)表示套公式以計(jì)算面積;只有 setter:這種屬性多用于規(guī)劃和隱式觸發(fā)事件(字段只有賦值操作,而 setter 可以封裝賦值和觸發(fā)事件的邏輯);
getter 和 setter 都有:這種屬性就更不用我多說(shuō)了,到處都有用。
而如果出現(xiàn) private
修飾符的訪(fǎng)問(wèn)器,就顯得非常沒(méi)有意義。private get
修飾的話(huà),這種一般只考慮出現(xiàn)在類(lèi)似剛才 Area
這種,只有 getter 的屬性。那么這種情況和剛才說(shuō)過(guò)的“只有一個(gè)訪(fǎng)問(wèn)器的屬性”是一樣的,因此它還不如將其定義為 private
的屬性;而 private set
的話(huà),它用作給對(duì)象的字段賦值。而本身字段的級(jí)別就比較低,低到什么程度呢?低到要么 private
要么 protected
,internal
和 protected internal
也有但很少。你想想,我在派生類(lèi)型上重寫(xiě)屬性的 setter 的時(shí)候,如果 private
修飾符允許的話(huà),那么為什么我不直接給這個(gè)字段賦值呢?字段本身就已經(jīng)低到內(nèi)部才能看到,而我卻繞了一個(gè)步驟用屬性去賦值,結(jié)果屬性的 setter 說(shuō)不定訪(fǎng)問(wèn)級(jí)別比直接給字段賦值還要低(比如我原本字段是在 C
類(lèi)型的,是 protected
修飾的,我有一個(gè) D
類(lèi)型從 C
類(lèi)型派生,結(jié)果屬性的賦值器卻用 private
給這個(gè)字段賦值)。你說(shuō),這有沒(méi)有起到封裝的目的和作用?顯然沒(méi)有嘛。
所以,在重寫(xiě)屬性(可能基類(lèi)型是抽象屬性,或者虛屬性)的時(shí)候,都不允許出現(xiàn) private
修飾的訪(fǎng)問(wèn)器。
3-2 不可變更從基類(lèi)型拿下來(lái)的待重寫(xiě)屬性的訪(fǎng)問(wèn)器的訪(fǎng)問(wèn)修飾符
這句話(huà)有點(diǎn)繞。舉個(gè)例子就明白了。
我有這么一個(gè)屬性,它是帶有兩個(gè)索引器需要我們重寫(xiě),一個(gè)是 getter,一個(gè)是 protected
修飾過(guò)的 setter。這種屬性在重寫(xiě)的時(shí)候,我們不能去更改 setter 的 protected
訪(fǎng)問(wèn)修飾符。因?yàn)槟憧赡軙?huì)把訪(fǎng)問(wèn)修飾符的范圍改小或改大。改得更小則破壞了面向?qū)ο蟮脑O(shè)計(jì)規(guī)則,再次派生的時(shí)候你都看不到它了;而改得更大了則暴露出了本來(lái)應(yīng)該封裝不讓外部看到的 setter,也破壞了面向?qū)ο蟮脑O(shè)計(jì)原則。所以,我們不能篡改修飾在訪(fǎng)問(wèn)器上的訪(fǎng)問(wèn)修飾符。
Part 4 它的用途
可以從前面給的例子里看出,這種機(jī)制似乎還不能和無(wú)法說(shuō)服我使用這個(gè)語(yǔ)法機(jī)制來(lái)處理一些字段的數(shù)據(jù)。這里我們來(lái)說(shuō)一下,封裝的靈活性。
我們?nèi)匀患僭O(shè) C
和 D
類(lèi)型,并規(guī)定 _field
字段和 Field
屬性。
_field