目錄
一、抽象類
?1.示例
二、抽象方法
三、接口
1.示例
2.內聯表達治愈警告CA1859
(1)傳統程序書寫源碼
(2)內聯后的源碼
四、多重繼承
1.示例
五、顯式接口成員實現
1.示例
六、總結?
贈人玫瑰,手有余香,?留下心得,換點流量。
一、抽象類
????????一般將父類定義為抽象類,需要使用這個父類進行繼承與多態處理。C#中聲明抽象類時需要使用abstract關鍵字,聲明抽象類時,除abstract關鍵字、class關鍵字和類名外,其他的都是可選項。
????????抽象類除了被繼承之外沒有任何意義。
?1.示例
//抽象類、抽象方法
namespace _01
{public abstract class MyClass{private string id = "";private string name = "";/// <summary>/// 編號屬性及實現/// </summary>public string ID{get{return id;}set{id = value;}}/// <summary>/// 姓名屬性及實現/// </summary>public string Name{get{return name;}set{name = value;}}/// <summary>/// 抽象方法,用來輸出信息/// </summary>public abstract void ShowInfo();}public class DriveClass : MyClass //繼承抽象類{/// <summary>/// 重寫抽象類中輸出信息的方法/// </summary>public override void ShowInfo(){Console.WriteLine(ID + " " + Name);}}class Program{/// <summary>/// 內聯/// </summary>static void Main(string[] args){DriveClass driveclass = new(); //實例化派生類,內聯前用派生類對象實例化抽象類 ((DriveClass)driveclass).ID = "BH0001"; //使用抽象類對象訪問抽象類中的編號屬性((DriveClass)driveclass).Name = "TM"; //使用抽象類對象訪問抽象類中的姓名屬性((DriveClass)driveclass).ShowInfo(); //使用抽象類對象調用派生類中的方法Console.WriteLine("------------");driveclass.ID = "BH0001"; //派生類直接實例化driveclass.Name = "TM";driveclass.ShowInfo();Console.Read();}}
}//結果:
/*BH0001 TM
---------------
BH0001 TM*/
二、抽象方法
????????在抽象類中聲明方法時,如果加上abstract關鍵字,則為抽象方法。
????????使用abstract關鍵字定義的類稱為抽象類,而使用這個關鍵字定義的方法稱為抽象方法,抽象方法沒有方法體,這個方法本身沒有任何意義,除非它被重寫,而承載這個抽象方法的抽象類就必須被繼承。反之,如果聲明一個抽象的方法,就必須將承載這個抽象方法的類定義為抽象類,不可能在非抽象類中獲取抽象方法。只要類中有一個抽象方法,此類就被標記為抽象類。
????????抽象類被繼承后需要實現其中所有的抽象方法,也就是保證相同的方法名稱、參數列表和相同返回值類型創建出非抽象方法,當然也可以是抽象方法。繼承抽象類的所有子類需要將抽象類中的抽象方法進行覆蓋。
聲明抽象方法時需要注意以下兩點:
? 抽象方法必須聲明在抽象類中。
? 聲明抽象方法時,不能使用virtual、static和private修飾符。
????????抽象方法聲明引入了一個新方法,但不提供該方法的實現,由于抽象方法不提供任何實際實現,因此抽象方法的方法體只包含一個分號。
????????當從抽象類派生一個非抽象類時,需要在非抽象類中重寫抽象方法,以提供具體的實現,重寫抽象方法時使用override關鍵字。
三、接口
????????接口是抽象類的延伸,可以將它看作是純粹的抽象類,接口中的所有方法都沒有方法體。接口可由方法、屬性、事件和索引器或這4種成員類型的任何組合構成,但不能包含字段,也不能設置這些成員的具體值,即,只能定義,不能給它們賦值。?接口可以繼承其他接口。??
接口具有以下特征:
? 接口類似于抽象基類:繼承接口的任何非抽象類型都必須實現接口的所有成員。
? 不能直接實例化接口。
? 接口可以包含事件、索引器、方法和屬性。
? 接口不包含方法的實現。
? 類和結構可從多個接口繼承。
? 接口自身可從多個接口繼承。
1.示例
// 接口
namespace _02
{interface IMyInterface{/// <summary>/// 編號(可讀可寫)/// </summary>string ID{get;set;}/// <summary>/// 姓名(可讀可寫)/// </summary>string Name{get;set;}/// <summary>/// 顯示定義的編號和姓名/// </summary>void ShowInfo();}class Program : IMyInterface //繼承自接口{string id = "";string name = "";/// <summary>/// 編號/// </summary>public string ID{get{return id;}set{id = value;}}/// <summary>/// 姓名/// </summary>public string Name{get{return name;}set{name = value;}}/// <summary>/// 顯示定義的編號和姓名/// </summary>public void ShowInfo(){Console.WriteLine("編號\t 姓名");Console.WriteLine(ID + "\t " + Name);}/// <summary>/// 注釋掉的部分,警告CA1859/// 內聯臨時變量后,警告消失/// </summary>static void Main(string[] args){Program program = new(); //實例化Program類對象//IMyInterface Imyinterface = program; //使用派生類對象實例化接口ImyInterface//Imyinterface.ID = "TM"; //為派生類中的ID屬性賦值//Imyinterface.Name = "C#從入門到精通"; //為派生類中的Name屬性賦值//Imyinterface.ShowInfo();((IMyInterface)program).ID = "TM"; //為派生類中的ID屬性賦值((IMyInterface)program).Name = "C#從入門到精通";//為派生類中的Name屬性賦值((IMyInterface)program).ShowInfo(); //調用派生類中方法顯示定義的屬性值Console.WriteLine("----------------------"); //用子類派生類直接實例化program.ID = "TM";program.Name = "C#從入門到精通";program.ShowInfo();Console.Read();}}
}//運行結果:
/*
編號 姓名
TM C#從入門到精通
----------------------
編號 姓名
TM C#從入門到精通*/
2.內聯表達治愈警告CA1859
?????????上述示例中,傳統的Main()方法中,先實例化派生類Program,再用派生對象實例化接口,然后用接口的屬性為派生類屬性賦值。這是.NET Framework 4.8之前的做法,其缺點是程序表達晦澀,不易讀,繼承關系不明顯。
????????在.NET 7.0、.NET 8.0下不建議這樣表達,會提示警告CA1859。VS2022.NET 8.0快速重構建議給出的意見是用內聯臨時變量的方法,直接表達繼承關系,使得程序簡潔易讀,繼承關系清晰明了。
嚴重性 | 代碼 | 說明 | 項目 | 文件 | 行 | 禁止顯示狀態 |
消息 | CA1859 | 將變量“_Imyinterface”的類型從“_02.IMyInterface”更改為“_02.Program”,以提高性能 | 02 | E:\C#_TM\chapter17\02\Program.cs | 72 | 活動 |
(1)傳統程序書寫源碼
static void Main(string[] args)
{Program program = new(); //實例化Program類對象IMyInterface Imyinterface = program; //使用派生類對象實例化接口ImyInterfaceImyinterface.ID = "TM"; //為派生類中的ID屬性賦值Imyinterface.Name = "C#從入門到精通"; //為派生類中的Name屬性賦值Imyinterface.ShowInfo(); //調用派生類中方法顯示定義的屬性值Console.WriteLine("------------------");//用子類派生類直接實例化program.ID = "TM";program.Name = "C#從入門到精通";program.ShowInfo();Console.Read();
}
(2)內聯后的源碼
static void Main(string[] args){Program program = new(); //實例化Program類對象//使用派生類對象實例化接口 ((IMyInterface)program).ID = "TM"; //為派生類中的ID屬性賦值((IMyInterface)program).Name = "C#從入門到精通"; //為派生類中的Name屬性賦值((IMyInterface)program).ShowInfo(); //調用派生類中方法顯示定義的屬性值Console.WriteLine("----------------------"); //用子類派生類直接實例化program.ID = "TM";program.Name = "C#從入門到精通";program.ShowInfo();Console.Read();}
????????內聯的表達可以隱藏(舍去)? ?,結果是一樣的,只是繼承關系不再明顯可見了。? ? ?
????????后面的例子中都會出現CA1859警告,其處理方法同此例,不再重復講述。
四、多重繼承
????????可以只繼承一個接口,接口也可以多重繼承,使用多重繼承時,要繼承的接口之間用逗號“,”分隔。
1.示例
// 繼承多個接口
namespace _03
{interface IPeople{/// <summary>/// 姓名/// </summary>string Name{get;set;}/// <summary>/// 性別/// </summary>string Sex{get;set;}}interface ITeacher : IPeople //繼承公共接口{/// <summary>/// 教學方法/// </summary>void Teach();}interface IStudent : IPeople //繼承公共接口{/// <summary>/// 學習方法/// </summary>void Study();}class Program : IPeople, ITeacher, IStudent//多接口繼承{string name = "";string sex = "";/// <summary>/// 姓名/// </summary>public string Name{get{return name;}set{name = value;}}/// <summary>/// 性別/// </summary>public string Sex{get{return sex;}set{sex = value;}}/// <summary>/// 教學方法/// </summary>public void Teach(){Console.WriteLine(Name + " " + Sex + " 教師");}/// <summary>/// 學習方法,學習內聯的表達/// 內聯前,用派生類對象實例化接口ITeacher/// 內聯前,用派生類對象實例化接口IStudent/// 舍去內聯的表達后效果一樣,內聯的存在使得繼承關系清晰明了/// </summary>public void Study(){Console.WriteLine(Name + " " + Sex + " 學生");}static void Main(string[] args){Program program = new(); //實例化類對象,內聯表達((ITeacher)program).Name = "TM";((ITeacher)program).Sex = "男";((ITeacher)program).Teach();((IStudent)program).Name = "C#";((IStudent)program).Sex = "男";((IStudent)program).Study();Console.WriteLine("--舍去內聯--");program.Name = "TM";program.Sex = "男";program.Teach();program.Name = "C#";program.Sex = "男";program.Study();Console.Read();}}
}//運行結果:
/*
TM 男 教師
C# 男 學生
--舍去內聯--
TM 男 教師
C# 男 學生 */
五、顯式接口成員實現
????????如果類實現兩個接口,并且這兩個接口包含具有相同簽名的成員,那么在類中實現該成員將導致兩個接口都使用該成員作為它們的實現。然而,如果兩個接口成員實現不同的功能,則可能會導致其中一個接口的實現不正確或兩個接口的實現都不正確,這時可以顯式地實現接口成員,即創建一個僅通過該接口調用并且特定于該接口的類成員。顯式接口成員實現是使用接口名稱和一個句點命名該類成員來實現的。
????????顯式接口成員實現中不能包含訪問修飾符、abstract、virtual、override或static修飾符。
????????顯式接口成員屬于接口的成員,而不是類的成員,因此,不能使用類對象直接訪問,只能通過接口對象來訪問。
? ? ? ? ?使用場合:當繼承多個接口的類中欲實現來自各自接口的同名方法賦予不同的功能時。
1.示例
// 顯式接口成員實現
namespace _04
{interface IMyInterface1{/// <summary>/// 求和方法/// </summary>int Add();}interface IMyInterface2{/// <summary>/// 求和方法/// </summary>int Add();}/// <summary>/// 顯式接口成員實現/// </summary>class MyClass : IMyInterface1, IMyInterface2 //繼承接口{/// <summary>/// 求和方法1/// </summary>int IMyInterface1.Add() //顯式接口成員實現{int x = 3;int y = 5;return (x + y);}/// <summary>/// 求和方法2/// </summary>int IMyInterface2.Add() //顯式接口成員實現{int x = 3;int y = 5;int z = 7;return (x + y + z);}}class Program{static void Main(string[] args){MyClass myclass = new(); //實例化接口繼承類的對象IMyInterface1 imyinterface1 = myclass; //使用接口繼承類的對象實例化接口Console.WriteLine(imyinterface1.Add()); //使用接口對象調用接口中的方法IMyInterface2 imyinterface2 = myclass; //使用接口繼承類的對象實例化接口Console.WriteLine(imyinterface2.Add()); //使用接口對象調用接口中的方法Console.WriteLine("----內聯表達----"); //使用內聯表達,是不是更清晰易讀?Console.WriteLine(((IMyInterface1)myclass).Add()); Console.WriteLine(((IMyInterface2)myclass).Add());Console.Read();}}
}//運行結果:
/*
8
15
----內聯表達----
8
15 */
????????內聯表達,用最少的語句,實現最清晰的繼承關系表達,是不是你的最愛?
六、總結?
????????抽象類和接口都包含可以由派生類繼承的成員,它們都不能直接實例化,但可以聲明它們的變量。如果這樣做,就可以使用多態性把繼承這兩種類型的對象指定給它們的變量。
????????抽象類和接口這兩種類型用于完全不同的目的。抽象類主要用作對象系列的基類,共享某些主要特性,例如共同的目的和結構。接口則主要用于類,這些類在基礎水平上有所不同,但仍可以完成某些相同的任務。
抽象類和接口的區別主要有以下幾點:
? 它們的派生類只能繼承一個基類,即只能直接繼承一個抽象類,但可以繼承任意多個接口。
? 抽象類中可以定義成員的實現,但接口中不可以。
? 抽象類中可以包含字段、構造函數、析構函數、靜態成員或常量等,接口中不可以。
? 抽象類中的成員可以是私有的(只要它們不是抽象的)、受保護的、內部的或受保護的內部成員(受保護的內部成員只能在應用程序的代碼或派生類中訪問),但接口中的成員必須是公共的。
? 派生類必須繼承抽象類的全部。派生類可以選擇是否繼承接口。
? 抽象類使得所有派生類大同小異,繼承多個接口則使得派生類各有不同。