C# 類和繼承(使用基類的引用)

使用基類的引用

派生類的實例由基類的實例和派生類新增的成員組成。派生類的引用指向整個類對象,包括
基類部分。

如果有一個派生類對象的引用,就可以獲取該對象基類部分的引用(使用類型轉換運算符把
該引用轉換為基類類型)。類型轉換運算符放置在對象引用的前面,由圓括號括起的要被轉換成
的類名組成。類型轉換將在第17章闡述。將派生類對象強制轉換為基類對象的作用是產生的變
量只能訪問基類的成員(在被覆寫方法中除外,稍后會討論)。

接下來的幾節將闡述使用對象的基類部分的引用來訪問對象。我們從觀察下面兩行代碼開
始,它們聲明了對象的引用。圖8-6闡明了代碼,并展示了不同變量所看到的對象部分。

  • 第一行聲明并初始化了變量derived,它包含一個MyDerivedClass類型對象的引用。
  • 第二行聲明了一個基類類型MyBaseClass的變量,并把derived中的引用轉換為該類型,
    給出對象的基類部分的引用。
    • 基類部分的引用被存儲在變量mybc中,在賦值運算符的左邊。
    • 基類部分的引用“看不到"派生類對象的其余部分,因為它通過基類類型的引用“看”
      這個對象。
MyDerivedClass derived=new MyDerivedClass();      //創建一個對象
MyBaseClass mybc=(MyBaseClass) derived;           //轉換引用

派生類的引用可以看到完整的MyDerivedClass對象,而mybc只能看到對象的
MyBaseClass部分

下面的代碼展示了兩個類的聲明和使用。圖8-7闡明了內存中的對象和引用。
Main創建了一個MyDerivedClass類型的對象,并把它的引用存儲到變量derived中。Main
還創建了一個MyBaseClass類型的變量,并用它存儲對象基類部分的引用。當對每個引用調用
print方法時,調用的是該引用所能看到的方法的實現,并產生不同的輸出字符串。

class MyBaseClass
{public void Print(){Console.WriteLine("This is the base class.");}
}class MyDerivedClass:MyBaseClass
{public int var1;new public void Print(){Console.WriteLine("This is the derived class.");}
}class Program
{static void Main(){MyDerivedClass derived=new MyDerivedClass();MyBaseClass mybc=(MyBaseClass)derived;       //轉換為基類derived.Print();                             //從派生類部分調用Printmybc.Print();                                //從基類部分調用Print//mybc.var1=5;                               //錯誤:基類引用無法訪問派生類成員}
}

對派生類和基類的引用

虛方法和覆寫方法

在上一節我們看到,當使用基類引用訪問派生類對象時,得到的是基類的成員。虛方法可以
使基類的引用訪問“升至“派生類內。
可以使用基類引用調用派生類的方法,只需滿足下面的條件。

  • 派生類的方法和基類的方法有相同的簽名和返回類型。
  • 基類的方法使用virtual標注。
  • 派生類的方法使用override標注。

例如,下面的代碼展示了基類方法和派生類方法的virtual及override修飾符。

class MyBaseClass             //基類
{virtual public void Print()....
}class MyDerivedClass:MyBaseClass //派生類
{override void Print()}

圖8-8闡明了這組virtual和override方法。注意和上一種情況(用new隱藏基類成員)相
比在行為上的區別。

  • 當使用基類引用(mybc)調用Print方法時,方法調用被傳遞到派生類并執行,因為:
    • 基類的方法被標記為virtual;
    • 在派生類中有匹配的override方法。
  • 圖8-8闡明了這一點,顯示了一個從virtual Print方法后面開始,并指向overridePrint
    方法的箭頭。

虛方法和覆寫方法

下面的代碼和上一節中的相同,但這一次,方法上標注了virtual和override。產生的結果
和前一個示例有很大不同。在這個版本中,對基類方法的調用實際調用了子類中的方法。

class MyBaseClass
{virtual public void Print(){Console.WiteLine("This is the base class.");}
}class MyDerivedClass:MyBaseClass
{public override void Print(){Console.WriteLine("This is the derived class.");}
}class Program
{static void Main(){MyDerivedClass derived=new MyDerivedClass();MyBaseClass mybc=(MyBaseClass)derived; //強制轉換成基類derived.Print();mybc.Print();}
}

其他關于virtual和override修飾符的重要信息如下。

  • 覆寫和被覆寫的方法必須有相同的可訪問性。例如,這種情況是不可以的:被覆寫的方
    法是private的,而覆寫方法是public的。
  • 不能覆寫static方法或非虛方法。
  • 方法、屬性和索引器(前一章闡述過),以及另一種成員類型一一事件(將在后面闡述),
    都可以被聲明為virtual和override。

覆寫標記為override的方法

覆寫方法可以在繼承的任何層次出現。

  • 當使用對象基類部分的引用調用一個被覆寫的方法時,方法的調用被沿派生層次上溯執
    行,一直到標記為override的方法的最高派生(most-derived)版本。
  • 如果在更高的派生級別有該方法的其他聲明,但沒有被標記為override,那么它們不會
    被調用。

例如,下面的代碼展示了3個類,它們形成了一個繼承層次:MyBaseClass、MyDerivedClass
和SecondDerived。所有這3個類都包含名為Print的方法,并帶有相同的簽名。在MyBaseClass
中,Print被標記為virtual。在MyDerivedClass中,它被標記為override。在類SecondDerived
中,可以使用override或new聲明方法Print。讓我們看一看在每種情況下將發生什么。

class MyBaseClass  //基類
{virtual public void Print(){COnsole.WriteLine("This is the base class.");}
}class MyDerivedClass:MyBaseClass   //派生類
{override void Print(){Console.WriteLine("This is the derived class.");}}class SecondDerived:MyDerivedClass   //最高派生類
{...//在后面給出
}

情況1:使用override聲明Print

如果把SecondDerived的Print方法聲明為override,那么它會覆寫方法的兩個低派生級別的
版本,如圖8-9所示。如果一個基類的引用被用于調用Print,它會向上傳遞,一直到類secondDerived
中的實現。

執行被傳遞到多層覆寫鏈的頂端
下面的代碼實現了這種情況。注意方法Main的最后兩行代碼。

  • 兩條語句中的第一條使用最高派生類SecondDerived的引用調用Print方法。這不是通過
    基類部分的引用的調用,所以它將會調用SecondDerived中實現的方法。
  • 而第二條語句使用基類MyBaseClass的引用調用Print方法。
class SecondDerived:MyDerivedClass
{override public void Print(){Console.WriteLine("This is the second derived class.");}
}class Program
{static void Main(){SecondDerived derived=new SecondDerived();//使用SecondDerivedMyBaseClass mybc=(MybaseClass)derived;    //使用MyBaseClassderived.Print();mybc.Print();}
}

結果是:無論Print是通過派生類調用還是通過基類調用的,都會調用最高派生類中的方法。
當通過基類調用時,調用沿著繼承層次向上傳遞。這段代碼產生以下輸出:

This is the second derived class.
This is the second derived class.

2.情況2:使用new聲明Print

相反,如果將SecondDerived中的Print方法聲明為new,則結果如圖8-10所示。Main和上
一種情況相同。

class SecondDerived:MyDerivedClass
{new public void Print(){Console.WriteLine("This is the second derived class.");}
}class Program
{static void Main(){SecondDerived derived=new SecondDerived();//使用SecondDerivedMyBaseClass mybc=(MyBaseClass)derived;    //使用MyBaseClassderived.Print();mybc.Print();}
}

結果是:當通過SecondDerived的引用調用方法Print時,SecondDenved中的方法被執行,
正如所期待的那樣。然而,當通過MyBaseClass的引用調用Print方法時,方法調用只向上傳遞
了一級,到達類MyDerived,在那里它被執行。兩種情況的唯一不同是SecondDerived中的方法
使用修飾符override還是修飾符new聲明。
這段代碼產生以下輸出:

This is the second derived class.
This is the derived class.

隱藏覆寫的方法

覆蓋其他成員類型

在之前的幾節中,我們已經學習了如何在方法上使用virtual/override。在屬性、事件以及
索引器上的用法也是一樣的。例如,下面的代碼演示了名為MyProperty的只讀屬性,其中使用
了virtual/override。

class MyBaseClass
{private int _myInt=5;virtual public int MyProperty{get{return _myInt;}}
}class MyDerivedClass:MyBaseClass
{private int _myInt=10;override public int  MyProperty {get{return _myInt;}}
}class Program
{static void Main(){MyDerivedClass derived=new MyDerivedClass();MyBaseClass mybc=(MyBaseClass)derived;Console.WriteLine(derived.MyProperty);Console.WriteLine(mybc.MyProperty);}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/83151.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/83151.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/83151.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如何在騰訊云 OpenCloudOS 上安裝 Docker 和 Docker Compose

從你提供的 /etc/os-release 文件內容來看,你的服務器運行的是 OpenCloudOS 9.2。這是一個基于 CentOS 和 RHEL 的開源操作系統,因此它屬于 CentOS/RHEL 系列。 關鍵信息總結 操作系統名稱:OpenCloudOS版本:9.2ID:op…

趨勢直線指標

趨勢直線副圖和主圖指標,旨在通過技術分析工具幫助交易者識別市場趨勢和潛在的買賣點。 副圖指標:基于KDJ指標的交易策略 1. RSV值計算: - RSV(未成熟隨機值)反映了當前收盤價在過去一段時間內的相對位置。通過計算當前…

FEMFAT許可分析的數據可視化方法

隨著企業對FEMFAT軟件使用的增加,如何有效地管理和分析許可數據成為了關鍵。數據可視化作為一種強大的工具,能夠幫助企業直觀地理解FEMFAT許可的使用情況,從而做出更明智的決策。本文將介紹FEMFAT許可分析的數據可視化方法,并探討…

AMBER軟件介紹

AMBER軟件介紹 AMBER(Assisted Model Building with Energy Refinement)是一套廣泛應用于分子動力學(MD)模擬和生物分子結構分析的軟件工具集,尤其在蛋白質、核酸、多糖等生物大分子的模擬中表現突出。以下是關于AMBE…

GoogLeNet網絡模型

GoogLeNet網絡模型 誕生背景 在2014年的ImageNet圖像識別挑戰賽中,一個GoogLeNet的網絡架構大放異彩,與VGG不同的是,VGG用的是3*3的卷積,而GoogLeNet從1*1到7*7的卷積核都用,也就是使用不同大小的卷積核組合。 網絡…

Free2AI:企業智能化轉型的加速器

隨著數字化與智能化的深度交融,企業的競爭舞臺已悄然轉變為數據處理能力和智能服務水平的競技場。Free2AI以其三大核心功能——智能數據采集、多格式文檔解析、智能FAQ構建,為企業鋪設了一條從數據洞察到智能服務的全鏈路升級之路,成為推動企…

Vue 核心技術與實戰day07

1. vuex概述 2. 構建 vuex [多組件數據共享] 環境 <template><div id"app"><h1>根組件- {{ title }}- {{ count }}</h1><input :value"count" input"handleInput" type"text"><Son1></Son1>…

【原神 × 插入排序】刷圣遺物也講算法:圣遺物評分系統背后的排序邏輯你真的懂嗎?

?? 改編自:王爭《數據結構與算法之美》 ?? 游戲演繹:米哈游《原神》 ?? 核心關鍵詞:插入排序、排序算法、評分系統、屬性評價、強化圣遺物、冒泡排序對比 ?? 引言:原神刷本=刷排序? 玩《原神》的玩家每天日常是啥?體力用來刷圣遺物、精通頭、暴擊頭、攻充沙………

quasar electron mode如何打包無邊框桌面應用程序

預覽 開源項目Tokei Kun 一款簡潔的周年紀念app&#xff0c;現已發布APK&#xff08;安卓&#xff09;和 EXE&#xff08;Windows&#xff09; 項目倉庫地址&#xff1a;Github Repo 應用下載鏈接&#xff1a;Github Releases Preparation for Electron quasar dev -m elect…

微信小程序真機調試時如何實現與本地開發環境服務器交互

最近在開發微信小程序項目,真機調試時需要在手機上運行小程序,為了實現本地開發服務器與手機小程序的交互,需要以下步驟 1.將手機連到和本地一樣的局域網 2.Visual Studio中將IIS Express服務器的localhost端口地址修改為本機的IP自定義的端口: 1&#xff09;找到web api項目…

Scratch節日 | 拯救屈原 | 端午節

端午節快樂&#xff01; 這款特別為端午節打造的Scratch游戲 《拯救屈原》&#xff0c;將帶你走進古代中國&#xff0c;感受歷史與文化的魅力&#xff01; &#x1f3ee; 游戲介紹 扮演勇敢的探險者&#xff0c;穿越時空回到古代&#xff0c;解鎖謎題&#xff0c;完成任務&…

PHP下實現RSA的加密,解密,加簽和驗簽

前言&#xff1a; RSA下加密&#xff0c;解密&#xff0c;加簽和驗簽是四種不同的操作&#xff0c;有時候會搞錯&#xff0c;記錄一下。 1.公鑰加密&#xff0c;私鑰解密 發送方通過公鑰將原數據加密成一個sign參數&#xff0c;相當于就是信息的載體&#xff0c;接收方能通過si…

Win10秘笈:兩種方式修改網卡物理地址(MAC)

Win10秘笈&#xff1a;兩種方式修改網卡物理地址&#xff08;MAC&#xff09; 在修改之前&#xff0c;可以先確定一下要修改的網卡MAC地址&#xff0c;查詢方法有很多種&#xff0c;比如&#xff1a; 1、在設置→網絡和Internet→WLAN/以太網&#xff0c;如下圖所示。 2、在控…

C++中IO文件輸入輸出知識詳解和注意事項

以下內容將從文件流類體系、打開模式、文本與二進制 I/O、隨機訪問、錯誤處理、性能優化等方面&#xff0c;詳解 C 中文件輸入輸出的使用要點&#xff0c;并配以示例。 一、文件流類體系 C 標準庫提供三種文件流類型&#xff0c;均定義在 <fstream> 中&#xff1a; std…

Unity3D仿星露谷物語開發56之保存角色位置到文件

1、目標 游戲中通過Save Game保存角色位置&#xff0c;當重啟游戲后&#xff0c;通過Load Game可以恢復角色的位置。 2、Player對象操作 &#xff08;1&#xff09;組件添加 給Hierarchy下的Player組件添加Generate GUID組件。 &#xff08;2&#xff09;修改SceneSave.cs腳…

TKernel模塊--雜項

TKernel模塊–雜項 1.DEFINE_HARRAY1 #define DEFINE_HARRAY1(HClassName, _Array1Type_) \ class HClassName : public _Array1Type_, public Standard_Transient { \public: …

c++ typeid運算符

typeid運算符能獲取類型信息。獲取到的是type_info對象。type_info類型如下&#xff1a; 可以看到&#xff0c;這個類刪除了拷貝構造函數以及等號操作符。有一些成員函數&#xff1a;hash_code、before、name、raw_name, 還重載了和!運算符。 測試&#xff1a; void testTyp…

第304個Vulnhub靶場演練攻略:digital world.local:FALL

digital world.local&#xff1a;FALL Vulnhub 演練 FALL (digitalworld.local: FALL) 是 Donavan 為 Vulnhub 打造的一款中型機器。這款實驗室非常適合經驗豐富的 CTF 玩家&#xff0c;他們希望在這類環境中檢驗自己的技能。那么&#xff0c;讓我們開始吧&#xff0c;看看如何…

【數據庫】數據庫恢復技術

數據庫恢復技術 實現恢復的核心是使用冗余&#xff0c;也就是根據冗余數據重建不正確數據。 事務 事務是一個數據庫操作序列&#xff0c;是一個不可分割的工作單位&#xff0c;是恢復和并發的基本單位。 在關系數據庫中&#xff0c;一個事務是一條或多條SQL語句&#xff0c…

switch-case判斷

switch-case判斷 #include <stdio.h> int main() {int type;printf("請輸入你的選擇&#xff1a;\n");scanf("%d",&type);getchar();switch (type){case 1:printf("你好&#xff01;");break;case 2:printf("早上好&#xff01;…