C# 程式建置組塊
此 C# 導覽系列的上一篇文章中所描述的類型是利用下列建置組塊所建立:
成員
class
的成員為靜態成員或執行個體成員。 靜態成員隸屬於類別,而執行個體成員則隸屬於物件 (類別的執行個體)。
下列清單可讓您概覽類別所能包含的成員種類。
- 常數:與類別相關聯的常數值
- 欄位:與類別相關聯的變數
- 方法:類別可執行的動作
- 屬性:與讀取和寫入類別之具名屬性相關聯的動作
- 索引子:與針對類別的執行個體編製索引 (如同陣列一般) 相關聯的動作
- 事件:類別可產生的通知
- 運算子:類別所支援的轉換和運算式運算子
- 建構函式:將類別的執行個體或類別本身初始化所需的動作
- 完成項:在永久捨棄類別執行個體之前所執行的動作
- 類型:類別所宣告的巢狀型別
協助工具選項
類別中的每個成員都有相關聯的存取性,此存取性會控制可存取成員的程式文字區域。 存取能力有六種可能的形式。 存取修飾詞的摘要如下。
public
:存取不受限制。private
:存取僅限於此類別。protected
:存取僅限於此類別或衍生自此類別的類別。internal
:存取僅限於目前的組件 (.exe
或.dll
)。protected internal
:存取僅限於此類別、衍生自此類別的類別,或相同組件內的類別。private protected
:存取僅限於此類別或相同組件內自此類型衍生的類別。
欄位
「欄位」是與類別或類別執行個體關聯的變數。
使用 static 修飾詞來宣告的欄位會定義靜態欄位。 靜態欄位只會識別一個儲存位置。 不論一個類別內建立了多少執行個體,都只會一份靜態欄位。
未使用 static 修飾詞來宣告的欄位會定義執行個體欄位。 每個類別執行個體都包含一個該類別所有執行個體欄位的個別複本。
在下列範例中,Color
類別的每個執行個體都分別有一份 R
、G
和 B
執行個體欄位,但Black
、White
、Red
、Green
和 Blue
靜態欄位只會有一份:
public class Color
{
public static readonly Color Black = new(0, 0, 0);
public static readonly Color White = new(255, 255, 255);
public static readonly Color Red = new(255, 0, 0);
public static readonly Color Green = new(0, 255, 0);
public static readonly Color Blue = new(0, 0, 255);
public byte R;
public byte G;
public byte B;
public Color(byte r, byte g, byte b)
{
R = r;
G = g;
B = b;
}
}
如先前的範例所示,可以使用 readonly
修飾詞來宣告「唯讀欄位」。 只有在欄位宣告或相同類別內的建構函式中,才能對唯讀欄位進行指派。
方法
「方法」是實作物件或類別所能執行之計算或動作的成員。 存取「靜態方法」時,是透過類別來存取。 存取「執行個體方法」時,是透過類別的執行個體來存取。
方法可能會有一份「參數」清單,這份清單代表傳遞給方法的值或變數參考。 方法也會有「傳回型別」,此型別會指定方法計算和傳回的值類型。 若方法不會傳回值,則其傳回型別會是 void
。
與型別相同,方法也可能有一組型別參數,而呼叫方法時,必須為這些參數指定型別引數。 與型別不同的是,型別引數通常可以從方法呼叫的引數推斷,而不需要明確指定。
在宣告方法的類別中,方法的「簽章」必須是唯一的。 方法的特徵標記包含方法名稱、型別參數的數目,以及方法參數的數目、修飾元和類型。 方法的特徵標記不包含傳回型別。
當方法主體為單一運算式時,可使用精簡運算式格式定義方法,如下方範例所示:
public override string ToString() => "This is an object";
參數
參數是用來將值或變數參考傳遞給方法。 方法的參數會從叫用方法時所指定的「引數」取得其實際值。 參數有四種:值參數、參考參數、輸出參數,以及參數陣列。
「值參數」適用於傳遞輸入引數。 值參數會對應至區域變數,此變數會從針對參數傳遞的引數取得其初始值。 修改值參數並不會影響針對該參數傳遞的引數。
只要指定預設值,值參數便可以成為選用參數,如此即可省略對應的引數。
「參考參數」適用於以參考方式傳遞引數。 針對傳址參數所傳遞的引數必須是具有明確值的變數。 在方法執行期間,傳址參數代表的是與引數變數相同的儲存位置。 宣告參考參數時,是使用 ref
修飾詞來宣告。 下列範例示範 ref
參數的用法。
static void Swap(ref int x, ref int y)
{
int temp = x;
x = y;
y = temp;
}
public static void SwapExample()
{
int i = 1, j = 2;
Swap(ref i, ref j);
Console.WriteLine($"{i} {j}"); // "2 1"
}
「輸出參數」適用於以參考方式傳遞引數。 其類似於參考參數,只不過它並不需要您明確指派值給呼叫端提供的引數。 宣告輸出參數時,是使用 out
修飾詞來宣告。 下列範例示範 out
參數的用法。
static void Divide(int x, int y, out int quotient, out int remainder)
{
quotient = x / y;
remainder = x % y;
}
public static void OutUsage()
{
Divide(10, 3, out int quo, out int rem);
Console.WriteLine($"{quo} {rem}"); // "3 1"
}
「參數陣列」可允許將數目不固定的引數傳遞給方法。 宣告參數陣列時,是使用 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) { }
// ...
}
在使用參數陣列的方法內,參數陣列的行為與陣列型別的一般參數完全相同。 不過,在叫用含有參數陣列的方法時,可以傳遞單一的參數陣列類型引數,或是傳遞任意數目的參數陣列元素類型引數。 在後者的案例中,會自動建立陣列執行個體並以指定的引數將其初始化。 以下範例
int x, y, z;
x = 3;
y = 4;
z = 5;
Console.WriteLine("x={0} y={1} z={2}", x, y, z);
等同於撰寫下列程式碼。
int x = 3, y = 4, z = 5;
string s = "x={0} y={1} z={2}";
object[] args = new object[3];
args[0] = x;
args[1] = y;
args[2] = z;
Console.WriteLine(s, args);
方法主體和區域變數
方法主體會指定叫用方法時所要執行的陳述式。
方法主體可以宣告方法叫用專屬的變數。 這類變數稱為「區域變數」。 區域變數宣告會指定型別名稱、變數名稱,還可能指定初始值。 下列範例會宣告一個初始值為零的區域變數 i
,以及一個沒有初始值的區域變數 j
。
class Squares
{
public static void WriteSquares()
{
int i = 0;
int j;
while (i < 10)
{
j = i * i;
Console.WriteLine($"{i} x {i} = {j}");
i++;
}
}
}
C# 要求必須「明確指派」區域變數,才能取得其值。 例如,若先前 i
的宣告並未包含初始值,編譯器就會針對之後使用 i
的情況回報錯誤,因為 i
在這些時間點並不會在程式中明確受到指派。
方法可以使用 return
陳述式將控制權交還給其呼叫端。 在傳回 void
的方法中,return
陳述式無法指定運算式。 在傳回非 void 的方法中,return
陳述式必須包含會計算傳回值的運算式。
靜態和執行個體方法
使用 static
修飾元宣告的方法即為「靜態方法」。 靜態方法不會在特定的執行個體上運作,並且只能直接存取靜態成員。
不是使用 static
修飾元宣告的方法為「執行個體方法」。 執行個體方法會在特定的執行個體上運作,並且既可存取靜態成員也可存取執行個體成員。 透過 this
可以明確存取叫用執行個體方法時所在的執行個體。 若在靜態方法中參考 this
,則會產生錯誤。
下列 Entity
類別同時含有靜態成員和執行個體成員。
class Entity
{
static int s_nextSerialNo;
int _serialNo;
public Entity()
{
_serialNo = s_nextSerialNo++;
}
public int GetSerialNo()
{
return _serialNo;
}
public static int GetNextSerialNo()
{
return s_nextSerialNo;
}
public static void SetNextSerialNo(int value)
{
s_nextSerialNo = value;
}
}
每個 Entity
執行個體都會包含一個序號 (且可能會有這裡未顯示的一些其他資訊)。 Entity
建構函式 (類似於執行個體方法) 會將具有下一個可用序號的新執行個體初始化。 由於此建構函式為執行個體成員,因此可以存取 _serialNo
執行個體欄位和 s_nextSerialNo
靜態欄位。
GetNextSerialNo
和 SetNextSerialNo
靜態方法可以存取 s_nextSerialNo
靜態欄位,但如果直接存取 _serialNo
執行個體欄位,則會產生錯誤。
下列範例會示範 Entity
類別的用法。
Entity.SetNextSerialNo(1000);
Entity e1 = new();
Entity e2 = new();
Console.WriteLine(e1.GetSerialNo()); // Outputs "1000"
Console.WriteLine(e2.GetSerialNo()); // Outputs "1001"
Console.WriteLine(Entity.GetNextSerialNo()); // Outputs "1002"
請注意,SetNextSerialNo
和 GetNextSerialNo
靜態方法的叫用位置為類別,而 GetSerialNo
執行個體方法的叫用位置則為類別的執行個體。
虛擬、覆寫及抽象方法
您可以使用虛擬、覆寫和抽象方法來針對類別類型的階層定義行為。 由於類別可衍生自基底類別,因此這類衍生類別可能需要修改在基底類別中實作的行為。 虛擬方法是在基底類別中宣告及實作的一種方法,而在該基底類別中的任一衍生類別都可能提供更明確的實作。 覆寫方法是在衍生類別中實作的一種方法,會修改基底類別實作的行為。 抽象方法是在基底類別中宣告的一種方法,在所有衍生類別中都「必須」被覆寫。 事實上,抽象方法不會定義基底類別中的實作。
執行個體方法的方法呼叫可能會解析為基底類別或衍生類別實作。 變數的類型會決定其「編譯時間類型」。 「編譯時間類型」是編譯器用來判斷成員的類型。 不過,變數可能會指派給任一類型的執行個體,而該類型則衍生自其「編譯時間類型」。 「執行階段類型」是變數所參考的實際執行個體類型。
叫用虛擬方法時,叫用所針對之執行個體的「執行階段型別」會決定要叫用的實際方法實作。 在非虛擬方法叫用中,決定因素則是執行個體的「編譯階段型別」。
在衍生類別中可以「覆寫」虛擬方法。 當執行個體方法宣告包含 override 修飾詞時,該方法會覆寫具有相同簽章的已繼承虛擬方法。 虛擬方法宣告會導入新的方法。 覆寫方法宣告則會提供現有繼承之虛擬方法的新實作,藉此特製化該方法。
「抽象方法」係指不含實作的虛擬方法。 抽象方法是透過 abstract
修飾元進行宣告,且只允許在抽象類別中使用。 抽象方法必須在每個非抽象的衍生類別中被覆寫。
下列範例會宣告一個抽象類別 Expression
和三個衍生的類別 Constant
、VariableReference
及 Operation
,前者代表一個運算式樹狀架構節點,後者則會實作常數、變數參考及算數運算式的運算式樹狀架構節點。 (此範例雖與運算式樹狀架構類型相似,但與其並無關聯。)
public abstract class Expression
{
public abstract double Evaluate(Dictionary<string, object> vars);
}
public class Constant : Expression
{
double _value;
public Constant(double value)
{
_value = value;
}
public override double Evaluate(Dictionary<string, object> vars)
{
return _value;
}
}
public class VariableReference : Expression
{
string _name;
public VariableReference(string name)
{
_name = name;
}
public override double Evaluate(Dictionary<string, object> vars)
{
object value = vars[_name] ?? 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)
{
_left = left;
_op = op;
_right = right;
}
public override double Evaluate(Dictionary<string, object> 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;
default: throw new Exception("Unknown operator");
}
}
}
先前的四個類別可用來建構算數運算式的模型。 例如,在使用這些類別執行個體的情況下,可以將運算式 x + 3
表示如下。
Expression e = new Operation(
new VariableReference("x"),
'+',
new Constant(3));
系統會叫用 Expression
執行個體的 Evaluate
方法來評估指定的運算式並產生 double
值。 此方法會採用包含變數名稱 (作為項目的索引鍵) 和值 (作為項目的值) 的 Dictionary
引數。 因為 Evaluate
是一種抽象方法,所以衍生自 Expression
的非抽象類別必須覆寫 Evaluate
。
Constant
的 Evaluate
實作會直接傳回預存的常數。 VariableReference
的實作會查詢字典中的變數名稱並傳回產生的值。 Operation
的實作會先評估左邊和右邊的運算元 (透過以遞迴方式叫用其 Evaluate
方法),然後才執行指定的算數運算。
下列程式會使用 Expression
類別來評估不同 x
和 y
值的 x * (y + 2)
運算式。
Expression e = new Operation(
new VariableReference("x"),
'*',
new Operation(
new VariableReference("y"),
'+',
new Constant(2)
)
);
Dictionary<string, object> vars = new();
vars["x"] = 3;
vars["y"] = 5;
Console.WriteLine(e.Evaluate(vars)); // "21"
vars["x"] = 1.5;
vars["y"] = 9;
Console.WriteLine(e.Evaluate(vars)); // "16.5"
方法多載
方法「多載」可允許相同類別中的多個方法擁有相同的名稱,只要它們的簽章是唯一的即可。 編譯多載方法的叫用時,編譯器會使用「多載解析」來判斷要叫用的特定方法。 多載解析會尋找與引數最相符的一個方法。 若一個最相符的方法都找不到,則會回報錯誤。 下列範例示範多載解析的實際運作情況。 UsageExample
方法中每個叫用的註解都會說明叫用哪一個方法。
class OverloadingExample
{
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<T>(T x) => Console.WriteLine($"F<T>(T), T is {typeof(T)}");
static void F(double x, double y) => Console.WriteLine("F(double, double)");
public static void UsageExample()
{
F(); // Invokes F()
F(1); // Invokes F(int)
F(1.0); // Invokes F(double)
F("abc"); // Invokes F<T>(T), T is System.String
F((double)1); // Invokes F(double)
F((object)1); // Invokes F(object)
F<int>(1); // Invokes F<T>(T), T is System.Int32
F(1, 1); // Invokes F(double, double)
}
}
如範例所示,透過將引數明確轉換成確切的參數類型和型別引數,即可一律選取特定的方法。
其他函式成員
包含可執行程式碼的成員統稱為類別的「函式成員」。 上節所述的方法是函式成員的主要類型。 本節將說明 C# 所支援的其他函式成員類型:建構函式、屬性、索引子、事件、運算子及完成項。
下列範例示範的泛型型別稱為 MyList<T>
,會實作一份可成長的物件清單。 此類別包含數個最常見的函式成員類型。
public class MyList<T>
{
const int DefaultCapacity = 4;
T[] _items;
int _count;
public MyList(int capacity = DefaultCapacity)
{
_items = new T[capacity];
}
public int Count => _count;
public int Capacity
{
get => _items.Length;
set
{
if (value < _count) value = _count;
if (value != _items.Length)
{
T[] newItems = new T[value];
Array.Copy(_items, 0, newItems, 0, _count);
_items = newItems;
}
}
}
public T this[int index]
{
get => _items[index];
set
{
if (!object.Equals(_items[index], value)) {
_items[index] = value;
OnChanged();
}
}
}
public void Add(T item)
{
if (_count == Capacity) Capacity = _count * 2;
_items[_count] = item;
_count++;
OnChanged();
}
protected virtual void OnChanged() =>
Changed?.Invoke(this, EventArgs.Empty);
public override bool Equals(object other) =>
Equals(this, other as MyList<T>);
static bool Equals(MyList<T> a, MyList<T> b)
{
if (Object.ReferenceEquals(a, null)) return Object.ReferenceEquals(b, null);
if (Object.ReferenceEquals(b, null) || a._count != b._count)
return false;
for (int i = 0; i < a._count; i++)
{
if (!object.Equals(a._items[i], b._items[i]))
{
return false;
}
}
return true;
}
public event EventHandler Changed;
public static bool operator ==(MyList<T> a, MyList<T> b) =>
Equals(a, b);
public static bool operator !=(MyList<T> a, MyList<T> b) =>
!Equals(a, b);
}
建構函式
C# 同時支援執行個體建構函式和靜態建構函式。 「執行個體建構函式」是實作將類別執行個體初始化所需之動作的成員。 「靜態建構函式」是實作動作的成員,這類動作是第一次載入類別時,初始化類別本身所需的動作。
建構函式的宣告方式與方法類似,但不含傳回型別且名稱會與包含它的類別相同。 若建構函式宣告包含 static
修飾元,則所宣告的即為靜態建構函式。 否則,所宣告的會是執行個體建構函式。
執行個體建構函式可以多載,且可以具有選擇性參數。 例如,MyList<T>
類別會使用單一選擇性 int
參數來宣告一個執行個體建構函式。 叫用執行個體建構函式時,是使用 new
運算子來叫用。 下列陳述式會使用 MyList
類別的建構函式搭配或不搭配選用的引數來配置兩個 MyList<string>
執行個體。
MyList<string> list1 = new();
MyList<string> list2 = new(10);
與其他成員不同,執行個體建構函式不是繼承而來的。 除了在類別中實際宣告的建構函式以外,類別不會有任何執行個體建構函式。 如果沒有為類別提供任何執行個體建構函式,則會自動提供一個沒有任何參數的空建構函式。
屬性
「屬性」是欄位的自然延伸。 兩者都是具有關聯型別的具名成員,並且用來存取欄位和屬性的語法是相同的。 不過,與欄位不同的是,屬性不會指示儲存位置, 而是會有「存取子」,這些存取子會指定讀取或寫入屬性值時要執行的陳述式。 「get 存取子」會讀取值。 「set 存取子」則會寫入值。
屬性的宣告方式與欄位類似,不同之處在於宣告會以寫於分隔符號 {
與 }
之間的 get 存取子或 set 存取子作為結尾,而不是以分號作為結尾。 若屬性同時具備 get 存取子和 set 存取子,則為「讀寫屬性」。 若屬性僅有 get 存取子,則為「唯讀屬性」。 若屬性僅有 set 存取子,則為「唯寫屬性」。
get 存取子會與傳回值屬於屬性型別的無參數方法對應。 set 存取子會與具有單一參數具名值且沒有任何傳回型別的方法對應。 get 存取子會計算屬性的值。 set 存取子則會為屬性提供新的值。 當屬性為指派目標,或是 ++
或 --
的運算元時,即會叫用 set 存取子。 在參考屬性的其他情況下,則會叫用 get 存取子。
MyList<T>
類別會宣告 Count
和 Capacity
這兩個屬性,它們分別是唯讀屬性和讀寫屬性。 下列程式碼是使用這些屬性的範例:
MyList<string> names = new();
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
修飾詞時,會套用至該屬性的存取子。
索引子
「索引子」是可讓物件以和陣列相同的方式進行索引編製的成員。 索引子的宣告方式與屬性類似,不同之處在於成員的名稱是 this
,後面接著在 [
與 ]
分隔符號之間撰寫的參數清單。 索引子的存取子中會提供參數。 與屬性類似,索引子可以是讀寫、唯讀及唯寫的,而索引子的存取子可以是虛擬的。
MyList<T>
類別會宣告一個採用 int
參數的單一讀寫索引子。 此索引子使得系統能夠以 int
值編製 MyList<T>
執行個體的索引。 例如:
MyList<string> names = new();
names.Add("Liz");
names.Add("Martha");
names.Add("Beth");
for (int i = 0; i < names.Count; i++)
{
string s = names[i];
names[i] = s.ToUpper();
}
索引子可以多載。 一個類別可宣告多個索引子,只要參數的數量或類型不同即可。
事件
「事件」是可讓類別或物件提供通知的成員。 事件的宣告方式與欄位類似,不同之處在於宣告會包含 event
關鍵字,且類型必須是委派類型。
在宣告事件成員的類別內,事件的行為會與委派類型的欄位相同 (前提是該事件不是抽象事件且未宣告存取子)。 欄位會儲存對委派項目的參考,該委派項目代表已新增到事件中的事件處理常式。 如果沒有任何事件處理常式存在,欄位就會是 null
。
MyList<T>
類別會宣告稱為 Changed
的單一事件成員,這表示清單中已加入了新項目,或清單項目已透過索引子 set 存取子進行變更。 Changed 事件是由 OnChanged
虛擬方法所引發,此方法會先檢查事件是否為 null
(意謂著沒有任何處理常式存在)。 引發事件的概念完全等同於叫用該事件所代表的委派。 對引發事件而言,並沒有任何特殊的語言建構。
用戶端是透過「事件處理常式」來對事件進行反應。 附加事件處理常式時,是使用 +=
運算子,移除時,則是使用 -=
運算子。 下列範例會將事件處理常式附加到 MyList<string>
的 Changed
事件。
class EventExample
{
static int s_changeCount;
static void ListChanged(object sender, EventArgs e)
{
s_changeCount++;
}
public static void Usage()
{
var names = new MyList<string>();
names.Changed += new EventHandler(ListChanged);
names.Add("Liz");
names.Add("Martha");
names.Add("Beth");
Console.WriteLine(s_changeCount); // "3"
}
}
若為需要控制事件基礎儲存體的進階案例,事件宣告可明確提供 add
和 remove
存取子,而這些存取子則與屬性的 set
存取子相似。
運算子
「運算子」是定義將特定運算式運算子套用到類別執行個體之意義的成員。 可定義的運算子有三種:一元運算子、二元運算子及轉換運算子。 所有運算子都必須宣告為 public
和 static
。
MyList<T>
類別會宣告兩個運算子:operator ==
和 operator !=
。 這些被覆寫的運算子會為運算式提供新意義,且運算式會將這些運算子套用至 MyList
執行個體。 具體來說,這些運算子會將兩個 MyList<T>
執行個體的相等定義為使用 Equals
方法比較包含的各個物件。 下列範例會使用 ==
運算子來比較兩個 MyList<int>
執行個體。
MyList<int> a = new();
a.Add(1);
a.Add(2);
MyList<int> b = new();
b.Add(1);
b.Add(2);
Console.WriteLine(a == b); // Outputs "True"
b.Add(3);
Console.WriteLine(a == b); // Outputs "False"
第一個 Console.WriteLine
會輸出 True
,因為兩個清單所包含物件的數目相同、值相同且順序相同。 如果 MyList<T>
並未定義 operator ==
,則第一個 Console.WriteLine
所輸出的會是 False
,因為 a
和 b
參考不同的 MyList<int>
執行個體。
完成項
「完成項」是實作將類別執行個體完成所需之動作的成員。 通常需要完成項才能釋出非受控資源。 完成項不能有參數、不能有存取性修飾元,也不能明確叫用。 系統會在記憶體回收期間自動叫用執行個體的完成項。 如需詳細資訊,請參閱完成項的相關文章。
記憶體回收行程有相當大的自由來決定何時回收物件並執行完成項。 具體而言,叫用完成項的時機並不具決定性,且在任何執行緒上都可能執行完成項。 基於這些及其他理由,類別應該只有在沒有任何其他解決方案可行時,才實作完成項。
using
陳述式提供較佳的物件解構方法。
運算式
「運算式」是由「運算元」和「運算子」建構而成。 運算式的運算子會指出要將哪些運算套用到運算元。 運算子範例包括 +
、-
、*
、/
及 new
。 運算元範例包括常值、欄位、區域變數及運算式。
當運算式包含多個運算子時,運算子的「優先順序」會控制個別運算子的評估順序。 例如,運算式 x + y * z
會評估為 x + (y * z)
,因為 *
運算子的優先順序高於 +
運算子。
當兩個優先順序相同的運算子之間有運算元時,運算子的「關聯性」會控制執行運算的順序:
- 除了指派和 Null 聯合運算子以外,所有二元運算子都是「左結合」,亦即運算的執行方向是從左到右。 例如,
x + y + z
會判斷值為(x + y) + z
。 - 指派運算子、Null 聯合運算子
??
和??=
運算子,以及條件運算子?:
皆為「右結合」,亦即運算的執行方向是從右到左。 例如,x = y = z
會判斷值為x = (y = z)
。
您可以使用括弧來控制優先順序和關聯性。 例如,x + y * z
會先將 y
乘以 z
,然後再將結果加到 x
,而 (x + y) * z
則會先將 x
與 y
相加,然後再將結果乘以 z
。
大部分的運算子都可以「多載」。 運算子多載可允許針對一個運算元屬於 (或兩個運算元都屬於) 使用者定義之類別或結構型別的運算式,指定使用者定義的運算子實作。
C# 提供的運算子可執行算術、邏輯、位元和位移 運算,以及相等和順序比較。
如需按優先順序層級排序的 C# 運算子完整清單,請參閱 C# 運算子。
陳述式
程式的動作是藉由陳述式來表達。 C# 支援數種不同類型的陳述式,其中一些是以內嵌陳述式來定義。
- 「區塊」可允許在許可單一陳述式的內容中撰寫多個陳述式。 區塊是由在
{
與}
分隔符號之間撰寫的陳述式清單所組成。 - 「宣告陳述式」可用來宣告區域變數和常數。
- 「運算式陳述式」可用來評估運算式。 可用來作為陳述式的運算式包括方法叫用、使用
new
運算子的物件配置、使用=
和複合指派運算子的指派、使用++
和--
運算子的遞增和遞減運算,以及await
運算。 - 「選取範圍陳述式」可用來選取一些可能陳述式的其中之一,以根據某個運算式的值來執行。 此群組包含
if
和switch
陳述式。 - 「反覆運算陳述式」可用來重複執行內嵌的陳述式。 此群組包含
while
、do
、for
和foreach
陳述式。 - 「跳躍陳述式」可用來轉移控制項。 此群組包含
break
、continue
、goto
、throw
、return
和yield
陳述式。 try
...catch
陳述式可用來攔截在執行區塊時發生的例外狀況,而try
...finally
陳述式則可用來指定不論是否發生例外狀況都一律會執行的最終處理程式碼。checked
和unchecked
陳述式可用來控制整數型別算術運算和轉換的溢位檢查內容。lock
陳述式可用來取得所指定物件的互斥鎖定、執行陳述式,然後釋放鎖定。using
陳述式可用來取得資源、執行陳述式,然後處置該資源。
以下列出可供使用的陳述式類型:
- 區域變數宣告。
- 區域常數宣告。
- 運算陳述式。
if
陳述式。switch
陳述式。while
陳述式。do
陳述式。for
陳述式。foreach
陳述式。break
陳述式。continue
陳述式。goto
陳述式。return
陳述式。yield
陳述式。throw
陳述式和try
陳述式。checked
和unchecked
陳述式。lock
陳述式。using
陳述式。
.NET feedback
The .NET documentation is open source. Provide feedback here.
意見反應
提交並檢視相關的意見反應