類(class)是C#類型中最基礎的類型。類是一個數據結構,將狀態(字段)和行為(方法和其他函數成員)組合在一個單元中。類提供了用於動態創建類實例的定義,也就是對象(object)。類支持繼承(inheritance)和多態(polymorphism),即派生類能夠擴展和特殊化基類的機制。
使用類聲明可以創建新的類。類聲明以一個聲明頭開始,其組成方式如下:先是指定類的特性和修飾符,後跟類的名字,基類(如果有的話)的名字,以及被該類實現的接口名。聲明頭後面就是類體了,它由一組包含在大括號({})中的成員聲明組成。
下面是一個名為Point的簡單類的聲明:
public class Point
{
public int x, y;
public Point(int x, int y){
this.x = x;
this.y = y;
}
}
使用new運算符創建類的實例,它將為新實例分配內存,調用構造函數初始化實例,並且返回對該實例的引用。下面的語句創建兩個Point對象,並且將那些對象的引用保存到兩個變量中:
Point p1 = new Point(0, 0);
Point p2 = new Point(10, 20);
當不再使用對象時,該對象所占的內存將被自動回收。在C#中,沒有必要也不可能顯式地釋放對象。
1.6.1 成員
類的成員或者是靜態成員(static member),或者是實例成員(instance member)。靜態成員屬於類,實例成員屬於對象(類的實例)。
表1.6提供了類所能包含的各種成員的描述。
表1.6 類 的 成 員
成 員
描 述
常數
與類關聯的常量值
字段
類的變量
方法
能夠被類執行的計算和行為
屬性
使對象能夠讀取和寫入類的命名屬性
索引器
使對象能夠用與數組相同的方式進行索引
事件
能夠被類產生的通知
運算符
類支持的轉換和表達式運算符
構造函數
初始化類的實例或者類本身
析構函數
在永久銷毀類的實例之前執行的行為
類型
被類聲明的嵌套類型
1.6.2 可訪問性
類的每個成員都有關聯的可訪問性,它控制能夠訪問該成員的程序文本區域。有5種可能的可訪問性形式。表1.7概述了類的可訪問性的意義。
表1.7 類的可訪問性
可訪問性
意 義
public
訪問不受限制
protected
訪問僅限於包含類或從包含類派生的類型
internal
訪問僅限於當前程序集
protected internal
訪問僅限於從包含類派生的當前程序集或類型
private
訪問僅限於包含類
1.6.3 基類
類的聲明可能通過在類名後加上冒號和基類的名字來指定一個基類譯注4。省略基類等同於直接從object類派生。在下面的示例中,Point3D的基類是Point,而Point的基類是object:
public class Point
{
public int x, y;
public Point(int x, int y){
this.x = x;
this.y = y;
}
}
public class Point3D: Point
{
public int z;
public Point3D(int x, int y, int z): Point(x, y){
this.z = z;
}
}
Point3D類繼承了其基類的成員。繼承意味著類將隱式地包含其基類的所有成員(除了基類的構造函數)。派生類能夠在繼承基類的基礎上增加新的成員,但是它不能移除繼承成員的定義。在前面的示例中,Point3D類從Point類中繼承了x字段和y字段,並且每一個Point3D實例都包含三個字段x,y和z。
從類類型到它的任何基類類型都存在隱式的轉換。並且,類類型的變量能夠引用該類的實例,或者任何派生類的實例。例如,對於前面給定的類聲明,Point類型的變量能夠引用Point實例或者Point3D實例:
Point a = new Point(10, 20);
Point b = new Point3D(10, 20, 30);
1.6.4 字段
字段是與對象或類相關聯的變量。
當一個字段聲明中含有static修飾符時,由該聲明引入的字段為靜態字段(static field)。它只標識了一個存儲位置。不管創建了多少個類實例,靜態字段都只會有一個副本。
當一個字段聲明中不含有static修飾符時,由該聲明引入的字段為實例字段(instance field)。類的每個實例都包含了該類的所有實例字段的一個單獨副本。
在下面的示例中,Color類的每個實例都有r,g,b實例字段的不同副本,但是Black,White,Red,Green和Blue等靜態字段只有一個副本:
public class Color
{
public static readonly Color Black = new Color(0, 0, 0);
public static readonly Color White = new Color(255, 255, 255);
public static readonly Color Red = new Color(255, 0, 0);
public static readonly Color Green = new Color(0, 255, 0);
public static readonly Color Blue = new Color(0, 0, 255);
private byte r, g, b;
public Color(byte r, byte g, byte b) {
this.r = r;
this.g = g;
this.b = b;
}
}
如前面的示例所示,通過readonly修飾符聲明只讀字段。給readonly字段的賦值只能作為聲明的組成部分出現,或者在同一類中的實例構造函數或靜態構造函數中出現。
1.6.5 方法
方法(method)是一種用於實現可以由對象或類執行的計算或操作的成員。靜態方法(static method)只能通過類來訪問。實例方法(instance method)則要通過類的實例訪問。
方法有一個參數(parameter)列表(可能為空),表示傳遞給方法的值或者引用;方法還有返回類型(return type),用於指定由該方法計算和返回的值的類型。如果方法不返回一個值,則它的返回類型為void。
在聲明方法的類中,該方法的簽名必須是惟一的。方法的簽名由它的名稱、參數的數目、每個參數的修飾符和類型組成。返回類型不是方法簽名的組成部分。
1.6.5.1 參數
參數用於將值或者引用變量傳遞給方法。當方法被調用時,方法的參數譯注5從指定的自變量(argument)譯注6得到它們實際的值。C#有4種參數:值參數、引用參數、輸出參數和參數數組。
值參數(value parameter)用於輸入參數的傳遞。值參數相當於一個局部變量,它的初始值是從為該參數所傳遞的自變量獲得的。對值參數的修改不會影響所傳遞的自變量。
引用參數(reference parameter)用於輸入和輸出參數的傳遞。用於引用參數的自變量必須是一個變量,並且在方法執行期間,引用參數和作為自變量的變量所表示的是同一個存儲位置。引用參數用ref修飾符聲明。下面的示例展示了ref參數的使用:
using System;
class Test
{
static void Swap(ref int x, ref int y) {
int temp = x;
x = y;
y = temp;
}
static void Main() {
int i = 1, j = 2;
Swap(ref i, ref j);
Console.WriteLine("{0} {1}", i, j); //輸出 "2 1"
}
}
輸出參數(output parameter)用於輸出參數的傳遞。輸出參數類似於引用參數,不同之處在於調用方提供的自變量初始值無關緊要。輸出參數用out修飾符聲明。下面的示例展示了out參數的使用:
using System;
class Test {
static void Divide(int x, int y, out int result, out int remainder) {
result = x / y;
remainder = x % y;
}
static void Main() {
int res, rem;
Divide(10, 3, out res, out rem);
Console.WriteLine("{0} {1}", res, rem); //輸出 "3 1"
}
}
參數數組(parameter array)允許將可變長度的自變量列表傳遞給方法。參數數組用params修飾符聲明。只有方法的最後一個參數能夠被聲明為參數數組,而且它必須是一維數組類型。System.Console類的Write和WriteLine方法是參數數組應用的很好的例子。它們的聲明形式如下:
public class Console
{
public static void Write(string fmt, params object[] args) {...}
public static void WriteLine(string fmt, params object[] args) {...}
...
}
在方法中使用參數數組時,參數數組表現得就像常規的數組類型參數一樣。然而,帶數組參數的方法調用中,既可以傳遞參數數組類型的單個自變量,也可以傳遞參數數組的元素類型的若干自變量。對於後者的情形,數組實例將自動被創建,並且通過給定的自變量初始化。示例:
Console.WriteLine("x={0} y={1} z={2}", x, y, z);
等價於下面的語句:
object[] args = new object[3];
args[0] = x;
args[1] = y;
args[2] = z;
Console.WriteLine("x={0}