類別與物件Classes and objects

是 C# 的最基本類型。Classes are the most fundamental of C#’s types. 類別是以單一單位結合狀態 (欄位) 和動作 (方法及其他函式成員) 的資料結構。A class is a data structure that combines state (fields) and actions (methods and other function members) in a single unit. 類別可以為動態建立的類別「執行個體」(稱為「物件」) 提供定義。A class provides a definition for dynamically created instances of the class, also known as objects. 類別支援「繼承」** 和「多型」,這些是可供「衍生類別」 將「基底類別」** 延伸及特製化的機制。Classes support inheritance and polymorphism, mechanisms whereby derived classes can extend and specialize base classes.

建立新類別時,是使用類別宣告來建立。New classes are created using class declarations. 類別宣告的開頭是一個標頭,此標頭會指定類別的屬性和修飾詞、類別的名稱、基底類別 (如果提供),以及類別所實作的介面。A class declaration starts with a header that specifies the attributes and modifiers of the class, the name of the class, the base class (if given), and the interfaces implemented by the class. 此標頭後面會接著類別主體,此主體是由在 {} 分隔符號之間撰寫的成員宣告清單所組成。The header is followed by the class body, which consists of a list of member declarations written between the delimiters { and }.

以下代碼顯示名為Point: 的簡單類的聲明:The following code shows a declaration of a simple class named Point:

public class Point
{
    public int x, y;
    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
}

建立類別執行個體時,是使用 new 運算子來建立,此運算子會為新執行個體配置記憶體、叫用建構函式來將執行個體初始化,然後傳回對執行個體的參考。Instances of classes are created using the new operator, which allocates memory for a new instance, invokes a constructor to initialize the instance, and returns a reference to the instance. 下列陳述式會建立兩個 Point 物件,並以兩個變數儲存對這些物件的參考:The following statements create two Point objects and store references to those objects in two variables:

Point p1 = new Point(0, 0);
Point p2 = new Point(10, 20);

當物件不再可供存取時,系統會自動回收物件所佔用的記憶體。The memory occupied by an object is automatically reclaimed when the object is no longer reachable. 在 C# 中顯式解構物件既不必要也是不可能的。It's neither necessary nor possible to explicitly deallocate objects in C#.

成員Members

類別的成員不是靜態成員,就是執行個體成員。The members of a class are either static members or instance members. 靜態成員隸屬於類別,而執行個體成員則隸屬於物件 (類別的執行個體)。Static members belong to classes, and instance members belong to objects (instances of classes).

下面的清單概述了類可以包含的成員類型。The following list provides an overview of the kinds of members a class can contain.

  • 常數Constants
    • 與類別關聯的常數值Constant values associated with the class
  • 欄位Fields
    • 類別的變數Variables of the class
  • 方法Methods
    • 類別所能執行的計算和動作Computations and actions that can be performed by the class
  • 屬性Properties
    • 與讀取和寫入具名的類別特性關聯的動作Actions associated with reading and writing named properties of the class
  • 索引子Indexers
    • 與編製陣列之類的類別執行個體關聯的動作Actions associated with indexing instances of the class like an array
  • 事件Events
    • 類別所能產生的通知Notifications that can be generated by the class
  • 操作員Operators
    • 類別所支援的轉換和運算式運算子Conversions and expression operators supported by the class
  • 建構函式Constructors
    • 將類別執行個體或類別本身初始化所需的動作Actions required to initialize instances of the class or the class itself
  • 完成項Finalizers
    • 在永久捨棄類別執行個體之前所要執行的動作Actions to perform before instances of the class are permanently discarded
  • 類型Types
    • 類別所宣告的巢狀型別Nested types declared by the class

AccessibilityAccessibility

類的每個成員都有一個關聯的可訪問性,它控制可以訪問該成員的程式文本區域。Each member of a class has an associated accessibility, which controls the regions of program text that can access the member. 存取能力有六種可能的形式。There are six possible forms of accessibility. 訪問修改器總結如下。The access modifiers are summarized below.

  • public
    • 訪問不受限制。Access isn't limited.
  • protected
    • 訪問僅限於此類或從此類派生的類。Access is limited to this class or classes derived from this class.
  • internal
    • 訪問僅限於當前程式集(.exe、.dll 等)。Access is limited to the current assembly (.exe, .dll, and so on.).
  • protected internal
    • 訪問僅限於包含類、派生自包含類的類或同一程式集中的類。Access is limited to the containing class, classes derived from the containing class, or classes within the same assembly.
  • private
    • 訪問僅限於此類。Access is limited to this class.
  • private protected
    • 訪問僅限於從同一程式集中的包含類型派生的包含類或類。Access is limited to the containing class or classes derived from the containing type within the same assembly.

型別參數Type parameters

類別定義可以在類別名稱後面以角括弧括住型別參數名稱清單,來定義一組型別參數。A class definition may specify a set of type parameters by following the class name with angle brackets enclosing a list of type parameter names. 接著,就可以在類別宣告的主體中使用這些型別參數,來定義類別的成員。The type parameters can then be used in the body of the class declarations to define the members of the class. 在下列範例中,Pair 的型別參數是 TFirstTSecondIn the following example, the type parameters of Pair are TFirst and TSecond:

public class Pair<TFirst,TSecond>
{
    public TFirst First;
    public TSecond Second;
}

類別型別若宣告為會採用型別參數,即稱為「泛型類別型別」**。A class type that is declared to take type parameters is called a generic class type. 結構、介面和委託類型也可以是泛型的。Struct, interface, and delegate types can also be generic. 使用泛型類別時,必須為每個型別參數提供型別參數:When the generic class is used, type arguments must be provided for each of the type parameters:

Pair<int,string> pair = new Pair<int,string> { First = 1, Second = "two" };
int i = pair.First;     // TFirst is int
string s = pair.Second; // TSecond is string

泛型型別若已有提供的型別參數 (如上述的 Pair<int,string>),即稱為「建構的型別」**。A generic type with type arguments provided, like Pair<int,string> above, is called a constructed type.

基底類別Base classes

類別宣告可以在類別名稱和型別參數後面加上冒號和基底類別的名稱,來指定基底類別。A class declaration may specify a base class by following the class name and type parameters with a colon and the name of the base class. 省略基底類別規格即等同於衍生自類型 objectOmitting a base class specification is the same as deriving from type object. 在下列範例中,Point3D 的基底類別是 Point,而 Point 的基底類別是 objectIn the following example, the base class of Point3D is Point, and the base class of Point is 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) :
        base(x, y)
    {
        this.z = z;
    }
}

類別會繼承其基底類別的成員。A class inherits the members of its base class. 繼承意謂著類別隱含地包含其基底類別的所有成員,但執行個體和靜態建構函式及基底類別的完成項除外。Inheritance means that a class implicitly contains all members of its base class, except for the instance and static constructors, and the finalizers of the base class. 派生類可以將新成員添加到它繼承的成員,但不能刪除繼承成員的定義。A derived class can add new members to those members it inherits, but it can't remove the definition of an inherited member. 在先前的範例中,Point3D 會從 Point 繼承 xy 欄位,而每個 Point3D 執行個體都會包含 xyz 這三個欄位。In the previous example, Point3D inherits the x and y fields from Point, and every Point3D instance contains three fields, x, y, and z.

在類別型別到其任何基底類別型別之間都存在著隱含轉換。An implicit conversion exists from a class type to any of its base class types. 類類型的變數可以引用該類的實例或任何派生類的實例。A variable of a class type can reference an instance of that class or an instance of any derived class. 例如,以先前的類別宣告為例,Point 型別的變數可以參考 PointPoint3DFor example, given the previous class declarations, a variable of type Point can reference either a Point or a Point3D:

Point a = new Point(10, 20);
Point b = new Point3D(10, 20, 30);

欄位Fields

「欄位」** 是與類別或類別執行個體關聯的變數。A field is a variable that is associated with a class or with an instance of a class.

使用 static 修飾詞來宣告的欄位會定義靜態欄位。A field declared with the static modifier defines a static field. 靜態欄位只會識別一個儲存位置。A static field identifies exactly one storage location. 無論創建多少個類的實例,靜態欄位只有一個副本。No matter how many instances of a class are created, there's only ever one copy of a static field.

未使用 static 修飾詞來宣告的欄位會定義執行個體欄位。A field declared without the static modifier defines an instance field. 每個類別執行個體都包含一個該類別所有執行個體欄位的個別複本。Every instance of a class contains a separate copy of all the instance fields of that class.

在下面的示例中Color,類的每個實例都有r單獨的 副本,gb實例欄位,但只有一個副本的BlackWhiteRedGreenBlue靜態欄位:In the following example, each instance of the Color class has a separate copy of the r, g, and b instance fields, but there's only one copy of the Black, White, Red, Green, and Blue static fields:

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 修飾詞來宣告「唯讀欄位」**。As shown in the previous example, read-only fields may be declared with a readonly modifier. 只有在欄位的宣告或在相同類別的建構函式中,才能對 readonly 欄位進行指派。Assignment to a readonly field can only occur as part of the field’s declaration or in a constructor in the same class.

方法Methods

「方法」** 是實作物件或類別所能執行之計算或動作的成員。A method is a member that implements a computation or action that can be performed by an object or class. 存取「靜態方法」** 時,是透過類別來存取。Static methods are accessed through the class. 存取「執行個體方法」** 時,是透過類別的執行個體來存取。Instance methods are accessed through instances of the class.

方法可能會有一份「參數」** 清單和「傳回型別」**,前者代表傳送給方法的值或變數參考,後者則指定方法所計算並傳回的值型別。Methods may have a list of parameters, which represent values or variable references passed to the method, and a return type, which specifies the type of the value computed and returned by the method. 方法的返回類型是void它不傳回值。A method’s return type is void if it doesn't return a value.

與型別相同,方法也可能有一組型別參數,而呼叫方法時,必須為這些參數指定型別引數。Like types, methods may also have a set of type parameters, for which type arguments must be specified when the method is called. 與型別不同的是,型別引數通常可以從方法呼叫的引數推斷,而不需要明確指定。Unlike types, the type arguments can often be inferred from the arguments of a method call and need not be explicitly given.

在宣告方法的類別中,方法的「簽章」** 必須是唯一的。The signature of a method must be unique in the class in which the method is declared. 方法的簽章是由方法的名稱、型別參數的數目以及其參數的數目、修飾詞和型別所組成。The signature of a method consists of the name of the method, the number of type parameters and the number, modifiers, and types of its parameters. 方法的簽名不包括返回類型。The signature of a method doesn't include the return type.

參數Parameters

參數是用來將值或變數參考傳遞給方法。Parameters are used to pass values or variable references to methods. 方法的參數會從叫用方法時所指定的「引數」** 取得其實際值。The parameters of a method get their actual values from the arguments that are specified when the method is invoked. 參數有四種:值參數、參考參數、輸出參數,以及參數陣列。There are four kinds of parameters: value parameters, reference parameters, output parameters, and parameter arrays.

「值參數」** 適用於傳遞輸入引數。A value parameter is used for passing input arguments. 值參數會對應至區域變數,此變數會從針對參數傳遞的引數取得其初始值。A value parameter corresponds to a local variable that gets its initial value from the argument that was passed for the parameter. 對值參數的修改不會影響為參數傳遞的參數。Modifications to a value parameter don't affect the argument that was passed for the parameter.

只要指定預設值,值參數便可以成為選用參數,如此即可省略對應的引數。Value parameters can be optional, by specifying a default value so that corresponding arguments can be omitted.

「參考參數」** 適用於以參考方式傳遞引數。A reference parameter is used for passing arguments by reference. 針對參考參數傳遞的引數必須是含有定義值的變數,而在執行方法的期間,參考參數會代表與引數變數相同的儲存位置。The argument passed for a reference parameter must be a variable with a definite value, and during execution of the method, the reference parameter represents the same storage location as the argument variable. 宣告參考參數時,是使用 ref 修飾詞來宣告。A reference parameter is declared with the ref modifier. 下列範例示範 ref 參數的用法。The following example shows the use of ref parameters.

using System;
class RefExample
{
    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}");    // Outputs "2 1"
    }
}

「輸出參數」** 適用於以參考方式傳遞引數。An output parameter is used for passing arguments by reference. 其類似於參考參數,只不過它並不需要您明確指派值給呼叫端提供的引數。It's similar to a reference parameter, except that it doesn't require that you explicitly assign a value to the caller-provided argument. 宣告輸出參數時,是使用 out 修飾詞來宣告。An output parameter is declared with the out modifier. 下列範例示範如何使用 C# 7 中所引入的語法來利用 out 參數。The following example shows the use of out parameters using the syntax introduced in C# 7.

    using System;
    class OutExample
    {
        static void Divide(int x, int y, out int result, out int remainder)
        {
            result = x / y;
            remainder = x % y;
        }
        public static void OutUsage()
        {
            Divide(10, 3, out int res, out int rem);
            Console.WriteLine("{0} {1}", res, rem);	// Outputs "3 1"
        }
    }
}

「參數陣列」** 可允許將數目不固定的引數傳遞給方法。A parameter array permits a variable number of arguments to be passed to a method. 宣告參數陣列時,是使用 params 修飾詞來宣告。A parameter array is declared with the params modifier. 只有方法的最後一個參數可以是參數陣列,而參數陣列的型別必須是單一維度陣列型別。Only the last parameter of a method can be a parameter array, and the type of a parameter array must be a single-dimensional array type. System.Console 類別的 Write 和 WriteLine 方法即為參數陣列用法的好例子。The Write and WriteLine methods of the System.Console class are good examples of parameter array usage. 聲明如下。They're declared as follows.

public class Console
{
    public static void Write(string fmt, params object[] args) { }
    public static void WriteLine(string fmt, params object[] args) { }
    // ...
}

在使用參數陣列的方法內,參數陣列的行為與陣列型別的一般參數完全相同。Within a method that uses a parameter array, the parameter array behaves exactly like a regular parameter of an array type. 但是,在調用具有參數陣列的方法時,可以傳遞參數陣列類型的單個參數或參數陣列元素類型的任意數量的參數。However, in an invocation of a method with a parameter array, it's possible to pass either a single argument of the parameter array type or any number of arguments of the element type of the parameter array. 在後者的案例中,會自動建立陣列執行個體並以指定的引數將其初始化。In the latter case, an array instance is automatically created and initialized with the given arguments. 以下範例This example

Console.WriteLine("x={0} y={1} z={2}", x, y, z);

等同於撰寫下列程式碼。is equivalent to writing the following.

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);

方法主體和區域變數Method body and local variables

方法的主體會指定叫用方法時所要執行的陳述式。A method’s body specifies the statements to execute when the method is invoked.

方法主體可以宣告方法叫用專屬的變數。A method body can declare variables that are specific to the invocation of the method. 這類變數稱為「區域變數」**。Such variables are called local variables. 區域變數宣告會指定型別名稱、變數名稱,還可能指定初始值。A local variable declaration specifies a type name, a variable name, and possibly an initial value. 下列範例會宣告一個初始值為零的區域變數 i,以及一個沒有初始值的區域變數 jThe following example declares a local variable i with an initial value of zero and a local variable j with no initial value.

using System;
class Squares
{
    public static void WriteSquares()
    {
        int i = 0;
        int j;
        while (i < 10)
        {
            j = i * i;
            Console.WriteLine($"{i} x {i} = {j}");
            i = i + 1;
        }
    }
}

C# 要求必須「明確指派」** 區域變數,才能取得其值。C# requires a local variable to be definitely assigned before its value can be obtained. 例如,如果前面的i聲明不包含初始值,編譯器將報告後續用法的錯誤,i因為i不會在程式中的這些點明確分配。For example, if the declaration of the previous i didn't include an initial value, the compiler would report an error for the subsequent usages of i because i wouldn't be definitely assigned at those points in the program.

方法可以使用 return 陳述式將控制權交還給其呼叫端。A method can use return statements to return control to its caller. 在返回void的方法中,return語句不能指定運算式。In a method returning void, return statements can't specify an expression. 在傳回非 void 的方法中,return 陳述式必須包含會計算傳回值的運算式。In a method returning non-void, return statements must include an expression that computes the return value.

靜態和執行個體方法Static and instance methods

使用 static 修飾詞來宣告的方法即為「靜態方法」**。A method declared with a static modifier is a static method. 靜態方法不在特定實例上操作,只能直接存取靜態成員。A static method doesn't operate on a specific instance and can only directly access static members.

不使用 static 修飾詞來宣告的方法即為「執行個體方法」**。A method declared without a static modifier is an instance method. 執行個體方法會在特定的執行個體上運作,並且既可存取靜態成員也可存取執行個體成員。An instance method operates on a specific instance and can access both static and instance members. 透過 this 可以明確存取叫用執行個體方法時所在的執行個體。The instance on which an instance method was invoked can be explicitly accessed as this. this在靜態方法中引用是錯誤的。It's an error to refer to this in a static method.

下列 Entity 類別同時含有靜態成員和執行個體成員。The following Entity class has both static and instance members.

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實例都包含一個序號(大概還有一些此處未顯示的其他資訊)。Each Entity instance contains a serial number (and presumably some other information that isn't shown here). Entity 建構函式 (類似於執行個體方法) 會將具有下一個可用序號的新執行個體初始化。The Entity constructor (which is like an instance method) initializes the new instance with the next available serial number. 由於建構函式是實例成員,因此允許訪問serialNo實例欄位和nextSerialNo靜態欄位。Because the constructor is an instance member, it's permitted to access both the serialNo instance field and the nextSerialNo static field.

GetNextSerialNoSetNextSerialNo 靜態方法可以存取 nextSerialNo 靜態欄位,但如果直接存取 serialNo 執行個體欄位,則會產生錯誤。The GetNextSerialNo and SetNextSerialNo static methods can access the nextSerialNo static field, but it would be an error for them to directly access the serialNo instance field.

下列範例示範 Entity 類別的用法。The following example shows the use of the Entity class.

using System;
class EntityExample
{
    public static void Usage()
    {
        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"
    }
}

SetNextSerialNoGetNextSerialNo上調用 和 靜態方法,而GetSerialNo實例方法在類的實例上調用。The SetNextSerialNo and GetNextSerialNo static methods are invoked on the class whereas the GetSerialNo instance method is invoked on instances of the class.

虛擬、覆寫及抽象方法Virtual, override, and abstract methods

當執行個體方法宣告包含 virtual 修飾詞時,該方法即稱為「虛擬方法」**。When an instance method declaration includes a virtual modifier, the method is said to be a virtual method. 當沒有任何 virtual 修飾詞存在時,該方法則稱為「非虛擬方法」**。When no virtual modifier is present, the method is said to be a nonvirtual method.

叫用虛擬方法時,叫用所針對之執行個體的「執行階段型別」** 會決定要叫用的實際方法實作。When a virtual method is invoked, the run-time type of the instance for which that invocation takes place determines the actual method implementation to invoke. 在非虛擬方法叫用中,決定因素則是執行個體的「編譯階段型別」**。In a nonvirtual method invocation, the compile-time type of the instance is the determining factor.

在衍生類別中可以「覆寫」** 虛擬方法。A virtual method can be overridden in a derived class. 當執行個體方法宣告包含 override 修飾詞時,該方法會覆寫具有相同簽章的已繼承虛擬方法。When an instance method declaration includes an override modifier, the method overrides an inherited virtual method with the same signature. 虛擬方法宣告會導入新的方法,而覆寫方法宣告則是會提供現有已繼承之虛擬方法的新實作,來將該方法特製化。Whereas a virtual method declaration introduces a new method, an override method declaration specializes an existing inherited virtual method by providing a new implementation of that method.

「抽象方法」** 係指不含實作的虛擬方法。An abstract method is a virtual method with no implementation. 宣告抽象方法時,是使用 abstract 修飾詞,並且只有在同樣宣告為抽象的類別中,才能宣告抽象方法。An abstract method is declared with the abstract modifier and is permitted only in a class that is also declared abstract. 抽象方法必須在每個非抽象的衍生類別中被覆寫。An abstract method must be overridden in every non-abstract derived class.

下列範例會宣告一個抽象類別 Expression 和三個衍生的類別 ConstantVariableReferenceOperation,前者代表一個運算式樹狀架構節點,後者則會實作常數、變數參考及算數運算式的運算式樹狀架構節點。The following example declares an abstract class, Expression, which represents an expression tree node, and three derived classes, Constant, VariableReference, and Operation, which implement expression tree nodes for constants, variable references, and arithmetic operations. (此示例類似于,但不得與運算式樹類型混淆)。(This example is similar to, but not to be confused with the expression tree types).

using System;
using System.Collections.Generic;
public abstract class Expression
{
    public abstract double Evaluate(Dictionary<string,object> vars);
}
public class Constant: Expression
{
    double value;
    public Constant(double value)
    {
        this.value = value;
    }
    public override double Evaluate(Dictionary<string,object> vars)
    {
        return value;
    }
}
public class VariableReference: Expression
{
    string name;
    public VariableReference(string name)
    {
        this.name = name;
    }
    public override double Evaluate(Dictionary<string,object> 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(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;
        }
        throw new Exception("Unknown operator");
    }
}

先前的四個類別可用來建構算數運算式的模型。The previous four classes can be used to model arithmetic expressions. 例如,在使用這些類別執行個體的情況下,可以將運算式 x + 3 表示如下。For example, using instances of these classes, the expression x + 3 can be represented as follows.

Expression e = new Operation(
    new VariableReference("x"),
    '+',
    new Constant(3));

系統會叫用 Expression 執行個體的 Evaluate 方法來評估指定的運算式並產生 double 值。The Evaluate method of an Expression instance is invoked to evaluate the given expression and produce a double value. 此方法會採用包含變數名稱 (作為項目的索引鍵) 和值 (作為項目的值) 的 Dictionary 引數。The method takes a Dictionary argument that contains variable names (as keys of the entries) and values (as values of the entries). 因為 Evaluate 是一種抽象方法,所以衍生自 Expression 的非抽象類別必須覆寫 EvaluateBecause Evaluate is an abstract method, non-abstract classes derived from Expression must override Evaluate.

ConstantEvaluate 實作會直接傳回預存的常數。A Constant's implementation of Evaluate simply returns the stored constant. VariableReference 的實作會查詢字典中的變數名稱並傳回產生的值。A VariableReference's implementation looks up the variable name in the dictionary and returns the resulting value. Operation 的實作會先評估左邊和右邊的運算元 (透過以遞迴方式叫用其 Evaluate 方法),然後才執行指定的算數運算。An Operation's implementation first evaluates the left and right operands (by recursively invoking their Evaluate methods) and then performs the given arithmetic operation.

下列程式會使用 Expression 類別來評估不同 xy 值的 x * (y + 2) 運算式。The following program uses the Expression classes to evaluate the expression x * (y + 2) for different values of x and y.

using System;
using System.Collections.Generic;
class InheritanceExample
{
    public static void ExampleUsage()
    {
        Expression e = new Operation(
            new VariableReference("x"),
            '*',
            new Operation(
                new VariableReference("y"),
                '+',
                new Constant(2)
            )
        );
        Dictionary<string,object> vars = new Dictionary<string, object>();
        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"
    }
}

方法多載Method overloading

方法「多載」** 可允許相同類別中的多個方法擁有相同的名稱,只要它們的簽章是唯一的即可。Method overloading permits multiple methods in the same class to have the same name as long as they have unique signatures. 編譯多載方法的叫用時,編譯器會使用「多載解析」** 來判斷要叫用的特定方法。When compiling an invocation of an overloaded method, the compiler uses overload resolution to determine the specific method to invoke. 多載解析會尋找一個與引數最相符的方法,或者,如果找不到任何一個最相符的方法,則會回報錯誤。Overload resolution finds the one method that best matches the arguments or reports an error if no single best match can be found. 下列範例示範多載解析的實際運作情況。The following example shows overload resolution in effect. 方法中每個調用的UsageExample注釋顯示調用的方法。The comment for each invocation in the UsageExample method shows which method is invoked.

using System;
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)");
    }
    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<string>(string)
        F((double)1);   // Invokes F(double)
        F((object)1);   // Invokes F(object)
        F<int>(1);      // Invokes F<int>(int)
        F(1, 1);        // Invokes F(double, double)
    }
}

如範例所示,透過將引數明確轉換成確切的參數型別和 (或) 明確提供型別引數,即一律可以選取特定的方法。As shown by the example, a particular method can always be selected by explicitly casting the arguments to the exact parameter types and/or explicitly supplying type arguments.

其他函式成員Other function members

包含可執行程式碼的成員統稱為類別的「函式成員」**。Members that contain executable code are collectively known as the function members of a class. 上一節介紹方法,這些方法是函數成員的主要類型。The preceding section describes methods, which are the primary types of function members. 本節將說明 C# 所支援的其他函式成員類型:建構函式、屬性、索引子、事件、運算子及完成項。This section describes the other kinds of function members supported by C#: constructors, properties, indexers, events, operators, and finalizers.

下面的示例顯示了一個稱為 的MyList<T>泛型類,它實現了可增長的物件清單。The following example shows a generic class called MyList<T>, which implements a growable list of objects. 此類別包含數個最常見的函式成員類型。The class contains several examples of the most common kinds of function members.

注意

這個範例會建立一個 MyList 類別,這和 .NET 標準 System.Collections.Generic.List<T> 不同。This example creates a MyList class, which is not the same as the .NET standard System.Collections.Generic.List<T>. 它會說明此教學課程所需的概念,但不是該類別的取代項目。It does illustrate the concepts needed for this tour, but is not a replacement for that class.

public class MyList<T>
{
    // Constant
    const int defaultCapacity = 4;

    // Fields
    T[] items;
    int count;

    // Constructor
    public MyList(int capacity = defaultCapacity)
    {
        items = new T[capacity];
    }

    // Properties
    public int Count => count;

    public int Capacity
    {
        get { return 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;
            }
        }
    }

    // Indexer
    public T this[int index]
    {
        get
        {
            return items[index];
        }
        set
        {
            items[index] = value;
            OnChanged();
        }
    }

    // Methods
    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;
    }

    // Event
    public event EventHandler Changed;

    // Operators
    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);
}

建構函式Constructors

C# 同時支援執行個體建構函式和靜態建構函式。C# supports both instance and static constructors. 「執行個體建構函式」** 是實作將類別執行個體初始化所需之動作的成員。An instance constructor is a member that implements the actions required to initialize an instance of a class. 靜態建構函式是實現在第一次載入類本身時實現初始化類本身所需的操作的成員。A static constructor is a member that implements the actions required to initialize a class itself when it's first loaded.

建構函式的宣告方式與方法類似,但不含傳回型別且名稱會與包含它的類別相同。A constructor is declared like a method with no return type and the same name as the containing class. 如果建構函式宣告包含 static 修飾詞,則所宣告的就是靜態建構函式。If a constructor declaration includes a static modifier, it declares a static constructor. 否則,所宣告的會是執行個體建構函式。Otherwise, it declares an instance constructor.

執行個體建構函式可以多載,且可以具有選擇性參數。Instance constructors can be overloaded and can have optional parameters. 例如,MyList<T> 類別會使用單一選擇性 int 參數來宣告一個執行個體建構函式。For example, the MyList<T> class declares one instance constructor with a single optional int parameter. 叫用執行個體建構函式時,是使用 new 運算子來叫用。Instance constructors are invoked using the new operator. 下列陳述式會使用 MyList 類別的建構函式搭配或不搭配選用的引數來配置兩個 MyList<string> 執行個體。The following statements allocate two MyList<string> instances using the constructor of the MyList class with and without the optional argument.

MyList<string> list1 = new MyList<string>();
MyList<string> list2 = new MyList<string>(10);

與其他成員不同,實例建構函式不是繼承的,並且類除了在類中實際聲明的建構函式之外,沒有其他實例建構函式。Unlike other members, instance constructors aren't inherited, and a class has no instance constructors other than those constructors actually declared in the class. 如果沒有為類別提供任何執行個體建構函式,則會自動提供一個沒有任何參數的空建構函式。If no instance constructor is supplied for a class, then an empty one with no parameters is automatically provided.

屬性Properties

「屬性」** 是欄位的自然延伸。Properties are a natural extension of fields. 兩者都是具有關聯型別的具名成員,並且用來存取欄位和屬性的語法是相同的。Both are named members with associated types, and the syntax for accessing fields and properties is the same. 但是,與欄位不同,屬性不表示存儲位置。However, unlike fields, properties don't denote storage locations. 取而代之的是,屬性會有「存取子」**,這些存取子會指定讀取或寫入其值時要執行的陳述式。Instead, properties have accessors that specify the statements to be executed when their values are read or written.

屬性的宣告方式與欄位類似,不同之處在於宣告會以在 {} 分隔符號之間撰寫的 get 存取子和 (或) set 存取子作為結尾,而不是以分號作為結尾。A property is declared like a field, except that the declaration ends with a get accessor and/or a set accessor written between the delimiters { and } instead of ending in a semicolon. 同時具有 get 存取子和 set 存取子的屬性是「讀寫屬性」,只有 get 存取子的屬性是「唯讀屬性」,而只有 set 存取子的屬性則是「唯寫屬性」**。A property that has both a get accessor and a set accessor is a read-write property, a property that has only a get accessor is a read-only property, and a property that has only a set accessor is a write-only property.

get 存取子會與傳回值屬於屬性型別的無參數方法對應。A get accessor corresponds to a parameterless method with a return value of the property type. 除了作為指派目標的情況以外,在運算式中參考屬性時,會叫用屬性的 get 存取子來計算屬性的值。Except as the target of an assignment, when a property is referenced in an expression, the get accessor of the property is invoked to compute the value of the property.

set 存取子會與具有單一參數具名值且沒有任何傳回型別的方法對應。A set accessor corresponds to a method with a single parameter named value and no return type. 將屬性作為指派目標或是作為 ++ 或 -- 的運算元來參考時,會以提供新值的引數來叫用 set 存取子。When a property is referenced as the target of an assignment or as the operand of ++ or --, the set accessor is invoked with an argument that provides the new value.

MyList<T> 類別會宣告 CountCapacity 這兩個屬性,它們分別是唯讀屬性和讀寫屬性。The MyList<T> class declares two properties, Count and Capacity, which are read-only and read-write, respectively. 以下代碼是使用這些屬性的示例:The following code is an example of use of these properties:

MyList<string> names = new MyList<string>();
names.Capacity = 100;   // Invokes set accessor
int i = names.Count;    // Invokes get accessor
int j = names.Capacity; // Invokes get accessor

與欄位和方法類似,C# 也同時支援執行個體屬性和靜態屬性。Similar to fields and methods, C# supports both instance properties and static properties. 宣告靜態屬性時,是使用 static 修飾詞來宣告,而宣告執行個體屬性時,則不使用該修飾詞。Static properties are declared with the static modifier, and instance properties are declared without it.

屬性的存取子可以是虛擬的。The accessor(s) of a property can be virtual. 當屬性宣告包含 virtualabstractoverride 修飾詞時,會套用至該屬性的存取子。When a property declaration includes a virtual, abstract, or override modifier, it applies to the accessor(s) of the property.

索引子Indexers

「索引子」** 是可讓物件以和陣列相同的方式進行索引編製的成員。An indexer is a member that enables objects to be indexed in the same way as an array. 索引子的宣告方式與屬性類似,不同之處在於成員的名稱是 this,後面接著在 [] 分隔符號之間撰寫的參數清單。An indexer is declared like a property except that the name of the member is this followed by a parameter list written between the delimiters [ and ]. 索引子的存取子中會提供參數。The parameters are available in the accessor(s) of the indexer. 與屬性類似,索引子可以是讀寫、唯讀及唯寫的,而索引子的存取子可以是虛擬的。Similar to properties, indexers can be read-write, read-only, and write-only, and the accessor(s) of an indexer can be virtual.

MyList<T> 類別會宣告一個採用 int 參數的單一讀寫索引子。The MyList<T> class declares a single read-write indexer that takes an int parameter. 此索引子使得系統能夠以 int 值編製 MyList<T> 執行個體的索引。The indexer makes it possible to index MyList<T> instances with int values. 例如:For example:

MyList<string> names = new MyList<string>();
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();
}

索引子可被多載,這意謂著類別可以宣告多個索引子,只要其參數的號碼或型別不同即可。Indexers can be overloaded, meaning that a class can declare multiple indexers as long as the number or types of their parameters differ.

事件Events

「事件」** 是可讓類別或物件提供通知的成員。An event is a member that enables a class or object to provide notifications. 事件的宣告方式與欄位類似,不同之處在於其宣告包含事件關鍵字,並且型別必須是委派型別。An event is declared like a field except that the declaration includes an event keyword and the type must be a delegate type.

在聲明事件成員的類中,事件的作用類似于委託類型的欄位(前提是事件不是抽象的,並且不聲明訪問者)。Within a class that declares an event member, the event behaves just like a field of a delegate type (provided the event isn't abstract and doesn't declare accessors). 欄位會儲存對委派項目的參考,該委派項目代表已新增到事件中的事件處理常式。The field stores a reference to a delegate that represents the event handlers that have been added to the event. 如果沒有任何事件處理常式存在,欄位就會是 nullIf no event handlers are present, the field is null.

MyList<T> 類別會宣告一個名為 Changed 的單一事件成員,此成員會指出新項目已新增到清單中。The MyList<T> class declares a single event member called Changed, which indicates that a new item has been added to the list. Changed 事件是由 OnChanged 虛擬方法所引發,此方法會先檢查事件是否為 null (意謂著沒有任何處理常式存在)。The Changed event is raised by the OnChanged virtual method, which first checks whether the event is null (meaning that no handlers are present). 引發事件的概念完全等同於叫用該事件所代表的委派項目,因此,就引發事件而言,並沒有任何特殊的語言建構。The notion of raising an event is precisely equivalent to invoking the delegate represented by the event—thus, there are no special language constructs for raising events.

用戶端是透過「事件處理常式」** 來對事件進行反應。Clients react to events through event handlers. 附加事件處理常式時,是使用 += 運算子,移除時,則是使用 -= 運算子。Event handlers are attached using the += operator and removed using the -= operator. 下列範例會將事件處理常式附加到 MyList<string>Changed 事件。The following example attaches an event handler to the Changed event of a MyList<string>.

class EventExample
{
    static int changeCount;
    static void ListChanged(object sender, EventArgs e)
    {
        changeCount++;
    }
    public static void Usage()
    {
        MyList<string> names = new MyList<string>();
        names.Changed += new EventHandler(ListChanged);
        names.Add("Liz");
        names.Add("Martha");
        names.Add("Beth");
        Console.WriteLine(changeCount);		// Outputs "3"
    }
}

對於需要控制事件基礎存儲的高級方案,事件聲明可以顯式提供addremove訪問者,這與屬性的訪問器類似。 setFor advanced scenarios where control of the underlying storage of an event is desired, an event declaration can explicitly provide add and remove accessors, which are similar to the set accessor of a property.

操作員Operators

「運算子」** 是定義將特定運算式運算子套用到類別執行個體之意義的成員。An operator is a member that defines the meaning of applying a particular expression operator to instances of a class. 可定義的運算子有三種:一元運算子、二元運算子及轉換運算子。Three kinds of operators can be defined: unary operators, binary operators, and conversion operators. 所有運算子都必須宣告為 publicstaticAll operators must be declared as public and static.

MyList<T> 類別會宣告 operator ==operator != 這兩個運算子,藉此賦予將這些運算子套用到 MyList 執行個體的運算式新意義。The MyList<T> class declares two operators, operator == and operator !=, and thus gives new meaning to expressions that apply those operators to MyList instances. 具體而言,運算子會將兩個 MyList<T> 執行個體的等號比較定義為使用其 Equals 方法來比較所含的每個物件。Specifically, the operators define equality of two MyList<T> instances as comparing each of the contained objects using their Equals methods. 下列範例會使用 == 運算子來比較兩個 MyList<int> 執行個體。The following example uses the == operator to compare two MyList<int> instances.

MyList<int> a = new MyList<int>();
a.Add(1);
a.Add(2);
MyList<int> b = new MyList<int>();
b.Add(1);
b.Add(2);
Console.WriteLine(a == b);  // Outputs "True"
b.Add(3);
Console.WriteLine(a == b);  // Outputs "False"

第一個 Console.WriteLine 會輸出 True,因為兩個清單所包含物件的數目相同、值相同且順序相同。The first Console.WriteLine outputs True because the two lists contain the same number of objects with the same values in the same order. 如果 MyList<T> 並未定義 operator ==,則第一個 Console.WriteLine 所輸出的會是 False,因為 ab 參考不同的 MyList<int> 執行個體。Had MyList<T> not defined operator ==, the first Console.WriteLine would have output False because a and b reference different MyList<int> instances.

完成項Finalizers

「完成項」** 是實作將類別執行個體完成所需之動作的成員。A finalizer is a member that implements the actions required to finalize an instance of a class. 終端子不能具有參數,它們不能具有協助工具修改器,也不能顯式調用它們。Finalizers can't have parameters, they can't have accessibility modifiers, and they can't be invoked explicitly. 系統會在記憶體回收期間自動叫用執行個體的完成項。The finalizer for an instance is invoked automatically during garbage collection.

記憶體回收行程有相當大的自由來決定何時回收物件並執行完成項。The garbage collector is allowed wide latitude in deciding when to collect objects and run finalizers. 具體而言,終端子調用的時間不是確定性的,可以在任何執行緒上執行終端子。Specifically, the timing of finalizer invocations isn't deterministic, and finalizers may be executed on any thread. 基於這些及其他理由,類別應該只有在沒有任何其他解決方案可行時,才實作完成項。For these and other reasons, classes should implement finalizers only when no other solutions are feasible.

using 陳述式提供較佳的物件解構方法。The using statement provides a better approach to object destruction.