我們什麼時候需要函式隱藏

2022-02-12 11:11:20 字數 4276 閱讀 2071

technorati 標籤: c#,new,方法隱藏

肖建的一篇博文引發了我的思考。

我們應該什麼時候使用函式隱藏(new關鍵字,不明白的請移步msdn)?

public class base

}public class a : base

}public class b : base

}public class testcase

}}

可以看到,base類中將method方法宣告為虛方法,就是為了今後的擴充套件的,其子類如果覺得base類中method方法的預設實現不合適的話,可以自行override,從而達到擴充套件的目的。而類b中由於使用了new關鍵字而不是override,使得在多型呼叫時呼叫的仍是基類中的method而不是子類中的method。形象的說,由於b沒有遵守「遊戲規則」,所以b對於method的擴充套件不會在通常情況下起作用。

這裡插一句,飛林沙提出「那virtual這個關鍵字就可以廢棄了,永遠把這個方法給暴露出來就成了,或者使用abstract」

對於這個觀點,前半句我將在下面說明;而使用abstract就要求子類必須override父類中的方法,而不能使用一種預設的實現,如果只是宣告為virtual,子類就可以選擇是否覆蓋父類的這個方法。

個人認為,只有這一種情況我們可能用到new關鍵字來進行函式隱藏,且看**:

public class 魚

}public class 章魚 : 魚

}

顯然,在設計「魚」這個類的時候,沒有想到會有魚沒有尾巴,但是我們又確實碰見了沒有尾巴的「章魚」(別較真)。

如果「魚」這個類是在類庫中的話,我們就不能修改這個類,於是我們只能用new關鍵字進行方法隱藏,從而在表面上差不多的情況下使得「章魚」有正確的swim效果。也就是說,我認為,只有在類庫設計不當,導致在本應該有virtual宣告而沒有宣告的情況下,才需要子類進行方法隱藏。我不知道我這個觀點是不是全面,希望大家補充,還有什麼情況下可能會用到方法隱藏。

但是方法隱藏有這樣乙個問題(第一段中提出),即在以基類的簽名呼叫子類時呼叫的仍然是基類的方法(如((魚)章魚a).swim())。也就是說,只有在我們知道乙個例項是該子類的時候,我們才能夠呼叫這個新宣告的方法。既然如此,我們為什麼不乾脆宣告乙個新的方法,而選擇隱藏基類的方法呢?

需要注意的是,我的問題並不是

override和new的區別,而是以下兩點:

方法隱藏的使用時機是什麼?

既然只有在知道型別簽名的時候才能夠呼叫新宣告的方法,我們為什麼不乾脆宣告乙個新方法?

首先來看一些我個人認為比較有意義的回覆:

引用rouper:

我也有過一次,不過後來改成顯示實現介面來做了。只有在類庫的設計太差的時候才會導致非用new不可。感覺有點像是在hack

引用飛林沙:

關鍵是這樣的轉換可以避免再重新new乙個耗時的大物件。我覺得這是關鍵

飛林沙的觀點在於為什麼不使用組合而是使用繼承,但是我感覺跟函式隱藏沒有什麼特別的聯絡,不知道是不是我理解的不對。

引用週中豪:

當要求子類b必須具有某個給定方法名的方法供b自身呼叫或者供其他部分反射,同名方法又已經在父類a中存在時,為了不影響把b視為a時的邏輯,應該用new。不過 這種情況是一種hack,還是不好的設計。

的確如此,但是我想知道是不是有一種應該(適合)使用函式隱藏的情況,如果沒有的話,是不是應該禁用這個語言特性。

引用cfan.net:

呵呵,我也談談我的理解吧。

1、什麼時候使用這是乙個應用的問題,不要從語言層面的技術角度去思考,那樣就鑽牛角尖了;

2、舉乙個遊戲的例子,dosomething表示使用**,預設的基本**是刀a,人物在地上揀了乙個長槍b, 於是他可以使用b.dosomething,但是當他走到乙個狹窄的巷子裡,或是其它什麼原因暫時受限制,不能用槍b時,這時呼叫((a)b).dosomething臨時用刀a了,而正常情況則b.dosomething;

我認為這個例子明顯不能說明問題,這個例子中乙個人顯然應該擁有若干種**,什麼時候應該用什麼**直接切換就是了,范不著這麼彆扭。

引用alex he:

new關鍵字告訴編譯器,子類中的方法雖然與父類方法一樣(簽名,引數),但是該方法與基類中的方法沒有任何關係

這句話短小精闢的揭示了函式隱藏的本質。

引用諾貝爾:

首先,章魚不是魚。因此這個「強硬」的繼承,不過是濫用語法的乙個特徵。語法能支援的,不等於邏輯能通過。

ms設計這個new,自然有他的用意所在。但是我看不到很有意義的設計點。

我個人的看法,這不過是具體編碼中的一種妥協,比如,這個方法雖然不行,但是還有其他方法可以,而既然這個方法不行(也就是沒有多型意義),那我完全也不用考慮要避免和基類同名,因為這個名字可能還是很能達意的。

當然,我不認為這種妥協是什麼好設計,因為這等於放棄了物件導向的哲學,淪為一種複製貼上,是一種簡陋,容易出錯的復用**行為

觀點相同~

下面給大家帶來zhenway的精彩回覆!

引用zhenway:

new有new的用處,不要一棍子打死

例如:

public class a

}public class b : a

}

看起來不錯,但用起來哪?

b b1 = new b();

b b2 = (b)b1.clone();

是不是很彆扭?

稍加改造:

public class a

protected virtual a clonecore()

}public class b : a

protected override a clonecore()

}

這樣用起來就舒服了吧:

b b1 = new b();

b b2 = b1.clone(); // another instance of b as type b

a a1 = b1;

a a2 = a1.clone(); // another instance of b as type a

引用hcoona:

@alex he

我上面是這樣說的

我認為你說的這種情況不可能出現,原因如下

1.、如果我使用的是父類的簽名,那麼你子類即使使用函式隱藏也不會起到任何效果

2、如果我使用的是子類,不知道在.net2.0上是怎麼滿足我的要求的。退一步說,就算真的需要改變邏輯,那麼說明這個子類的行為特徵與父類不相符合,屬於一種邏輯上的錯誤,不應該使用繼承

3.補充一下,

而且,為了**相容性,我們更應該使用擴充套件方法,或者新增乙個新方法的方式來擴充子類

下面我來解釋一下

假設你是乙個類庫提供者,

對於第一種情況

如果客戶是這樣使用的:

public class classinbll

}

那麼,在子類中使用new關鍵字根本就不起作用。

對於第二種情況:

原來是這樣的:

public class base

}public class a : base

並且在客戶的**中是直接使用a的,現在你覺得使用a.method()時候的base::method方法不合用了,那麼你需要修改子類,為子類新增乙個同名方法method,以使得在客戶的**不需要改動,只需要重新編譯。

現在,我建議進行這樣的修改:

public class aa : base

public class a

}

如果你說a必須得繼承自base,那麼你那種修改方式也是有問題的。

對於我說的第三點,我建議提供擴充套件方法來擴充套件a,而不必修改任何**,當然,這種方式的前提是,修改method方法不是必須的,它能夠滿足部分功能,但是新的需求導致我們需要提供乙個新的method1方法,這個方法可以使用擴充套件方法滿足:)

討論了這麼長時間,我說一下我得出的結論吧,不同意的也不要噴,畢竟大家是可以有不同想法的,不同想法就可以討論。

我得出的結論有以下兩點:

函式隱藏只是用於上面zhenway提到的那種,子類由於呼叫方便,需要宣告乙個與父類中同名函式返回值不同的函式,而這兩個函式除了返回值不同以外,在邏輯上沒有差別:)

其他情況不要使用函式隱藏,因為總有更好的解決方案可以解決而沒有函式隱藏帶來的***:)

2012/11/19補充:

在翻舊文的時候突然看到了這篇文章,zhenway的回覆中的內容,現在可以通過協變性完美的解決:

沒有方法級的協變性支援,還是做不到更簡潔的實現方式。

2012/11/20補充:

什麼時候需要建索引,什麼時候不需要?

什麼時候要索引?1 表的主鍵 外來鍵必須有索引 2 資料量超過300必須有索引 3 經常與其他表進行連線的表,在連線欄位上建立索引 4 經常出現在where子句的字段,特別是大表字段,必須建索引 5 索引應建立在小字段上,對於大文字字段甚至超長字段,不要建索引 什麼時候不需要索引?1 建立組合索引,...

什麼時候我們考慮使用指令碼

指令碼的架構 指令碼宿主 在其中執行 iis,ie,wsh wscript,cscript 指令碼引擎 解釋程式 windows作業系統內建的 vbscript 與jscript 指令碼可以呼叫的物件模型 wsh,wmi,adsi,ado,cdo等 wsh 可以呼叫com物件,使用命令列,呼叫she...

Swift 什麼時候需要mutating這個引數

swift中有兩種型別 值型別和引用型別 示例 在結構體中,有乙個例項方法,如果直接修改屬性的值,編譯器會報錯。上面中如果不新增mutating這個修飾符,則會出錯,提示為 cannot assign to property self is immutable 意思就是 不能給屬性分配值,因為sel...