日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

漫谈C#编程中的多态与new关键字

發布時間:2023/12/20 C# 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 漫谈C#编程中的多态与new关键字 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. 你通常怎樣用多態?
  假設我有一個類,里面有一個 PrintStatus 方法,用于打印實例的當前狀態,我希望該類的派生類都帶有一個 PrintStatus 方法,并且這些方法都用于打印其實例的當前狀態。那么我會這樣表達我的愿望:
// Code #01
class Base
{
 public virtual void PrintStatus()
 {
  Console.WriteLine("public virtual void PrintStatus() in Base");
 }
}
  于是我可以寫一個這樣的方法:
// Code #02
public void DisplayStatusOf(Base[] bs)
{
 foreach (Base b in bs)
 {
  b.PrintStatus();
 }
}
  bs 中可能包含著不同的 Base 的派生類,但我們卻可以忽略這些“個性”而使用一種統一的方式來處理某事。在 .NET 2.0 中,XmlReader 的 Create 有這樣一個版本:
public static XmlReader Create(Stream input);
  你可以向 Create 傳遞任何可用的“流”,例如來自文件的“流”(FileStream)、來自內存的“流”(MemoryStream)或來自網絡的“流”(NetworkStream)等。雖然每一中“流”的工作細節都不同,但我們卻使用一種統一的方式來處理這些“流”。
  2. 假如有人不遵守承諾...
  DisplayStatusOf 隱含著這樣一個假設:bs 中如果存在派生類的實例,那么該派生類應該重寫 PrintStatus,當然必須加上 override 關鍵字:
// Code #03
class Derived1 : Base
{
 public override void PrintStatus()
 {
  Console.WriteLine("public override void PrintStatus() in Derived1");
 }
}
  你可以把這看作一種承諾、約定,直到有人沉不住氣...
// Code #04
class Derived2 : Base
{
 public new void PrintStatus()
 {
  Console.WriteLine("public new void PrintStatus() in Derived2");
 }
}
  假設我們有這樣一個數組: // Code #05
Base[] bs = new Base[]
{
 new Base(),
 new Derived1(),
 new Derived2()
};
  把它傳遞給 DisplayStatusOf,則輸出是:
// Output #01
// public virtual void PrintStatus() in Base
// public override void PrintStatus() in Derived1
// public virtual void PrintStatus() in Base
  從輸出結果中很容易看出 Derived2 并沒有按照我們期望的去做。但你無需驚訝,這是由于 Derived2 的設計者沒有“遵守約定”的緣故。
  3. new:封印咒術
  new 似乎給人一種這樣的感覺,它的使用者喜歡打破別人的約定,然而,如果使用恰當,new 可以彌補基類設計者的“短見”。在 Creating a Data Bound ListView Control 中,Rockford Lhotka 就示范了如何封印原來的 ListView.Columns,并使自行添加的返回 DataColumnHeaderCollection 的 Columns 取而代之。
  從 Output #01 中我們可以看到,new 只是把 Base.PrintStatus 封印起來而不是消滅掉,你可以解除封印然后進行訪問。對于 Derived2 的使用者,解封的方法是把 Derived2 的實例轉換成 Base 類型:
// Code #06
Base d2 = new Derived2();
d2.PrintStatus();
// Output #02
// public virtual void PrintStatus() in Base
而在 Derived2 內部,你可以透過 base 來訪問:
// Code #07
base.PrintStatus();
  這種方法是針對實例成員的,如果被封印的成員是靜態成員的話,就要透過類名來訪問了。
  4. 假如 Base.PrintStatus 是某個接口的隱式實現...
  假如 Base 實現了一個 IFace 接口:
// Code #08
interface IFace
{
 void PrintStatus();
}
class Base : IFace
{
 public virtual void PrintStatus()
 {
  Console.WriteLine("public virtual void PrintStatus() in Base");
 }
}
  我們只需要讓 Derived2 重新實現 IFace:
// Code #09
class Derived2 : Base, IFace
{
 public new void PrintStatus()
 {
  Console.WriteLine("public new void PrintStatus() in Derived2");
 }
}
  Derived1 保持不變。則把:
// Code #10
IFace[] fs = new IFace[]
{
 new Base(),
 new Derived1(),
 new Derived2(),
}
  傳遞給:
// Code #11
public void DisplayStatusOf(IFace[] fs)
{
 foreach (IFace f in fs)
 {
  f.PrintStatus();
 }
}
  輸出結果是:
// Output #03
// public virtual void PrintStatus() in Base
// public override void PrintStatus() in Derived1
// public new void PrintStatus() in Derived2
  從輸出結果中,我們可以看到,雖然 Derived2.PrintStatus 應用了 new,但卻依然參與動態綁定,這是由于 new 只能割斷 Derived2.PrintStatus 和 Base.PrintStatus 的聯系,而不能割斷它與 IFace.PrintStatus 的聯系。我在 Derived2 的定義中重新指定實現 IFace,這將使得編譯器認為 Derived2.PrintStatus 是 IFace.PrintStatus 的隱式實現,于是,在動態綁定時 Derived2.PrintStatus 就被包括進來了。
  5. 誰的問題?
  我必須指出,如果 Base(Code #01)和 Derived2(Code #04)同時存在的話,它們倆其中一個存在著設計上的問題。為什么這樣說呢?Base 的設計者在 PrintStatus 上應用 virtual 說明了他希望派生類能透過重寫這一方法來參與動態綁定,即多態性;而 Derived2 的設計者在 PrintStatus 上應用 new 則說明了他希望割斷 Derived2.PrintStatus 和 Base.PrintStatus 之間的聯系,這將使得 Derived2.PrintStatus 無法參與到 Base 的設計者所期望的動態綁定中。如果在 Base.PrintStatus 上應用 virtual(即對多態性的期望)是合理的話,那么 Derived2.PrintStatus 應該換用另外一個名字了;如果在 Derived2.PrintStatus 上應用 new(即否決參與動態綁定)是合理的,那么 Base.PrintStatus 應該考慮是否去掉 virtual 了,否則就會出現一些奇怪的行為,例如 Output #01 的第三行輸出。
  假如繼承體系中多態性行為的期望是合理的話,那么更實際的做法應該是把 Base 定義成這樣:
// Code #12
abstract class Base
{
 public abstract void PrintStatus();
}
  而原來 Base 中的實現應該下移到一個派生類中: // Code #13
class Derived3 : Base
{
 public override void PrintStatus()
 {
  Console.WriteLine("public override void PrintStatus() in Derived3 [originally implemented in Base]");
 }
}
  這樣,Derived2.PrintStatus 將使得編譯無法完成,從而迫使其設計者要么更改方法的名字,要么換用 override 修飾。這種強制使得 Derived2 的設計者不得不重新考慮其設計的合理性。
  假如繼承體系中多態性行為的期望不總是合理呢?例如 Stream 有這樣一個方法:
public abstract long Seek(long offset, SeekOrigin origin);
  現在假設我有一個方法在處理輸入流時需要用到 Stream.Seek:
// Code #14
public void Resume(Stream input, long offset)
{
 //
 input.Seek(offset, SeekOrigin.Begin);
 //
}
  當我們向 Resume 傳遞一個 NetworkStream 的實例,Resume 將會拋出一個 NotSupportedException,因為 NetworkStream 不支持 Seek。那么這是否說明 Stream 的設計有問題呢?
  設想 Resume 是一個下載工具進行斷點續傳的方法,然而,并不是所有的服務器都支持斷點續傳的,于是,你需要首先判斷輸入流是否支持 Seek 操作,再決定如何處理輸入流:
// Code #15
public void Resume(Stream input, long offset)
{
 if (input.CanSeek)
 {
  //
  input.Seek(offset, SeekOrigin.Begin);
  //
 }
 else
 {
  //
 }
}
  如果 CanSeek 為 false,那就只好從頭來過了。
  實際上,我們并不能保證任何 Stream 的派生類都能夠支持某個(些)操作,我們甚至不能保證來自同一個派生類的所有實例都支持某個(些)操作。你可以設想有這樣一個 PriorityStream,它能夠根據當前登錄賬號的權限來決定是否提供寫操作,這使得擁有足夠權限的人才能修改數據。或許 Stream 的設計者已經預料到這類情況的發生,所以 CanRead、CanSeek 和 CanWrite 就被加入到 Stream 里了。
  值得注意的是,Code #07 的 Derived2 可能是一個很糟糕的設計,也可能是一個很實用的設計。在本文,它是一個很糟糕的設計,如果你足夠細心,你會察覺到 Derived2 的設計者希望 Derived2.PrintStatus 繞過 Base.PrintStatus 而直接和 IFace.PrintStauts 進行關聯,表面上這沒什么不妥,但實質上 Base.PrintStatus 和 IFace.PrintStauts 在約定上是同質的,這意味著如果與 IFace.PrintStauts 進行關聯就等于承認自己和 Base.PrintStatus 是同質的,這樣的話,為什么不直接在 Derived2 里重寫 PrintStatus 呢?在《基類與接口混合繼承的聲明問題》中,我示范了一個實用的設計,用 new 和接口重新實現(Interface reimplementation)來糾正非預期的多態行為。
  6. 最后...
  當我的朋友拿著問題來找我時,我通常都不會直接給出我的答案,而是盡我的能力向他提供足夠多的可用信息,以便他能夠根據他所面臨的實際情況作出處理,畢竟,我不會比他更了解他的問題,而他也應該形成他自己的關于他的問題的思考。我希望浪子能用自己的答案回答他所提出的問題,因為只有這樣,那些知識才真正屬于他,并且我也相信本文已經提供了足夠多的可用信息。
C# 3.0新特性初步研究 Part1:使用隱含類型的本地變量

C# 3.0新特性初步研究 Part2:使用擴展方法

C# 3.0新特性初步研究 Part3:使用拉姆達表達式

C# 3.0新特性初步研究 Part4:使用集合類型初始化器

C# 3.0新特性初步研究 Part5:匿名類型

C# 3.0新特性初步研究 Part6:使用查詢表達式

C# 4.0語言將出現重大改變,帶來一段Code Preview

轉載于:https://www.cnblogs.com/fhbcn/archive/2009/01/08/1371578.html

總結

以上是生活随笔為你收集整理的漫谈C#编程中的多态与new关键字的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。