C#圖解教程 第六章 深入理解類
深入理解類
類成員
前兩章闡述了9種類成員中的兩種:欄位和方法。本章將會介紹除事件(第14章)和運算子外的其他類成員,並討論其特徵。
成員修飾符的順序
欄位和方法的宣告可以包括許多如public、private這樣的修飾符。本章還會討論許多其他修飾符。多個修飾符一起使用時,它們需要怎麼排序呢?
[特性] [修飾符] 核心宣告
- 修飾符
- 修飾符,必須放在核心宣告前
- 多個修飾符順序任意
- 特性
- 特性,必須放在修飾符和核心宣告前
- 多個特性順序任意
例如,public和static都是修飾符,可以用在一起修飾某個宣告。因為它們都是修飾符,所以順序任意。下面兩行程式碼是語義等價的:
public static int MaxVal; static public int MaxVal;
例項類成員
類的每個例項擁有自己的各個類成員的副本,這些成員稱為例項成員。
改變一個例項欄位的值不會影響任何其他例項成員中的值。
例
class D { public int Mem1; } class Progarm { static void Main() { D d1=new D(); D d2=new D(); d1.Mem1=10; d2.Mem1=28; Console.WriteLine("d1={0},d2={1}",d1.Mem1,d2.Mem2); } }
靜態欄位
除了例項欄位,類還可以擁有靜態欄位。
- 靜態欄位被類的所有例項共享,所有例項訪問同一記憶體位置。因此,如果該記憶體位置的值被一個例項改變了,這種改變對所有例項都可見。
- 可以使用static修飾符將欄位宣告為靜態
class D { int Mem1; //例項欄位 static int Mem2; //靜態欄位 }
例:靜態欄位演示
- 因為Mem2是靜態的,類D的兩個例項共享單一的Mem2欄位。如果Mem2被改變了,這個改變在兩個例項中都能看到
- 成員Mem1沒有宣告為static,所以每個例項都有自己的副本
class D { int Mem1; static int Mem2; ... } static void Main() { D d1=new D(); D d2=new D(); ... }
從類的外部訪問靜態成員
靜態成員可以使用點運算子從類的外部訪問。但因為沒有例項,所以必須使用類名。
類名 ↓ D.Mem2=5; ↑ 成員名
靜態欄位示例
- 一個方法設定兩個資料成員的值
- 另一個方法顯示兩個資料成員的值
class D { int Mem1; static int Mem2; public void SetVars(int v1,int v2) { Mem1=v1; Mem2=v2; } public void Display(string str) { Console.WriteLine("{0}:Mem1={1},Mem2={2}",str,Mem1,Mem2); } } class Program { static void Main() { D d1=new D(),d2=new D(); d1.SetVars(2,4); d1.Display("d1"); d2.SetVars(15,17); d2.Display("d2"); d1.Display("d1"); } }
靜態成員的生存期
- 之前我們已經看到了,只有在例項建立後才產生例項成員,例項銷燬後例項成員也就不在存在
- 即使類沒有例項,也存在靜態成員,並可以訪問
class D { int Mem1; static int Mem2; ... } static void Main() { D.Mem2=5; Console.WriteLine("Mem2={0}",D.Mem2); }
欄位與類有關,與例項無關
靜態成員即使沒有類的例項也存在。如果靜態欄位有初始化語句,那麼會在使用該類的任何靜態成員之前初始化該欄位,但沒必要在程式執行的開始就初始化。
靜態函式成員
- 如同靜態欄位,靜態函式成員獨立於任何類例項。無需類例項就可以呼叫靜態方法
- 靜態函式成員不能訪問例項成員。只能訪問靜態成員
例:靜態函式與靜態欄位
class X { static public int A; static public void PrintValA() { Console.WriteLine("Value of A:{0}",A); } } class Program { static void Main() { X.A=10; X.PrintValA(); } }
其他靜態類成員型別
下表中為可以宣告為static的類成員型別做了√標記
成員常量
成員常量類似本地常量,只是它被宣告在類宣告中而不是方法內。
class MyClass { const int IntVal=100;//定義值為100的int型別常量 } const double PI=3.1416; //錯誤:不能在型別宣告之外
與本地常量類似,初始化成員常量的值在編譯時必須是可計算的。
class MyClass { const int IntVal1=100; const int IntVal2=2*IntVal1;//正確:因為IntVal的值在前一行已設定 }
與本地常量類似,不能在成員常量聲明後給它賦值
class MyClass { const int IntVal//錯誤:宣告時必須初始化 IntVal=100; //錯誤:不允許賦值 }
與C和C++不同,在C#中沒有全域性變數。每個常量都必須宣告在型別內。
常量與靜態量
成員常量比本地常量更有趣,因為它們表現得像靜態值。它們對類的每個例項都是“可見的”,而且即使沒有類的例項也可以用。與真正的靜態量不同,常量沒有自己的儲存位置,而是在編譯時被編譯器替換。常量類似C和C++中的#define。
正因為常量在記憶體沒有儲存位置,所以它也不能作為左值(被賦值)。
static靜態量是有自己的儲存位置的。
例:常量示例
class X { public const double PI=3.1416; } class Program { static void Main() { Console.WriteLine("pi={0}",X.PI); } }
雖然常量成員表現得像一個靜態量,但不能將常量宣告為static
static const double PI=3.14;//錯誤:不能將常量宣告為static
屬性
屬性代表類的例項或類中的一個數據項成員。使用屬性看起來像寫入或讀取一個欄位,它們語法相同。
與欄位類似,屬性有如下特徵
- 它是命名的類成員
- 它有型別
- 它可以被賦值和讀取
然而和欄位不同,屬性是一個函式成員
- 它不為資料儲存分配記憶體
- 它是執行程式碼
屬性是指定的一組兩個匹配的、稱為訪問器的方法
- set訪問器為屬性賦值
- get訪問器從屬性獲取
屬性宣告和訪問器
- set訪問器
- 擁有一個單獨的、隱式的值參,名稱為value,與屬性的型別相同
- 擁有一個返回型別void
- get訪問器
- 沒有引數
- 擁有一個與屬性型別相同的返回型別
訪問器的其他重點
- get訪問器的所有執行路徑必須包含一條return語句,返回一個屬性型別的值
- 訪問器set、get順序任意,除這兩個訪問器外在屬性上不允許有其他方法
屬性示例
例:名為C1的類,包含一個名為MyValue的屬性
- 屬性本身沒有任何儲存。訪問器決定如何處理髮進來的資料,以及什麼資料發出去。示例中,屬性使用TheRealValue欄位作為儲存
- set訪問器接受它的輸入引數value,並把值賦給欄位TheRealValue
- get訪問器只是返回欄位TheRealValue的值
class C1 { private int TheRealValue;//欄位:分配記憶體 public int MyValue //屬性:不分配記憶體 { set { TheRealValue=value; } get { return TheRealValue; } } }
使用屬性
- 要寫入屬性,在賦值語句的左邊使用屬性名
- 要讀取屬性,把屬性名用在表示式中
- 屬性根據是寫入還是讀取,隱式呼叫訪問器。(不能顯示呼叫訪問器)
int MyValue { get{...} set{...} } ... MyValue=5; z=MyValue;
屬性和關聯欄位
正如下文C1示例中的。一種常見的方式是在類中將欄位宣告為private以封裝欄位,並宣告一個public屬性來控制從類的外部對該欄位的訪問。
和屬性關聯的欄位常被稱為後備欄位、後備儲存。
例:使用public的MyValue來控制對private的TheRealValue的訪問
class C1 { private int TheRealValue=10;//欄位:分配記憶體 public int MyValue //屬性:不分配記憶體 { set{TheRealValue=value;} get{return TheRealValue;} } } class Program { static void Main() { C1 c=new C1(); Console.WriteLine("MyValue:{0}",c.MyValue); c.MyValue=20; Console.WriteLine("MyValue:{0}",c.MyValue); } }
屬性和它後備欄位的命名有兩種約定。
約定一:屬性使用Pascal大小寫,欄位使用Camel大小寫。雖然這違反了“僅使用大小寫區分不同識別符號是壞習慣”。但勝在簡單,有意義。
約定二:屬性使用Pascal大小寫,欄位使用Camel大小寫並在開頭加"_"號。
private int firstField; public int FirstField { get{return firstField;} set{firstField=value;} } private int _secondField; public int SecondField { get{return _secondField;} set{_secondField=value;} }
執行其他計算
屬性訪問器不僅對後備欄位傳進傳出資料。也可以執行任何計算。
例:通過set屬性訪問器限制Hour的最大值為24
int Hour=12; int MyValue { set { Hour=value>24?24:value; } get { return Hour; } }
上面示例中,演示的條件運算子,將在第8章詳細闡述。
條件運算子是一種三元運算子,計算問號前的表示式,如果表示式結果為true,則返回問號後第一個表示式,否則,返回冒號後的表示式。
只讀和只寫屬性
- 只有get訪問器的屬性稱為只讀屬性。只讀屬性是一種安全的,把一項資料從類或類的例項中傳出,而不允許太多訪問方法
- 只有set訪問器的屬性稱為只寫屬性,只寫屬性是一種安全的,把一項資料從類的外部傳入類,而不允許太多訪問方法
- 兩個訪問器中至少要定義一個
屬性與公共欄位
按照推薦的編碼實踐,屬性比公共欄位更好
- 屬性是函式型成員而不是資料成員,允許你處理輸入和輸出,而公共欄位不行
- 屬性可以只讀或只寫,欄位不行
- 編譯後的變數和編譯後的屬性語義不同
計算只讀屬性示例
例:類RightTriangle(直角三角形)的只讀屬性Hypotenuse(斜邊)
- 它有兩個公有欄位,表示直角三角形的兩個直角邊長度。這些欄位可以被寫入、讀取
- 第三邊由屬性Hypotenuse表示,是隻讀屬性,其返回值基於另外兩邊長度
class RightTriangle { public double A=3; public double B=4; public double Hypotenuse { get{return Math.Sqrt((A*A)+(B*B));} } } class Program { static void Main() { var c=new RightTriangle(); Console.WriteLine("Hypotenuse:{0}",c.Hypotenuse); } }
自動實現屬性
因為屬性經常關聯到後備欄位,C#提供了自動實現屬性(automatically implemented property),允許只宣告屬性而不聲明後備欄位。編譯器為你建立隱藏的後備欄位,並且欄位掛接到get和set訪問器上。
自動屬性的要點如下
- 不聲明後備欄位-編譯器根據屬性型別分配儲存
- 不能提供訪問器的方法體-它們必須被簡單地宣告為分號。get相當於簡單的記憶體讀,set相當於簡單的寫
- 除非通過訪問器,否則不能訪問後備欄位。因為不能用其他方法訪問,所以實現只讀和只寫屬性沒有意義,因此使用自動屬性必須同時提供讀寫訪問器。
例:自動屬性
class C1 { public int MyValue //屬性:分配記憶體 { set;get; } } class Program { static void Main() { C1 c=new C1(); Console.WriteLine("MyValue:{0}",c.MyValue); c.MyValue=20; Console.WriteLine("MyValue:{0}",c.MyValue); } }
除方便以外,自動屬性使你在傾向於使用公有欄位的地方很容易用屬性將其替代。
靜態屬性
屬性也可以宣告為static。靜態屬性的訪問器和靜態成員一樣,具有以下特點
- 不能訪問類的例項成員–它們能被例項成員訪問
- 不管類是否有例項,它們都存在
- 當從類的外部訪問時,必需使用類名引用
例:靜態屬性
class Trivial { public static int MyValue{get;set;} public void PrintValue() { Console.WriteLine("Value from inside:{0}",MyValue); } } class Program { static void Main() { Console.WriteLine("Init Value:{0}",Trival.MyValue); Trival.MyValue=10; Console.WriteLine("New Value:{0}",Trival.MyValue); var tr=new Trivial(); tr.PrintValue(); } }
例項建構函式
例項建構函式是一個特殊的方法,它在建立類的每個新例項時執行。
- 建構函式用於初始化類例項的狀態
- 如果希望從類的外部建立類的例項,需要將建構函式宣告為public
class MyClass { 和類名相同 ↓ public MyClass() { ↑ 沒有返回型別 ... } }
- 建構函式的名稱與類相同
- 建構函式不能有返回值
例:使用建構函式初始化TimeOfInstantiation欄位為當前時間
class MyClass { DateTime TimeOfInstantiation; ... public MyClass() { TimeOfInstantiation=DateTime.Now; } ... }
在學完靜態屬性後,我們可以仔細看看初始化TimeOfInstantiation那一行。DateTime類(實際上它是一個結構,但由於還沒介紹結構,你可以先把它當成類)是從BCL中引入的,Now是類DateTime的靜態屬性。Now屬性建立一個新的DateTime類例項,將其初始化為系統時鐘中的當前日期和時間,並返回新DateTime例項的引用。
帶引數的建構函式
- 建構函式可以帶引數。引數語法和其他方法完全相同
- 建構函式可以被過載
例:有3個建構函式的Class
class Class1 { int Id; string Name; public Class1(){Id=28;Name="Nemo";} public Class1(int val){Id=val;Name="Nemo";} public Class1(String name){Name=name;} public void SoundOff() { Console.WriteLine("Name{0},Id{1}",Name,Id); } } class Program { static void Main() { CLass1 a=new Class1(), b=new Class1(7), c=new Class1("Bill"); a.SoundOff(); b.SoundOff(); c.SoundOff(); } }
預設建構函式
如果在類的宣告中沒有顯式的提供例項建構函式,那麼編譯器會提供一個隱式的預設建構函式,它有以下特徵。
- 沒有引數
- 方法體為空
只要你聲明瞭建構函式,編譯器就不再提供預設建構函式。
例:顯式聲明瞭兩個建構函式的Class2
class Class2 { public Class2(int Value){...} public Class2(string Value){...} } class Program { static void Main() { Class2 a=new Class2();//錯誤!沒有無引數的建構函式 ... } }
- 因為已經聲明瞭建構函式,所以編譯器不提供無引數的預設建構函式
- 在Main中試圖使用無引數的建構函式建立例項,編譯器產生一條錯誤資訊
靜態建構函式
例項建構函式初始化類的每個新例項,static建構函式初始化類級別的項。通常,靜態建構函式初始化類的靜態欄位。
- 初始化類級別的項
- 在引用任何靜態成員之前
- 在建立類的任何例項之前
- 靜態建構函式在以下方面與例項建構函式類似
- 靜態建構函式的名稱和類名相同
- 建構函式不能返回值
- 靜態建構函式在以下方面和例項建構函式不同
- 靜態建構函式宣告中使用static
- 類只能有一個靜態建構函式,而且不能帶引數
- 靜態建構函式不能有訪問修飾符
class Class1 { static Class1 { ... } }
關於靜態建構函式還有其他要點
- 類既可以有靜態建構函式也可以有例項建構函式
- 如同靜態方法,靜態建構函式不能訪問類的例項成員,因此也不能是一個this訪問器
- 不能從程式中顯式呼叫靜態建構函式,系統會自動呼叫它們,在:
- 類的任何例項被建立前
- 類的任何靜態成員被引用前
靜態建構函式示例
class RandomNumberClass { private static Random RandomKey; static RandomNumberClass() { RandomKey=new Random(); } public int GetRandomNumber() { return RandomKey.Next(); } } class Program { static void Main() { var a=new RandomNumberClass(); var b=new RandomNumberClass(); Console.WriteLine("Next Random #:{0}",a.GetRandomNumber()); Console.WriteLine("Next Random #:{0}",b.GetRandomNumber()); } }
物件初始化語句
物件初始化語句擴充套件了建立語法,允許你在建立新的物件例項時,設定欄位和屬性的值。
例:
new Point {X=5,Y=6};
- 建立物件的程式碼必須能夠訪問初始化的欄位和屬性。如上例中,X和Y必須是public
- 初始化發生在構造方法執行之後,因為構造方法中設定的值可能會在物件初始化中重置為不同的值
public class Point { public int X=1; public int Y=2; } class Program { static void Main() { var pt1=new Point(); var pt2=new Point(X=5,Y=6); Console.WriteLine("pt1:{0},{1}",pt1.X,pt1.Y); Console.WriteLine("pt2:{0},{1}",pt2.X,pt2.Y); } }
解構函式
解構函式(destructor)執行在類的例項被銷燬前需要的清理或釋放非託管資源行為。非託管資源通過Win32 API獲得檔案控制代碼,或非託管記憶體塊。使用.NET資源無法得到它們,因此如果堅持使用.NET類,就無需為類編寫解構函式。
因此,我們等到第25章再描述解構函式。
readonly修飾符
欄位可用readonly修飾。其作用類似於將欄位宣告為const,一旦值被設定就不能改變。
- const欄位只能在欄位宣告語句中初始化,而readonly欄位可以在下列任意位置設定它的值
- 欄位宣告語句,類似const
- 類的任何建構函式。如果是static欄位,初始化必須在靜態建構函式中完成
- const欄位的值必須在編譯時決定,而readonly欄位值可以在執行時決定。這種增加的自由性允許你在不同環境或建構函式中設定不同的值
- 和const不同,const的行為總是靜態的,而readonly欄位有以下兩點
- 它可以是例項欄位,也可以是靜態欄位
- 它在記憶體中有儲存位置
例:Shape類,兩個readonly欄位
- 欄位PI在它的宣告中初始化
- 欄位NumberOfSides根據呼叫的建構函式被設定為3或4
class Shape { readonly double PI=3.1416; readonly int NumberOfSides; public Shape(double side1,double side2) { // 矩形 NumberOfSides=4; ... } public Shape(double side1,double side2,double side3) { // 三角形 NumberOfSides=3; ... } }
this關鍵字
this關鍵字在類中使用,表示對當前例項的引用。它只能被用在下列類成員的程式碼塊中。
- 例項建構函式
- 例項方法
- 屬性和索引器的例項訪問器
靜態成員不是例項的一部分,所以不能在靜態函式成員中使用this。換句話說,this用於下列目的:
- 用於區分類的成員和本地變數或引數
- 作為呼叫方法的實參
例:MyClass類,在方法內使用this關鍵字區分兩個Var1
class MyClass { int Var1=10; public int ReturnMaxSum(int Var1) { 引數 欄位 ↓ ↓ return Var1>this.Var1?Var1:this.Var1; } } class Program { static void Main() { var mc=new MyClass(); Console.WriteLine("Max:{0}",mc.ReturnMaxSum(30)); Console.WriteLine("Max:{0}",mc.ReturnMaxSum(5)); } }
索引器
假如我們定義一個Employee類,它帶有3個string型欄位,如果不用索引器,我們用欄位名訪問它們。
class Employee { public string LastName; public string FirstName; public string CityOfBirth; } class Program { static void Main() { var emp1=new Employee(); emp1.LaseName="Doe"; emp1.FirstName="Jane"; emp1.CityOfBirth="Dallas"; } }
如果能使用索引訪問它們將會很方便,好像該例項是欄位的陣列一樣。
static void Main() { var emp1=new Employee(); emp1[0]="Doe"; emp1[1]="Jane"; emp1[2]="Dallas"; }
什麼是索引器
索引器是一組get和set訪問器,與屬性類似。
索引器和屬性
索引器和屬性在很多方法類似
- 和屬性一樣,索引器不用分配記憶體來儲存
- 索引器通常表示多個數據成員
可以認為索引器是為類的多個數據成員提供get、set屬性。通過索引器,可以在許多可能的資料成員中進行選擇。索引器本身可以是任何型別。
關於索引器的注意事項
- 和屬性一樣,索引器可以只有一個訪問器,也可以兩個都有
- 索引器總是例項成員,因此不能宣告為static
- 和屬性一樣,實現get、set訪問器的程式碼不必一定關聯到某欄位或屬性。這段程式碼可以什麼都不做,只要get訪問器返回某個指定型別值即可
宣告索引器
- 索引器沒有名稱。在名稱的位置,關鍵詞是this
- 引數列表在方括號中
- 引數列表中至少宣告一個引數
Return Type this [Type param1,...] { get{...} set{...} }
宣告索引器類似於宣告屬性。
索引器的set訪問器
當索引器被用於賦值時,set訪問器被呼叫,並接受兩項資料
- 一個隱式引數,名為value,value持有要儲存的資料
- 一個或多個索引引數,表示資料應該儲存在哪裡
下圖例表明set訪問器有如下語義
- 它的返回型別為void
- 它使用的引數列表和索引器宣告中的相同
- 它有一個名為value的隱式引數,值參型別和索引型別相同
索引器的get訪問器
get訪問器方法體內的程式碼必須檢查索引引數,確定它表示哪個欄位,並返回欄位值。
get訪問器有如下語義
- 它的引數列表和索引器宣告中的相同
- 它返回與索引器相同型別的值
關於索引器的補充
和屬性一樣,不能顯示呼叫get、set訪問器。取而代之,當索引器用在表示式中取值時,將自動呼叫get訪問器。索引器被賦值時,自動呼叫set訪問器。
在“呼叫”索引器時,要在方括號中提供引數。
索引 值 ↓ ↓ emp[0]="Doe"; //呼叫set訪問器 string NewName=emp[0]; //呼叫get訪問器
為Employee示例宣告索引器
下面程式碼為示例中的類Employee聲明瞭一個索引器
- 索引器需要去寫string型別的值,所以string必須宣告為索引器的型別。它必須宣告為public,以便從類外部訪問
- 3個欄位被強行索引為整數0-2,所以本例中方括號中間名為index的形參必須為int型
- 在set訪問器方法體內,程式碼確定索引指的是哪個欄位,並把隱式變數value賦給它。在get訪問器方法體內,程式碼確定索引指的哪個欄位,並返回該欄位的值
class Employee { public string LastName; public string FirstName; public string CityOfBirth; public string this[int index] { set { switch(index) { case 0:LaseName=value; break; case 1:FirstName=value; break; case 2:CityOfBirth=value; break; default: throw new ArgumentOutOfRangeException("index"); } } get { switch(index) { case 0:return LaseName; case 1:return FirstName; case 2:return CityOfBirth; default:throw new ArgumentOutOfRangeException("index"); } } } }
另一個索引器示例
例:為類Class1的兩個int欄位設定索引
class Class1 { int Temp0; int Temp1; public int this[int index] { get { return(index==0?Temp0:Temp1;) } set { if(index==0){Temp0=value;} else{Temp1=value;} } } } class Example { static void Main() { var a=new Class1(); Console.WriteLine("Values -- T0:{0},T1:{1}",a[0],a[1]); a[0]=15; a[1]=20; Console.WriteLine("Values--T0:{0},T1:{1}",a[0],a[1]); } }
索引器過載
類可以有任意多個引數列表不同的索引器。(返回型別不同,不是過載)
例:下面示例有3個索引器
class Myclass { public string this[int index] { get{...} set{...} } public string this[int index1,int index2] { get{...} set{...} } public int this[float index1] { get{...} set{...} } }
訪問器的訪問修飾符
本章中,你已看到了兩種帶get、set訪問器的函式成員:屬性和索引器。預設情況下,成員的兩個訪問器的訪問級別和成員自身相同。也就是說,如果一個屬性有public訪問級別,那麼它的兩個訪問器也是public的。
不過,你可以為兩個訪問器分配不同訪問級別。例如,下面程式碼演示了一個常見且重要的例子–set訪問器宣告為private,get訪問器宣告為public。(get之所以是public,是因為屬性的訪問級別就是public)
注意:在這段程式碼中,儘管可以從類的外部讀取該屬性,但卻只能在類的內部設定它。這是非常重要的封裝工具。
class Person { public string Name{get;private set;} public Person(string name) { Name=name; } } class Program { static public void Main() { var p=new Person("Capt,Ernest Evans"); Console.WriteLine("Person's name is {0}",p.Name); } }
訪問器的訪問修飾符有幾個限制。最重要的限制如下。
- 僅當成員(屬性或索引器)既有get訪問器也有set訪問器時,其訪問器才能有訪問修飾符
- 雖然兩個訪問器都必須出現,但它們中只能有一個有訪問修飾符
- 訪問器的訪問修飾符必須比成員的訪問級別有更嚴格的限制性,即訪問器的訪問級別必須比成員的訪問級別低,詳見下圖
例如,如果一個屬性的訪問級別是public,在圖裡較低的4個級別中,它的訪問器可以使用任意一個。但如果屬性的訪問級別是protected,則其訪問器唯一能使用的訪問修飾符是private。
分部類和分部型別
類的宣告可以分割成幾個分部類的宣告
- 每個分部類的宣告都含有一些類成員的宣告
- 類的分部類宣告可以在同一檔案中也可以在不同檔案中
每個區域性宣告必須標為partial class,而不是class。分部類宣告看起來和普通類宣告相同。
型別修飾符partial不是關鍵字,所以在其他上下文中,可以把它用作識別符號。但直接用在關鍵字class、struct或interface前時,它表示分部型別。
例:分部類
Visual Studio為標準的Windows程式模板使用了這個特性。當你從標準模板建立ASP.NET專案、Windows Forms專案或Windows Persentation Foudation(WPF)專案時,模板為每個Web頁面、表單、WPF窗體建立兩個類檔案。
- 一個檔案的分部類包含由VS生成的程式碼,聲明瞭頁面上的元件。你不應該修改這個檔案中的分部類,因為如果修改頁面元件,VS會重新生成
- 另一個檔案包含的分部類可用於實現頁面或表單元件的外觀和行為
- 除了分部類,還有另外兩種分部型別
- 區域性結構(第10章)
- 區域性介面(第15章)
分部方法
分部方法是宣告在分部類中不同部分的方法。
分部方法的兩個部分如下
- 定義分部方法宣告
- 給出簽名和返回型別
- 宣告的實現部分只是一個分號
- 實現分部方法宣告
- 給出簽名和返回型別
- 是以正常形式的語句塊實現
關於分部方法需要了解的重要內容如下
- 定義宣告和實現宣告的簽名和返回型別必須匹配。簽名和返回型別有如下特徵
- 返回型別必須是void
- 簽名不能包括訪問修飾符,這使分部方法是隱式私有的
- 引數列表不能包含out引數
- 在定義宣告和實現宣告中都必須包含上下文關鍵字partial,直接放在關鍵字void前
- 可以有定義部分而沒有實現部分。這種情況下,編譯器把方法的宣告以及方法內部任何對方法的呼叫都移除。不能只有實現部分而沒有定義部分。
下面是一個名為PrintSum的分部方法的示例
- 因為分部方法是隱式私有的,PrintSum不能從類的外部呼叫。方法Add是呼叫PrintSum的公有方法
partial class MyClass { 必須是void ↓ partial void PrintSum(int x,int y);//定義分部方法 public void Add(int x,int y) { PrintSum(x,y); } } partial class MyClass { partial void PrintSum(int x,int y)//實現分部方法 { Console.WriteLine("Sum i {0}",x+y); } } class Program { static void Main() { var mc=new MyClass(); mc.Add(5,6); } }
from: http://www.cnblogs.com/moonache/p/6097402.html