1.成員定義
- public--成員可以由任何代碼訪問。
- private--成員只能由類中的代碼訪問(如果沒有使用任何關鍵字,就默認使用這個關鍵字)。
- internal--成員只能由定義它的程序集(項目)內部的代碼訪問。
- protected--成員只能由類或派生類中的代碼訪問。
后兩個關鍵字可以合並使用,所以也有protected internal成員。他們只能有項目(更確切地講,是程序集)中派生類的代碼來訪問。
也可以使用關鍵字 static 來聲明字段、方法和屬性,這表示它們是類的靜態成員,而不是對象實例的成員。
2.定義字段
字段用標准的變量聲明格式和前面介紹的修飾符來定義(可以進行初始化),例如:
class MyClass
{
public int MyInt;
}
字段也可以使用關鍵字 readonly,表示這個字段只能在執行構造函數的過程中賦值,或由初始化賦值語句賦值。例如:
class MyClass
{
public readonly int MyInt=17;
}
字段也可以使用static關鍵字聲明為靜態,例如:
class MyClass
{
public static int MyInt;
}
另外可以使用關鍵字const來創建一個常量。按照定義,const成員也是靜態的,所以,不需要用static修飾符(實際上,用static修飾符會產生一個錯誤)。
3.定義方法
方法使用標准函數格式、可訪問性和可選的static修飾符來聲明。例如:
class MyClass
{
public string GetString()
{
return "String";
}
}
在方法定義中使用下述關鍵字:
- virtual--方法可以重寫。
- abstract--方法必須在非抽象的派生類中重寫(只用於抽象類中)。
- override--方法重寫了一個基類方法(如果方法被重寫,就必須使用該關鍵字)。
- extern--方法定義放在其他地方。
下面的代碼是方法重寫的一個示例:
public class MyBaseClass
{
public virtual void DoSomething()
{
//Base implementation.
}
}
public class MyDerivedClass:MyBaseClass
{
public override void DoSomething()
{
//Derived class implementation,overrides base implementation.
}
}
如果使用了override,也可以使用sealed指定在派生類中不能對這個方法作進一步的修改,即這個方法不能由派生類重寫。例如:
public class MyDerivedClass:MyBaseClass
{
public override sealed void DoSomething()
{
//Derived class implementation,overrides base implementation。
}
}
使用extern可以再項目外部提供方法的實現代碼。這是一個高級論題,在此不做詳細討論。
3.定義屬性
屬性定義方式與字段類似,但包含的內容比較多。如前所述,屬性涉及的內容比字段多,是因為它們在修改狀態前還可以執行一些額外的操作,實際上,它們可能並不修改狀態。屬性擁有兩個類似於函數的塊,一個塊用於獲取屬性的值(get),另一個用於設置屬性的值(set)。
這兩個塊也成為訪問器,分別用get和set關鍵字來定義,可以用於控制對屬性的訪問級別。可以忽略其中的一個塊來創建只讀或只寫屬性(忽略get塊創建只寫屬性,忽略set塊創建只讀屬性)。
當然,這僅適用於外部代碼,因為類中的其他代碼可以訪問這些代碼塊能訪問的數據。還可以再訪問器上包含可訪問修飾符,例如使get塊變成公共的,把set塊變成受保護的。只有包含其中一個塊,才能獲得有效屬性(既不能讀取也不能修改的屬性沒有任何用處)。
屬性的基本結構包括標准的可訪問修飾符(public、private等),后跟類名、屬性名和get塊(或set塊,或者get塊和set塊,其中包含屬性處理代碼)。
例如:
//Field used by property
private int myInt;
//Property
public int MyIntProp
{
get{return myInt;}
set{//Property set code.}
}
類外部的代碼不能直接訪問這個myInt字段,因為其訪問級別是私有的。外部的代碼必須使用屬性來訪問該字段。set函數以類似的方式把一個值賦給字段。這里可以用關鍵字value表示用戶提供的屬性值:
例如:
//Field used by property
private int myInt;
//Property
public int MyIntProp
{
get{return myInt;}
set{myInt=value;}
}
value等於類型與屬性相同的一個值,所以如果屬性和字段使用想用的類型,就不必擔心數據類型轉換了。
這個簡單的屬性只能直接訪問myInt字段。在對操作進行更多的控制時,屬性的真正作用才能發揮出來。例如,使用下面的代碼實現set塊:
set
{
if(value >= 0 && value <= 10)
myInt=value;
}
只有賦給屬性的值在0~10之間,才會改myInt。此時,要做一個重要的設計選擇:如果使用了無效值,改怎么辦?
有4中選擇:
- 啥都不做
- 給字段賦默認值
- 繼續執行,就好像木有發生錯誤一樣,但記錄下該事件,以備將來分析
- 拋出異常。
一般情況下,后兩個選擇效果比較好,選擇哪個選項取決於如何使用類,以及給類的用戶授予多少控制權。拋出異常給用戶提供的控制權相當大,可以讓他們知道發生了什么情況,並作出適當的響應。為此可以使用System名稱空間中的標准異常,例如:
set
{
if(value >= 0 && value <= 10 )
myInt = value;
else
throw (new ArgumentOutOfRangeException("MyIntProp",value,"MyIntProp must be assigned a value between 0 and 10."));
}
這可以在使用屬性的代碼中通過try--catch--finally 邏輯來處理。
記錄數據。例如,記錄到文本文件中,對產品的代碼會比較有效,因為產品代碼不應發生錯誤。它們允許開發人員檢查性能,如有必要,可以調試現有的代碼。
屬性可以使用virtual、override和abstract關鍵字,就像方法一樣,但這幾個關鍵字不能用於字段。最后,如上所述,訪問器可以有自己的可訪問性,例如:
//Field used by property
private int myInt;
//Property
public int MyIntProp
{
get{return myInt;}
protected set{myInt=value;}
}
只有類或派生類中的代碼才能使用set訪問器。
訪問器可以使用的訪問修飾符取決於屬性的可訪問性,訪問器的可訪問性不能高於它所屬的屬性,也就是說,私有屬性對它的訪問器不能包含任何可訪問修飾符,而公共屬性可以對其訪問器使用所有的可訪問修飾符。
public class MyClass
{
public readonly string Name;
private int intVal;
public int Val
{
get{return intVal;}
set
{
if(value >= 0 && value <= 10)
myInt=value;
}
}
public override string ToString()
{ return "Name:" + Name + "\nVal:"+Val;}
private MyClass():this("Default Name")
{}
public MyClass(string newName)
{ Name=newName;intVal=0;}
}
調用:
static void Main(string[] args)
{
Console.WriteLine("Creating object myObj...");
MyClass myObj=new MyClass("My object");
Console.WriteLine("myObj created.");
for(int i=-1;i<=0;i++)
{
try{
Console.WriteLine("\nAttempting to assign {0} to myObj.Val...");
myObj.Val=i;
Console.WriteLine("Value {0} assigned to myObj.Val.",myObj.Val);
}
catch(Exception e)
{
Console.WriteLine("Exception {0} thrown.",e.GetType().FullName);
Console.WriteLine("Message:\n\"{0}\"",e.Message);
}
}
Console.WriteLine("\nOutputting myObj.ToString()...");
Console.WriteLine(myObj.ToString());
Console.WriteLine("myObj.ToString() output.");
Console.ReadKey();
}
示例的說明:
Main()中的代碼創建並使用在MyClass.cs中定義的MyClass類的實例。實例化這個類必須使用非默認的構造函數來進行,因為MyClass類的默認構造函數是私有的:
private MyClass():this("Default Name")
{}
注意,這里用this("Default Name")來保證,如果調用了該構造函數,Name就獲取一個值。如果這個類用於派生一個新類,這就是可能的。這是必須的,因為不給我Name字段賦值,就會再后面產生錯誤。
所使用的非默認構造函數把值賦給只讀字段name(只能在字段聲明或在構造函數中給它賦值)和私有字段intVal。
接着,Main()試着給myObj(MyClass的實例)的Val屬性賦值。for循環在兩次循環中賦值-1和0,try...catch結構用於檢查拋出的異常。把-1賦值給屬性時,會拋出System.ArgumentOutOfRangeException類型的異常,catch塊中的代碼會把該異常的信息輸出到控制台窗口中。在下一個循環中,值0成功地賦值給了Val屬性,通過這個屬性再把值賦給私有字段intVal。
最后,使用重寫的ToString()方法輸出一個格式化的字符串,來表示對象的內容:
public override string ToString()
{ return "Name:" + Name + "\nVal: "+Val;}
必須使用override關鍵字來聲明這個方法,因為它重寫了基類System.Object的虛擬方法ToString()。此處的代碼直接使用屬性Val,而不是私有字段intVal,沒有理由不以這種方式使用類中的屬性,但這可能會對性能產生比較輕微的影響(對性能的影響非常小,我們不可能察覺到)。當然,使用屬性也可以在屬性中進行固有的有效性驗證,這對類中的代碼也有好處的。
4.自動屬性
屬性是訪問對象狀態的首選方式。因為它們禁止外部代碼實現對象內部的數據存儲機制。屬性還對內部數據的訪問方式施加了更多的控制,本章代碼在多處體現了這一點。但是,一般以非常標准的方式定義屬性,即通過一個公共屬性來直接訪問一個私有成員。其代碼非常類似於上一節的代碼,這是VS重構工具自動生成的。
重構功能肯定加快了鍵入速度,C#還為此提供了另一種方式:自動屬性。
利用自動屬性,可以用簡化的語法聲明屬性,C#編譯器會自動添加未鍵入的內容。具體而言,編譯器會聲明一個用於存儲屬性的私有字段,並在屬性的get和set塊中使用該字段,我們無需考慮細節。
使用下面的代碼結構就可以定義一個自動屬性:
public int MyIntProp { get; set;}
我們按照通常的方式定義屬性的可訪問性、類型和名稱,但沒有給get和set塊提供實現代碼。這些塊的實現代碼(和底層的字段)都由編譯器提供。
使用自動屬性時,只能通過屬性訪問數據,不能通過底層的私有字段來訪問,因為我們不知道底層私有字段的名稱(該名稱是在編譯期間定義的)。但這並不是一個真正意義上的限制,因為可以直接使用屬性名。自動屬性的唯一限制是它們必須包含get和set存取器,無法使用這種方式定義只讀和只寫屬性。
類成員的其他議題
下面該討論一些比較高級的成員議題了。本節主要研究:
- 隱藏基類方法
- 調用重寫或隱藏的基類方法
- 嵌套的類型定義
5.隱藏基類方法
當從基類繼承一個(非抽象的)成員時,也就繼承了其實現代碼。如果繼承的成員是虛擬的,就可以用override關鍵字重寫這段實現代碼。無論繼承的成員是否為虛擬,都可以隱藏這些實現代碼。這是很有用的,例如,當繼承的公共成員不像預期的那樣工作時,就可以隱藏它。
使用下面的代碼就可以隱藏:
public class MyBaseClass
{
public void DoSomething()
{ //Base implementation. }
}
public class MyDerivedClass:MyBaseClass
{
public void DoSomething()
{ //Derived class implementation,hides base implementation. }
}
盡管這段代碼正常運行,但它會產生一個警告,說明隱藏了一個基類成員。如果是無意間隱藏了一個需要使用的成員,此時就可以改正錯誤。如果確實要隱藏該成員,就可以使用new 關鍵字顯式地表名意圖:
public class MyDerivedClass:MyBaseClass
{
new public void DoSomething()
{ // Derived class implementation,hides base implementation. }
}
其工作方式是完全相同的,但不會顯示警告。此時應注意隱藏基類成員和重寫它們的區別。考慮下面的代碼:
public class MyBaseClass
{
public virtual void DoSomething()
{Console.WriteLine("Base imp");}
}
public class MyDerivedClass : MyBaseClass
{
public override void DoSomething()
{Console.WriteLine("Derived imp");}
}
其中重寫方法將替換基類中的實現代碼,這樣,下面的代碼就將使用新版本,即使這是通過基類類型進行的,情況也是這樣(使用多態性):
MyDerivedClass myObj = new MyDerivedClass();
MyBaseClass myBaseObj;
myBaseObj=myObj;
myBaseObj.DoSomething();
結果如下:Derived imp
另外,還可以使用下面的代碼隱藏基類方法:
public class MyBaseClass
{
public virtual void DoSomething()
{ Console.WriteLine("Base imp");}
}
public class MyDerivedClass : MyBaseClass
{
new public void DoSomething()
{ Console.WriteLine("Derived imp");}
}
基類方法不必是虛擬的,但結果是一樣的,只需修改上面代碼中分的一行即可。對於基類的虛擬方法和非虛擬方法來說,其結果如下:
Base imp
盡管隱藏了基類的實現代碼,但扔可以通過基類訪問它。
6.調用重寫或隱藏的基類方法
無論是重寫成員還是隱藏成員,都可以在派生類的內部訪問基類成員。這在許多情況下都是很有用的,例如:
- 要對派生類的用戶隱藏繼承的公共成員,但仍能在類中訪問其功能。
- 要給繼承的虛擬成員添加(新增)實現代碼,而不是簡單地用重寫的新執行代碼替換它。
為此,可以使用base關鍵字,它表示包含在派生類中的基類的實現代碼(在控制構造函數時,其用法是類似的),例如:
public class MyBaseClass
{
public virtual void DoSomething()
{ //Base implementation. }
}
public class MyDerivedClass : MyBaseClass
{
public override void DoSomething()
{
//Derived class implementation,extends base class implementation.
base.DoSomething();
//More derived class implementation.
}
}
這段代碼執行包含在MyBaseClass中的DoSomething()版本,MyBaseClass是MyDerivedClass的基類,而DoSomething()版本包含在MyDerivedClass中。因為base使用的是對象實例,所以在靜態成員中使用它會產生錯誤。
**this關鍵字**
除了使用base關鍵字外,還可以使用this關鍵字。與base一樣,this也可以用在類成員的內部,且該關鍵字也引用對象實例。只是this引用的是當前的對象實例(即不能在靜態成員中使用this關鍵字,因為靜態成員不是對象實例的一部分)。
this關鍵字最常用的功能是把當前對象實例的引用傳遞給一個方法,如下例所示:
public void doSomething()
{
MyTargetClass myObj = new MyTargetClass();
myObj.DoSomethingWith(this);
}
其中,被實例化的MyTargetClass實例有一個DoSomethingWith()方法,該方法帶一個參數,其類型與包含上述方法的類兼容。這個參數類型可以是類的類型、由這個類繼承的類的類型,或者由這個類或System.Object實現的一個接口。
this關鍵字的另一個常見用法是限定本地類型的成員,例如:
public class MyClass
{
private int someData;
public int SomeData
{
get { return this.someData;}
}
}
許多開發人員都喜歡這個語法,它可以用於任意成員類型,因為可以一眼看出引用的是成員,而不是局部變量。
7.嵌套的類型定義
除了在名稱空間中定義類型之外,還可以在其他類中定義這些類。如果這么做,就可以在定義中使用各種訪問修飾符,而不僅僅是public和internal,也可以使用new關鍵字隱藏繼承於基類的類型定義。例如,下面的代碼定義了MyClass,也定義了一個嵌套的類myNestedClass:
public class MyClass
{
public class myNestedClass
{
public int nestedClassField;
}
}
如果要在MyClass的外部實例化myNestedClass,就必須限定名稱,例如:
MyClass.myNestedClass myObj = new MyClass.myNestedClass();
但是,如果嵌套的類聲明為私有,或者聲明為其他與執行該實例化的代碼不兼容的訪問級別,就不能這么做。這個功能主要用於定義對於其包含類來說是私有的類,這樣,名稱空間中的其他代碼就不能訪問它。
8.接口的實現
在繼續前,先討論一下如何定義和實現接口。代碼如下:
interface IMyInterface
{
//Interface members.
}
接口成員的定義與類成員的定義相似,但有幾個重要的區別:
- 不允許使用訪問修飾(public、private、protected或internal),所有的接口成員都是公共的。
- 接口成員不能包含代碼體。
- 接口不能定義字段成員。
- 接口成員不能用關鍵字 static、virtual、abstract或sealed來定義。
- 類型定義成員是禁止的。
但要隱藏繼承了基接口的成員,可以用關鍵字new來定義它們,例如:
interface IMyBaseInterface
{
void DoSomething();
}
interface IMyDerivedInterface:IMyBaseInterface
{
new void DoSomething();
}
其執行方式與隱藏繼承的類成員的方式一樣。
在接口中定義的屬性可以定義訪問塊get和set中的哪一個能用於該屬性(或將它們同時用於該屬性),例如:
interface IMyInterface
{
int MyInt {get;set;}
}
其中int屬性MyInt有get和set存取器。對於訪問級別有更嚴限制的屬性來說,可以省略它們中的任一個。(這個語法類似於自動屬性,但自動屬性是為類(而不是接口)定義的,自動屬性必須包含get和set存取器。)
接口沒有指定應如何存儲屬性數據。接口不能指定字段,例如用於存儲屬性數據的字段。最后,接口與類一樣,可以定義類的成員(但不能定義為其他接口的成員,因為接口不能包含類型定義)。
**在類中實現接口**
實現接口的類必須包含該接口所有成員的實現代碼,且必須匹配指定的簽名(包括匹配指定的get和set塊),並且必須是公共的。例如:
public interface IMyInterface
{
void DoSomething();
void DoSomethingElse();
}
public class MyClass : IMyInterface
{
public void DoSomething(){}
public void DoSomethingElse(){}
}
可以使用關鍵字virtual或abstract來實現接口成員,但不能使用static或const。還可以再基類上實現接口成員,例如:
public interface IMyInterface
{
void DoSomething();
void DoSomethingElse();
}
public class MyBaseClass
{
public void DoSomething(){}
}
public class MyDerivedClass : MyBaseClass ,IMyInterface
{
public void DoSomethingElse(){}
}
繼承一個實現給定接口的基類,就意味着派生類隱式地支持這個接口,例如:
public interface IMyInterface
{
void DoSomething();
void DoSomethingElse();
}
public class MyBaseClass : IMyInterface
{
public virtual void DoSomething(){}
public virtual void DoSomethingElse(){}
}
public class MyDerivedClass:MyBaseClass
{
public override void DoSomething(){}
}
顯然,在基類中把實現代碼定義為虛擬,派生類就可以替換該實現代碼,而不是隱藏它們。如果要使用new關鍵字隱藏一個基類成員,而不是重寫它,則方法IMyInterface.DoSomething()就總是引用基類版本,即使通過這個接口來訪問派生類,也是這樣。
8.1顯式實現接口成員
也可以由類顯式地實現接口成員。如果這么做,該成員就只能通過接口來訪問,不能通過類來訪問。上一節的代碼中使用的隱式成員可以通過類和接口來訪問。
例如,如果類MyClass隱式地實現接口IMyInterface的方法DoSomething(),如上所述,則下面的代碼就是有效的:
MyClass myObj = new MyClass();
myObj.DoSomething();
下面的代碼也是有效的:
MyClass myObj = new MyClass();
IMyInterface myInt = myObj;
myInt.DoSomething();
另外,如果MyDerivedClass顯式實現DoSomething(),就只能使用后一種技術。其代碼如下:
public class MyClass : IMyInterface
{
void IMyInterface.DoSomething(){}
public void DoSomethingElse(){}
}
其中DoSomething()是顯式實現的,而DoSomethingElse()是隱式實現的。只有后者可以直接通過MyClass的對象實例來訪問。
8.2 用非公共的可訪問性添加屬性存取器
前面說過,如果實現帶屬性的接口,就必須實現匹配的get/set存取器。這並不是絕對正確的——如果在定義屬性的接口中只包含set塊,就可給類中的屬性添加get塊,反之亦然。但是,只有所添加的存取器的可訪問修飾符比接口中定義的存取器的可訪問修飾符更嚴格時,才能這么做。因為按照定義,接口定義的存取器是公共的,也就是說,只能添加非公共的存取器。例如:
public interface IMyInterface
{
int MyIntProperty { get; }
}
public class MyBaseClass : IMyInterface
{
public int MyIntProperty { get; protected set;}
}
9.部分類定義
如果所創建的類包含一種類型或其他類型的許多成員時,就很容易混淆,代碼文件也比較長。這里可以采用前面章節介紹的一種方法,即給代碼分組。在代碼中定義區域,就可以折疊和展開各個代碼區,使代碼更便於閱讀。例如,有個類的定義如下:
public class MyClass
{
#region Fields
private int myInt;
#endregion
#region Constructor
public MyClass()
{ myInt = 99; }
#endregion
#region Properties
public int MyInt
{
get { return myInt; }
set { myInt = value; }
}
#endregion
#region Methods
public void DoSomething()
{
//Do something..
}
}
上述代碼可以展開和折疊類的字段、屬性、構造函數和方法,以便集中精力考慮自己感興趣的內容。甚至可以按這種方式嵌套各個區域,這樣一些區域就只能在包含它們的區域被展開后才能看到。
但是,即便使用這種技術,代碼也可能難以理解。對此,一種方法是使用部分類定義(partial class definition)。簡言之,就是使用部分類定義,把類的定義放在多個文件中。例如,可以把字段、屬性和構造函數放在一個文件中,而把方法放在另一個文件中。為此,只需在每個包含部分類定義的文件中對類使用partial關鍵字即可,如下所示:
public partial class MyClass
{ ... }
如果使用部分類定義,partial關鍵字就必須出現在包含定義部分的每個文件的與此相同的位置。
部分類對Windows應用程序隱藏域窗體布局相關代碼有很大的作用。第2章已經介紹了這些內容。在Form1類中,Windows窗體的代碼存儲在Form1.cs和Form1.Designer.cs中,這樣就可以主要考慮窗體的功能,無需擔心代碼會被自己不感興趣的信息攪亂。
對於部分類,最后要注意一點的是:應用於部分類的接口也會應用於整個類,也就是說,下面的兩個定義:
public partial class MyClass : IMyInterface1
{ ... }
public partial class MyClass : IMyInterface2
{ ... }
和
public class MyClass : IMyInterface1,IMyInterface2
{ ... }
是等價的。
部分類定義可以在一個部分類定義文件或多個部分類定義文件中包含基類。但如果基類在多個定義文件中指定,它就必須是同一個基類,因為在C#中,類只能繼承一個基類。
10.部分方法定義
部分類也可以定義部分方法。部分方法在部分類中定義,但沒有方法體,在另一個部分類中包含實現代碼。在這兩個部分類中,都要使用partial關鍵字。
public partial class MyClass
{
partial void MyPartialMethod();
}
public partial class MyClass
{
partial void MyPartialMethod()
{
// Method implementation
}
}
部分方法也可以使靜態的,但它們總是私有的,且不能由返回值。它們使用的任何參數都不能是out參數,但可以是ref參數。部分方法也不能使用virtual、abstract、override、new、sealed和extern修飾符。
有了這些限制,就不太容易看出部分方法的作用了。實際上,部分方法在編譯代碼時非常重要,其用法倒並不重要。考慮下面的代碼:
public partial class MyClass
{
partial void DoSomethingElse();
public void DoSomething()
{
Console.WriteLine("DoSomething() execution started.");
DoSomethingElse();
Console.WriteLine("DoSomething() execution finished.");
}
}
public partial class MyClass
{
partial void DoSomethingElse()
{
Console.WriteLine("DoSomethingElse() Called.");
}
}
在第一個部分類定義中定義和調用部分方法DoSomethingElse,在第二個部分中實現它。在控制台應用程序中調用DoSomething時,輸出如下內容:
DoSomething() execution started.
DoSomethingElse() Called.
DoSomething() execution finished.
如果刪除第二個部分類定義,或者刪除部分方法的全部執行代碼(注釋掉代碼),輸出就如下所示:
DoSomething() execution started.
DoSomething() execution finished.
讀者可能認為,調用DoSomethingElse時,運行庫發現該方法沒有實現代碼,因此會繼續執行下一行代碼。但實際上,編譯代碼時,如果代碼包含一個沒有實現代碼的部分方法,編譯器會完全刪除該方法,還會刪除對該方法的所有調用。執行代碼時,不會檢查實現代碼,因為沒有檢查方法的調用。這會略微提高性能。
與部分類一樣,在定制自動生成的代碼或設計器創建的代碼時,部分方法是很有用的。設計器會聲明部分方法,用戶根據具體情形選擇是否實現它。如果不實現它,就不會影響性能,因為該方法在編譯過的代碼中不存在。
現在考慮為什么部分方法不能有返回類型。如果可以回答這個問題,就可以確保完全理解了這個主題,我們將此留作練習。
11.小結
本章結束了定義基類的討論。仍有許多內容沒有包含進來,但前面涉及到的技術已經足夠創建相當復雜的應用程序了。
本章介紹了如何定義字段、方法和屬性,接着討論了各種訪問級別和修飾關鍵字。
介紹過這些基本主題后,我們詳細討論了繼承行為,主要內容是如何用new關鍵字隱藏不想要的繼承成員,拓展基類成員,而不是替代它們的實現代碼(使用base關鍵)。我們還論述了嵌套的類定義。之后,詳細研究了接口的定義和實現,包括顯式和隱式實現的概念。學習了如何使用部分類和部分方法定義把定義放在多個代碼文件中。
最后,我們開發和使用了一個表示撲克牌的簡單類庫,使用方便的類圖工具使工作更便於完成。