類 (class) 是最基礎的 C# 類型。類是一個數據結構,將狀態(字段)和操做(方法和其餘函數成員)組合在一個單元中。類爲動態建立的類實例 (instance) 提供了定義,實例也稱爲對象 (object)。類支持繼承 (inheritance) 和多態性 (polymorphism),這是派生類 (derived class) 可用來擴展和專用化基類 (base class) 的機制。算法
使用類聲明能夠建立新的類。類聲明以一個聲明頭開始,其組成方式以下:先指定類的屬性和修飾符,而後是類的名稱,接着是基類(若有)以及該類實現的接口。聲明頭後面跟着類體,它由一組位於一對大括號 { 和 } 之間的成員聲明組成。數組
下面是一個名爲 Point 的簡單類的聲明:數據結構
public class Point
{
public int x, y;ide
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}函數
類的實例使用 new 運算符建立,該運算符爲新的實例分配內存,調用構造函數初始化該實例,並返回對該實例的引用。下面的語句建立兩個 Point 對象,並將對這兩個對象的引用存儲在兩個變量中:this
Point p1 = new Point(0, 0);
Point p2 = new Point(10, 20);lua
當再也不使用對象時,該對象佔用的內存將自動收回。在 C# 中,沒有必要也不可能顯式釋放分配給對象的內存。spa
類的成員或者是靜態成員 (static member),或者是實例成員 (instance member)。靜態成員屬於類,實例成員屬於對象(類的實例)。.net
下表提供了類所能包含的成員種類的概述。線程
成員 |
說明 |
常量 |
與類關聯的常數值 |
字段 |
類的變量 |
方法 |
類可執行的計算和操做 |
屬性 |
與讀寫類的命名屬性相關聯的操做 |
索引器 |
與以數組方式索引類的實例相關聯的操做 |
事件 |
可由類生成的通知 |
運算符 |
類所支持的轉換和表達式運算符 |
構造函數 |
初始化類的實例或類自己所需的操做 |
析構函數 |
在永久丟棄類的實例以前執行的操做 |
類型 |
類所聲明的嵌套類型 |
類的每一個成員都有關聯的可訪問性,它控制可以訪問該成員的程序文本區域。有五種可能的可訪問性形式。下表概述了這些可訪問性。
可訪問性 |
含義 |
public |
訪問不受限制 |
protected |
訪問僅限於此類和今後類派生的類 |
internal |
訪問僅限於此程序 |
protected internal |
訪問僅限於此程序和今後類派生的類 |
private |
訪問僅限於此類 |
類聲明可經過在類名稱後面加上一個冒號和基類的名稱來指定一個基類。省略基類的指定等同於從類型 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 類從 Point 類繼承了 x 字段和 y 字段,每一個 Point3D 實例都包含三個字段 x、y 和 z。
從某個類類型到它的任何基類類型存在隱式的轉換。所以,類類型的變量能夠引用該類的實例或任何派生類的實例。例如,對於前面給定的類聲明,Point 類型的變量既能夠引用 Point 也能夠引用 Point3D:
Point a = new Point(10, 20);
Point b = new Point3D(10, 20, 30);
字段是與類或類的實例關聯的變量。
使用 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 修飾符聲明只讀字段 (read-only field)。給 readonly 字段的賦值只能做爲字段聲明的組成部分出現,或在同一類中的實例構造函數或靜態構造函數中出現。
方法 (method) 是一種用於實現能夠由對象或類執行的計算或操做的成員。靜態方法 (static method) 經過類來訪問。實例方法 (instance method) 經過類的實例來訪問。
方法具備一個參數 (parameter) 列表(可能爲空),表示傳遞給該方法的值或變量引用;方法還具備一個返回類型 (return type),指定該方法計算和返回的值的類型。若是方法不返回值,則其返回類型爲 void。
方法的簽名 (signature) 在聲明該方法的類中必須惟一。方法的簽名由方法的名稱及其參數的數目、修飾符和類型組成。方法的簽名不包含返回類型。
參數用於向方法傳遞值或變量引用。方法的參數從方法被調用時指定的實參 (argument) 獲取它們的實際值。有四種類型的參數:值參數、引用參數、輸出參數和參數數組。
值參數 (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); // Outputs "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); // Outputs "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} y={1} z={2}", args);
方法體指定了在該方法被調用時將執行的語句。
方法體能夠聲明僅用在該方法調用中的變量。這樣的變量稱爲局部變量 (local variable)。局部變量聲明指定了類型名稱、變量名稱,還可指定初始值。下面的示例聲明一個初始值爲零的局部變量 i 和一個沒有初始值的變量 j。
using System;
class Squares
{
static void Main() {
int i = 0;
int j;
while (i < 10) {
j = i * i;
Console.WriteLine("{0} x {0} = {1}", i, j);
i = i + 1;
}
}
}
C# 要求在對局部變量明確賦值 (definitely assigned) 以後才能獲取其值。例如,若是前面的 i 的聲明未包括初始值,則編譯器將對隨後對 i 的使用報告錯誤,由於 i 在程序中的該位置尚未明確賦值。
方法可使用 return 語句將控制返回到它的調用方。在返回 void 的方法中,return 語句不能指定表達式。在返回非 void 的方法中,return 語句必須含有一個計算返回值的表達式。
使用 static 修飾符聲明的方法爲靜態方法 (static method)。靜態方法不對特定實例進行操做,而且只能訪問靜態成員。
不使用 static 修飾符聲明的方法爲實例方法 (instance method)。實例方法對特定實例進行操做,而且可以訪問靜態成員和實例成員。在調用實例方法的實例上,能夠經過 this 顯式地訪問該實例。而在靜態方法中引用 this 是錯誤的。
下面的 Entity 類具備靜態成員和實例成員。
class Entity
{
static int nextSerialNo;
int serialNo;
public Entity() {
serialNo = nextSerialNo++;
}
public int GetSerialNo() {
return serialNo;
}
public static int GetNextSerialNo() {
return nextSerialNo;
}
public static void SetNextSerialNo(int value) {
nextSerialNo = value;
}
}
每一個 Entity 實例都包含一個序號(而且假定這裏省略了一些其餘信息)。Entity 構造函數(相似於實例方法)使用下一個可用的序號初始化新的實例。因爲該構造函數是一個實例成員,它既能夠訪問 serialNo 實例字段,也能夠訪問 nextSerialNo 靜態字段。
GetNextSerialNo 和 SetNextSerialNo 靜態方法能夠訪問 nextSerialNo 靜態字段,可是若是訪問 serialNo 實例字段就會產生錯誤。
下面的示例演示 Entity 類的使用。
using System;
class Test
{
static void Main() {
Entity.SetNextSerialNo(1000);
Entity e1 = new Entity();
Entity e2 = new Entity();
Console.WriteLine(e1.GetSerialNo()); // Outputs "1000"
Console.WriteLine(e2.GetSerialNo()); // Outputs "1001"
Console.WriteLine(Entity.GetNextSerialNo()); // Outputs "1002"
}
}
注意:SetNextSerialNo 和 GetNextSerialNo 靜態方法是在類上調用的,而 GetSerialNo 實例方法是在該類的實例上調用的。
若一個實例方法的聲明中含有 virtual 修飾符,則稱該方法爲虛方法 (virtual method)。若其中沒有 virtual 修飾符,則稱該方法爲非虛方法 (non-virtual method)。
在調用一個虛方法時,該調用所涉及的那個實例的運行時類型 (runtime type) 肯定了要被調用的到底是該方法的哪個實現。在非虛方法調用中,實例的編譯時類型 (compile-time type) 是決定性因素。
虛方法能夠在派生類中重寫 (override)。當某個實例方法聲明包括 override 修飾符時,該方法將重寫所繼承的具備相同簽名的虛方法。虛方法聲明用於引入新方法,而重寫方法聲明則用於使現有的繼承虛方法專用化(經過提供該方法的新實現)。
抽象 (abstract) 方法是沒有實現的虛方法。抽象方法使用 abstract 修飾符進行聲明,而且只有在一樣被聲明爲 abstract 的類中才容許出現。抽象方法必須在每一個非抽象派生類中重寫。
下面的示例聲明一個抽象類 Expression,它表示一個表達式樹節點;它有三個派生類 Constant、VariableReference 和 Operation,它們分別實現了常量、變量引用和算術運算的表達式樹節點。
using System;
using System.Collections;
public abstract class Expression
{
public abstract double Evaluate(Hashtable vars);
}
public class Constant: Expression
{
double value;
public Constant(double value) {
this.value = value;
}
public override double Evaluate(Hashtable vars) {
return value;
}
}
public class VariableReference: Expression
{
string name;
public VariableReference(string name) {
this.name = name;
}
public override double Evaluate(Hashtable vars) {
object value = vars[name];
if (value == null) {
throw new Exception("Unknown variable: " + name);
}
return Convert.ToDouble(value);
}
}
public class Operation: Expression
{
Expression left;
char op;
Expression right;
public Operation(Expression left, char op, Expression right) {
this.left = left;
this.op = op;
this.right = right;
}
public override double Evaluate(Hashtable vars) {
double x = left.Evaluate(vars);
double y = right.Evaluate(vars);
switch (op) {
case '+': return x + y;
case '-': return x - y;
case '*': return x * y;
case '/': return x / y;
}
throw new Exception("Unknown operator");
}
}
上面的四個類可用於爲算術表達式建模。例如,使用這些類的實例,表達式 x + 3 可以下表示。
Expression e = new Operation(
new VariableReference("x"),
'+',
new Constant(3));
Expression 實例的 Evaluate 方法將被調用,以計算給定的表達式的值,從而產生一個 double 值。該方法接受一個包含變量名稱(做爲哈希表項的鍵)和值(做爲項的值)的 Hashtable 做爲參數。Evaluate 方法是一個虛抽象方法,意味着非抽象派生類必須重寫該方法以提供具體的實現。
Constant 的 Evaluate 實現只是返回所存儲的常量。VariableReference 的實如今哈希表中查找變量名稱,並返回產生的值。Operation 的實現先對左操做數和右操做數求值(經過遞歸調用它們的 Evaluate 方法),而後執行給定的算術運算。
下面的程序使用 Expression 類,對於不一樣的 x 和 y 值,計算表達式 x * (y + 2) 的值。
using System;
using System.Collections;
class Test
{
static void Main() {
Expression e = new Operation(
new VariableReference("x"),
'*',
new Operation(
new VariableReference("y"),
'+',
new Constant(2)
)
);
Hashtable vars = new Hashtable();
vars["x"] = 3;
vars["y"] = 5;
Console.WriteLine(e.Evaluate(vars)); // Outputs "21"
vars["x"] = 1.5;
vars["y"] = 9;
Console.WriteLine(e.Evaluate(vars)); // Outputs "16.5"
}
}
方法重載 (overloading) 容許同一類中的多個方法具備相同名稱,條件是這些方法具備惟一的簽名。在編譯一個重載方法的調用時,編譯器使用重載決策 (overload resolution) 肯定要調用的特定方法。重載決策將查找與參數最佳匹配的方法,若是沒有找到任何最佳匹配的方法則報告錯誤信息。下面的示例演示重載決策的工做機制。Main 方法中的每一個調用的註釋代表實際被調用的方法。
class Test
{
static void F() {...}
Console.WriteLine("F()");
}
static void F(object x) {
Console.WriteLine("F(object)");
}
static void F(int x) {
Console.WriteLine("F(int)");
}
static void F(double x) {
Console.WriteLine("F(double)");
}
static void F(double x, double y) {
Console.WriteLine("F(double, double)");
}
static void Main() {
F(); // Invokes F()
F(1); // Invokes F(int)
F(1.0); // Invokes F(double)
F("abc"); // Invokes F(object)
F((double)1); // Invokes F(double)
F((object)1); // Invokes F(object)
F(1, 1); // Invokes F(double, double)
}
}
正如該示例所示,老是經過顯式地將實參強制轉換爲確切的形參類型,來選擇一個特定的方法。
包含可執行代碼的成員統稱爲類的函數成員 (function member)。前一節描述的方法是函數成員的主要類型。本節描述 C# 支持的其餘種類的函數成員:構造函數、屬性、索引器、事件、運算符和析構函數。
下表演示一個名爲 List 的類,它實現一個可增加的對象列表。該類包含了幾種最多見的函數成員的示例。
public class List |
|
const int defaultCapacity = 4; |
常量 |
object[] items; |
字段 |
public List(): List(defaultCapacity) {} public List(int capacity) { |
構造函數 |
public int Count { public string Capacity { |
屬性 |
public object this[int index] { |
索引器 |
public void Add(object item) { protected virtual void OnChanged() { public override bool Equals(object other) { static bool Equals(List a, List b) { |
方法 |
public event EventHandler Changed; |
事件 |
public static bool operator ==(List a, List b) { public static bool operator !=(List a, List b) { |
運算符 |
} |
C# 支持兩種構造函數:實例構造函數和靜態構造函數。實例構造函數 (instance constructor) 是實現初始化類實例所需操做的成員。靜態構造函數 (static constructor) 是一種用於在第一次加載類自己時實現其初始化所需操做的成員。
構造函數的聲明如同方法同樣,不過它沒有返回類型,而且它的名稱與其所屬的類的名稱相同。若是構造函數聲明包含 static 修飾符,則它聲明瞭一個靜態構造函數。不然,它聲明的是一個實例構造函數。
實例構造函數能夠被重載。例如,List 類聲明瞭兩個實例構造函數,一個無參數,另外一個接受一個 int 參數。實例構造函數使用 new 運算符進行調用。下面的語句分別使用 List 類的每一個構造函數分配兩個 List 實例。
List list1 = new List();
List list2 = new List(10);
實例構造函數不一樣於其餘成員,它是不能被繼承的。一個類除了其中實際聲明的實例構造函數外,沒有其餘的實例構造函數。若是沒有爲某個類提供任何實例構造函數,則將自動提供一個不帶參數的空的實例構造函數。
屬性 (propery) 是字段的天然擴展。屬性和字段都是命名的成員,都具備相關的類型,且用於訪問字段和屬性的語法也相同。然而,與字段不一樣,屬性不表示存儲位置。相反,屬性有訪問器 (accessor),這些訪問器指定在它們的值被讀取或寫入時需執行的語句。
屬性的聲明與字段相似,不一樣的是屬性聲明以位於定界符 { 和 } 之間的一個 get 訪問器和/或一個 set 訪問器結束,而不是以分號結束。同時具備 get 訪問器和 set 訪問器的屬性是讀寫屬性 (read-write property),只有 get 訪問器的屬性是隻讀屬性 (read-only property),只有 set 訪問器的屬性是隻寫屬性 (write-only property)。
get 訪問器至關於一個具備屬性類型返回值的無參數方法。除了做爲賦值的目標,當在表達式中引用屬性時,將調用該屬性的 get 訪問器以計算該屬性的值。
set 訪問器至關於具備一個名爲 value 的參數而且沒有返回類型的方法。當某個屬性做爲賦值的目標被引用,或者做爲 ++ 或 -- 的操做數被引用時,將調用 set 訪問器,並傳入提供新值的實參。
List 類聲明瞭兩個屬性 Count 和 Capacity,它們分別是隻讀屬性和讀寫屬性。下面是這些屬性的使用示例。
List names = new List();
names.Capacity = 100; // Invokes set accessor
int i = names.Count; // Invokes get accessor
int j = names.Capacity; // Invokes get accessor
與字段和方法類似,C# 同時支持實例屬性和靜態屬性。靜態屬性使用 static 修飾符聲明,而實例屬性的聲明不帶該修飾符。
屬性的訪問器能夠是虛的。當屬性聲明包括 virtual、abstract 或 override 修飾符時,修飾符應用於該屬性的訪問器。
索引器 (indexer) 是這樣一個成員:它使對象可以用與數組相同的方式進行索引。索引器的聲明與屬性相似,不一樣的是該成員的名稱是 this,後跟一個位於定界符 [ 和 ] 之間的參數列表。在索引器的訪問器中可使用這些參數。與屬性相似,索引器能夠是讀寫、只讀和只寫的,而且索引器的訪問器能夠是虛的。
該 List 類聲明瞭單個讀寫索引器,該索引器接受一個 int 參數。該索引器使得經過 int 值對 List 實例進行索引成爲可能。例如
List numbers = new List();
names.Add("Liz");
names.Add("Martha");
names.Add("Beth");
for (int i = 0; i < names.Count; i++) {
string s = (string)names[i];
names[i] = s.ToUpper();
}
索引器能夠被重載,這意味着一個類能夠聲明多個索引器,只要它們的參數的數量和類型不一樣便可。
事件 (event) 是一種使類或對象可以提供通知的成員。事件的聲明與字段相似,不一樣的是事件的聲明包含 event 關鍵字,而且類型必須是委託類型。
在聲明事件成員的類中,事件的行爲就像委託類型的字段(前提是該事件不是抽象的而且未聲明訪問器)。該字段存儲對一個委託的引用,該委託表示已添加到該事件的事件處理程序。若是還沒有添加事件處理程序,則該字段爲 null。
List 類聲明瞭一個名爲 Changed 的事件成員,它指示有一個新的項已被添加到列表中。Changed 事件由 OnChanged 虛方法引起,後者先檢查該事件是否爲 null(代表沒有處理程序)。「引起一個事件」與「調用一個由該事件表示的委託」徹底等效,所以沒有用於引起事件的特殊語言構造。
客戶端經過事件處理程序 (event handler) 來響應事件。事件處理程序使用 += 運算符添加,使用 -= 運算符移除。下面的示例向 List 類的 Changed 事件添加一個事件處理程序。
using System;
class Test
{
static int changeCount;
static void ListChanged(object sender, EventArgs e) {
changeCount++;
}
static void Main() {
List names = new List();
names.Changed += new EventHandler(ListChanged);
names.Add("Liz");
names.Add("Martha");
names.Add("Beth");
Console.WriteLine(changeCount); // Outputs "3"
}
}
對於要求控制事件的底層存儲的高級情形,事件聲明能夠顯式提供 add 和 remove 訪問器,它們在某種程度上相似於屬性的 set 訪問器。
運算符 (operator) 是一種類成員,它定義了可應用於類實例的特定表達式運算符的含義。能夠定義三種運算符:一元運算符、二元運算符和轉換運算符。全部運算符都必須聲明爲 public 和 static。
List 類聲明瞭兩個運算符 operator == 和 operator !=,從而爲將那些運算符應用於 List 實例的表達式賦予了新的含義。具體而言,上述運算符將兩個 List 實例的相等關係定義爲逐一比較其中所包含的對象(使用所包含對象的 Equals 方法)。下面的示例使用 == 運算符比較兩個 List 實例。
using System;
class Test
{
static void Main() {
List a = new List();
a.Add(1);
a.Add(2);
List b = new List();
b.Add(1);
b.Add(2);
Console.WriteLine(a == b); // Outputs "True"
b.Add(3);
Console.WriteLine(a == b); // Outputs "False"
}
}
第一個 Console.WriteLine 輸出 True,緣由是兩個列表包含的對象數目和值均相同。若是 List 未定義 operator ==,則第一個 Console.WriteLine 將輸出 False,緣由是 a 和 b 引用的是不一樣的 List 實例。
析構函數 (destructor) 是一種用於實現銷燬類實例所需操做的成員。析構函數不能帶參數,不能具備可訪問性修飾符,也不能被顯式調用。垃圾回收期間會自動調用所涉及實例的析構函數。
垃圾回收器在決定什麼時候回收對象和運行析構函數方面容許有普遍的自由度。具體而言,析構函數調用的時機並非肯定的,析構函數可能在任何線程上執行。因爲這些以及其餘緣由,僅當沒有其餘可行的解決方案時,才應在類中實現析構函數。
轉自CSDN:http://blog.csdn.net/llll29550242/article/details/6051812