1)概念
抽象類是特殊的類,只是不能被實例化;除此以外,具有類的其他特性;重要的是抽象類可以包括抽象方法,這是普通類所不能的。抽象方法只能聲明于抽象類中,且不包含任何實現,派生類必須覆蓋它們。另外,抽象類可以派生自一個抽象類,可以覆蓋基類的抽象方法也可以不覆蓋,如果不覆蓋,則其派生類必須覆蓋它們。接口是引用類型的,接口和抽象類實現了oop中的一個原則,把可變的與不可變的分離。抽象類和接口就是定義為不可變的,而把可變的作為子類去實現,接口和抽象類的相似之處有三點:
² 不能實例化;
² 包含未實現的方法聲明;
² 派生類必須實現未實現的方法,抽象類是抽象方法,接口則是所有成員(不僅是方法包括其他成員);
2)抽象類和接口的區別
² 類是對對象的抽象,可以把抽象類理解為把類當作對象,抽象成的類叫做抽象類.而接口只是一個行為規范或規定
² 接口基本上不具備繼承的任何具體特點,它僅僅承諾了能夠調用的方法
² 一個類一次可以實現若干個接口,但是只能擴展一個父類
² 接口除了可以包含方法之外,還可以包含屬性、索引器、事件,而且這些成員都被定義為公有的。除此之外,不能包含任何其他的成員,例如:常量、域、構造函數、析構函數、靜態成員。一個類可以直接繼承多個接口,但只能直接繼承一個類(包括抽象類)。
3)抽象類和接口的使用:
²如果預計要創建組件的多個版本,則創建抽象類。抽象類提供簡單的方法來控制組件版本。
²如果創建的功能將在大范圍的全異對象間使用,則使用接口。如果要設計小而簡練的功能塊,則使用接口。
²如果要設計大的功能單元,則使用抽象類.如果要在組件的所有實現間提供通用的已實現功能,則使用抽象類。
²抽象類主要用于關系密切的對象;而接口適合為不相關的類提供通用功能。
以下是我在網上看到的幾個形象比喻,真的非常不錯,呵呵:1.飛機會飛,鳥會飛,他們都繼承了同一個接口“飛”;但是F22屬于飛機抽象類,鴿子屬于鳥抽象類。2. 就像鐵門木門都是門(抽象類),你想要個門我給不了(不能實例化),但我可以給你個具體的鐵門或木門(多態);
而且只能是門,你不能說它是窗(單繼承);一個門可以有鎖(接口)也可以有門鈴(多實現)。 門(抽象類)定義了你是什么,接口(鎖)規定了你能做什么(一個接口最好只能做一件事,你不能要求鎖也能發出聲音吧(接口污染))。
假定不使用基類提供EatFood()方法,而是把該方法放到接口IConsume上,Cow和Chicken類也支持這個接口(Cow和Chicken類必須提供EatFood()方法的執行代碼),接著就可以使用下述代碼訪問該方法:
Cow myCow=new Cow();
Chicken myChicken=new Chicken();
IConsume consumeInterface;
consumeInterface=myConw;
consumeInterface.EatFood();
consumeInterface=myChicken;
consumeInterface.EatFood;
【例子1】
PRivate void button1_Click(objectsender,System.EventArgs e)
{
((CButton)sender).Text=”Clicked!”;
ButtonnewButton=new Button();
newButton.Text=”NewButton!”;
newButton.Click+=newEventHandler(newButton_Click);
Controls.Add(newButton);
}
private void newButton_Click(objectsender,System.EventArgs e)
{
((Button)sender).Text=”Clicked!”;
}
C#中只能有一個基類,如果繼承了一個抽象類,就必須實現所繼承的所有抽象成員(除非派生類也是抽象的)。一個類可指定多個接口,如:
public class MyClass: MyBase, IMyInterface,IMySecondInterface
{
}
修飾符 | 含義 |
無或internal | 類只能在當前項目中訪問 |
public | 類可以在任何地方訪問 |
abstract或internal abstract | 類只能在當前項目中訪問,不能實例化,只能繼承 |
public abstract | 類可以在任何地方訪問,不能實例化,只能繼承 |
sealed 或internal sealed | 類只能在當前項目中訪問,不能派生,只能實例化 |
public sealed | 類可以在任何地方訪問,不能派生,只能實例化 |
關鍵字abstract和sealed不能在接口中使用,因為這兩個修飾符在接口定義中無意義(接口不包含執行代碼,所以不能直接實例化,且必須是可以繼承的)。接口可使用多個基接口,如:
public interface IMyInterface:IMyBaseInterface, IMyBaseInterface2
{
}
public class MyBaseClass
{
publicMyBaseClass(){}
publicMyBaseClass(int i){}
}
public class MyDerivedClass: MyBaseClass
{
publicMyDerivedClass(){}
publicMyDerivedClass(int i){}
public MyDerivedClass(inti, int j){}
}
MyDerivedClass myObj=new MyDerivedClass();
l執行System.Object.Object()構造函數。
l執行MyBaseClass.MyBaseClass()構造函數。
l執行MyDerivedClass.MyDerivedClass()構造函數。
MyDerivedClass myObj=new MyDerivedClass(4);
l執行System.Object.Object()構造函數。
l執行MyBaseClass.MyBaseClass(int i)構造函數。
l執行MyDerivedClass.MyDerivedClass(int i)構造函數。
MyDerivedClass myObj=newMyDerivedClass(4,8);
l執行System.Object.Object()構造函數。
l執行MyBaseClass.MyBaseClass()構造函數。
l執行MyDerivedClass.MyDerivedClass(int i,int j)構造函數。
若MyDerivedClass的定義做如下修改:
public class MyDerivedClass:MyBaseClass
{
….
publicMyDerivedClass(int i, int j) : base(i)
{}
}
則MyDerivedClassmyObj=new MyDerivedClass(4,8);其執行順序如下:
l執行System.Object.Object()構造函數。
l執行MyBaseClass.MyBaseClass(i)構造函數。
l執行MyDerivedClass.MyDerivedClass(int i,int j)構造函數。
若MyDerivedClass定義作如下修改:
public class MyDerivedClass:MyBaseClass
{
publicMyDerivedClass():this(5,6)
{}
publicMyDerivedClass(int i, int j) :base(i)
}
則MyDerivedClassmyObj=new MyDerivedClass();的執行順序如下:
l執行System.Object.Object()構造函數。
l執行MyBaseClass.MyBaseClass(i)構造函數。
l執行MyDerivedClass.MyDerivedClass(int i,int j)構造函數。
l執行MyDerivedClass.MyDerivedClass()構造函數。
1)成員修飾符
public: 成員可以由任何代碼范圍
private: 成員只能由類中的代碼訪問(如果沒有使用任何關鍵字,就默認使用這個關鍵字)
internal: 成員只能由定義它的項目(程序集)內部的代碼訪問
protected:成員只能由類或派生類中的代碼訪問
2)方法修飾符
static:只能通過類訪問,不能通過對象實例化來訪問
abstract: 方法必須在非抽象的派生類中重寫(只用于抽象類中)
virtual:方法可以重寫
override: 方法重寫了一個基類方法(如果方法被重寫,就必須使用該關鍵字)
extern:方法定義放在其它地方
【例子1】字段、屬性與方法
public class MyClass
{
//使用readonly修飾,只能聲明或構造函數中賦值
publicreadonly stringName;
private intintVal;
//屬性Val的訪問器屬性為protect所以不能直接在main中使用,但可在其派生類中使用
publicint Val
{
protectedget
{
returnintVal+1;
}
//不能同時設置兩個訪問器的權限
set
{
if (value >= 0 && value <= 10)
intVal = value;
else
//使用throw拋出異常
throw (new ArgumentOutOfRangeException("Val",value,"Val 必須是0~10"));
}
}
//重寫了Object類的ToString方法,所以要用override修飾符
publicoverride stringToString()
{
return"Name:" + Name + "/nVal:" + Val;
}
//使用this關鍵字調用自身的MyClass(string newName)構造函數
privateMyClass(): this("DefaultName"){}
publicMyClass(string newName)
{
Name = newName;
intVal = 0;
}
}
public class MyClass2 : MyClass
{
Internalint myVal;
//構造函數中使用base關鍵字調用基類的構造函數
publicMyClass2() : base("MyClass2"){}
//由于基類的Val屬性的get訪問器修飾符為protected,只能在類或派生類代碼中訪問
publicint Val
{
//派生類代碼中可直接使用基類中Val屬性的get和set訪問器
get{return base.Val;}
set{base.Val = value;}
}
}
class Program
{
static void Main(string[] args)
{
MyClass2 obj2=new MyClass2();
Console.WriteLine(obj2.ToString());
obj2.myVal = 20;
Console.WriteLine("obj2.myVal={0}", obj2.myVal);
for(int i = 0; i <= 11; i++)
{
obj2.Val = i;
Console.WriteLine("intVal={0}", obj2.Val);
}
Console.ReadKey();
}
}
【例子2】類中的靜態字段和靜態方法(Ch09Ex03)
classProgram
新聞熱點
疑難解答