方法 (C# 程式設計手冊)Methods (C# Programming Guide)

方法是包含一系列陳述式的程式碼區塊。A method is a code block that contains a series of statements. 程式會造成呼叫方法並指定任何所需的方法引數來執行陳述式。A program causes the statements to be executed by calling the method and specifying any required method arguments. 在 C# 中,每個執行的指示是在方法的內容中執行。In C#, every executed instruction is performed in the context of a method. Main 方法是每個 C# 應用程式的進入點,而且它是由 Common Language Runtime (CLR) 啟動程式時呼叫。The Main method is the entry point for every C# application and it is called by the common language runtime (CLR) when the program is started.

注意

本主題討論具名的方法。This topic discusses named methods. 如需匿名函式的資訊,請參閱匿名函式For information about anonymous functions, see Anonymous Functions.

方法簽章Method Signatures

方法是在 類別結構 中宣告,透過指定存取層級 (例如 publicprivate)、選擇性修飾詞 (例如 abstractsealed)、傳回值、方法的名稱,以及任何方法參數。Methods are declared in a class or struct by specifying the access level such as public or private, optional modifiers such as abstract or sealed, the return value, the name of the method, and any method parameters. 這些部份放在一起即為方法的簽章。These parts together are the signature of the method.

注意

方法的傳回類型不是方法多載用途的方法簽章的一部分。A return type of a method is not part of the signature of the method for the purposes of method overloading. 不過,在判斷委派與所指向的方法之間的相容性時,它是方法簽章的一部分。However, it is part of the signature of the method when determining the compatibility between a delegate and the method that it points to.

方法參數會放在括號中,並以逗號分隔。Method parameters are enclosed in parentheses and are separated by commas. 空括號表示方法不需要任何參數。Empty parentheses indicate that the method requires no parameters. 這個類別包含四個方法:This class contains four methods:

abstract class Motorcycle
{
    // Anyone can call this.
    public void StartEngine() {/* Method statements here */ }

    // Only derived classes can call this.
    protected void AddGas(int gallons) { /* Method statements here */ }

    // Derived classes can override the base class implementation.
    public virtual int Drive(int miles, int speed) { /* Method statements here */ return 1; }

    // Derived classes must implement this.
    public abstract double GetTopSpeed(); 
}

方法存取Method Access

在物件上呼叫方法,就像是存取欄位。Calling a method on an object is like accessing a field. 在物件名稱後面加上句點、方法的名稱與括號。After the object name, add a period, the name of the method, and parentheses. 引數會在括號中列出,並以逗號分隔。Arguments are listed within the parentheses, and are separated by commas. 因此可以如下列範例所示呼叫 Motorcycle 類別的方法:The methods of the Motorcycle class can therefore be called as in the following example:

class TestMotorcycle : Motorcycle
{

    public override double GetTopSpeed()
    {
        return 108.4;
    }

    static void Main()
    {
        
        TestMotorcycle moto = new TestMotorcycle();

        moto.StartEngine();
        moto.AddGas(15);
        moto.Drive(5, 20);
        double speed = moto.GetTopSpeed();
        Console.WriteLine("My top speed is {0}", speed);            
    }
}

方法參數與引數Method Parameters vs. Arguments

方法定義會指定所需的任何參數的名稱和類型。The method definition specifies the names and types of any parameters that are required. 在呼叫程式碼呼叫此方法時,它會提供對每個參數呼叫的引數的具體值。When calling code calls the method, it provides concrete values called arguments for each parameter. 引數都必須與參數類型相容,呼叫程式碼中使用的引數名稱 (如果有的話) 不需要與方法中定義的具名參數相同。The arguments must be compatible with the parameter type but the argument name (if any) used in the calling code does not have to be the same as the parameter named defined in the method. 例如: For example:

 public void Caller()
 {
     int numA = 4;
     // Call with an int variable.
     int productA = Square(numA);

     int numB = 32;
     // Call with another int variable.
     int productB = Square(numB);

     // Call with an integer literal.
     int productC = Square(12);

     // Call with an expression that evaulates to int.
     productC = Square(productA * 3);
 }

 int Square(int i)
 {
     // Store input argument in a local variable.
     int input = i;
     return input * input;
 }

以傳址方式傳遞與以傳值方式傳遞Passing by Reference vs. Passing by Value

根據預設,傳遞實值類型至方法時,會傳遞複本,而不是物件本身。By default, when a value type is passed to a method, a copy is passed instead of the object itself. 因此,對引數的變更對於呼叫方法中的原始複本不會有影響。Therefore, changes to the argument have no effect on the original copy in the calling method. 您可以使用 ref 關鍵字依參考傳遞實值類型。You can pass a value-type by reference by using the ref keyword. 如需詳細資訊,請參閱傳遞實值型別參數For more information, see Passing Value-Type Parameters. 如需內建實值型別的清單,請參閱實值型別表For a list of built-in value types, see Value Types Table.

傳遞參考類型的物件至方法時,會傳遞物件的參考。When an object of a reference type is passed to a method, a reference to the object is passed. 也就是說,此方法接收的不是物件本身,而是指出物件位置的引數。That is, the method receives not the object itself but an argument that indicates the location of the object. 如果您使用此參考變更物件的成員,變更會反映在呼叫方法的引數中,即使以傳值方式傳遞物件。If you change a member of the object by using this reference, the change is reflected in the argument in the calling method, even if you pass the object by value.

您會使用 class 關鍵字建立參考類型,如下列範例所示。You create a reference type by using the class keyword, as the following example shows.

public class SampleRefType
{
    public int value;
}

現在,如果您將此類型為基礎的物件傳遞至方法,即會傳遞物件的參考。Now, if you pass an object that is based on this type to a method, a reference to the object is passed. 下列範例會將類型 SampleRefType 的物件傳遞至方法 ModifyObjectThe following example passes an object of type SampleRefType to method ModifyObject.

public static void TestRefType()
{
    SampleRefType rt = new SampleRefType();
    rt.value = 44;
    ModifyObject(rt);
    Console.WriteLine(rt.value);
}
static void ModifyObject(SampleRefType obj)
{
    obj.value = 33;
}

此範例會執行與上一個範例基本上相同的動作,依傳值方式將引數傳遞至方法。The example does essentially the same thing as the previous example in that it passes an argument by value to a method. 但是,由於使用參考類型,結果會不同。But, because a reference type is used, the result is different. ModifyObject 中對參數 valueobj欄位做的修改,也會變更 value 方法中引數 rtTestRefType 欄位。The modification that is made in ModifyObject to the value field of the parameter, obj, also changes the value field of the argument, rt, in the TestRefType method. TestRefType 方法會顯示 33 做為輸出。The TestRefType method displays 33 as the output.

如需如何依傳址和依傳值方式來傳遞參考型別的詳細資訊,請參閱傳遞參考型別參數參考型別For more information about how to pass reference types by reference and by value, see Passing Reference-Type Parameters and Reference Types.

傳回值Return Values

方法可以傳回值給呼叫者。Methods can return a value to the caller. 針對傳回類型,如果列在方法名稱前面的類型不是 void,方法可以使用 return 關鍵字傳回值。If the return type, the type listed before the method name, is not void, the method can return the value by using the return keyword. 具有 return 關鍵字後面接著符合傳回類型值的陳述式,會將該值傳回給方法呼叫者。A statement with the return keyword followed by a value that matches the return type will return that value to the method caller.

可以透過傳值方式將值傳回給呼叫者,或者,從 C# 7.0 開始,是以傳址方式The value can be returned to the caller by value or, starting with C# 7.0, by reference. 如果 ref 關鍵字用於方法簽章中,並且接在每個 return 關鍵字後面,則會以傳址方式將值傳回給呼叫者。Values are returned to the caller by reference if the ref keyword is used in the method signature and it follows each return keyword. 例如,下列方法簽章和傳回陳述式指出方法以傳址方式將變數名稱 estDistance 傳回給呼叫者。For example, the following method signature and return statement indicate that the method returns a variable names estDistance by reference to the caller.

public ref double GetEstimatedDistance()
{
   return ref estDistance;
}

return 關鍵字也會停止執行方法。The return keyword also stops the execution of the method. 如果傳回類型為 void,不含值的 return 陳述式對於停止方法的執行仍很有用。If the return type is void, a return statement without a value is still useful to stop the execution of the method. 若沒有 return 關鍵字,在方法到達程式碼區塊的結尾時,方法將會停止執行。Without the return keyword, the method will stop executing when it reaches the end of the code block. 具有非 void 傳回類型的方法需要使用 return 關鍵字以傳回值。Methods with a non-void return type are required to use the return keyword to return a value. 例如,這兩種方法使用 return 關鍵字傳回整數:For example, these two methods use the return keyword to return integers:

class SimpleMath
{
    public int AddTwoNumbers(int number1, int number2)
    {
        return number1 + number2;
    }

    public int SquareANumber(int number)
    {
        return number * number;
    }
}

若要使用從方法傳回的值,呼叫方法可以在使用相同類型值的任意位置使用方法呼叫本身即已足夠。To use a value returned from a method, the calling method can use the method call itself anywhere a value of the same type would be sufficient. 您也可以指派傳回值給變數。You can also assign the return value to a variable. 例如,下列兩個程式碼範例會達到相同的目標:For example, the following two code examples accomplish the same goal:

int result = obj.AddTwoNumbers(1, 2);
result = obj.SquareANumber(result);
// The result is 9.
Console.WriteLine(result);
result = obj.SquareANumber(obj.AddTwoNumbers(1, 2));
// The result is 9.
Console.WriteLine(result);

使用區域變數,在此情況下的 result來儲存值是選擇性的。Using a local variable, in this case, result, to store a value is optional. 它有助於程式碼的可讀性,或如果您需要儲存方法的整個範圍引數的原始值,則可能為必要。It may help the readability of the code, or it may be necessary if you need to store the original value of the argument for the entire scope of the method.

若要從方法使用以傳址方式傳回的值,則在您想要修改 ref 區域變數的值時必須宣告該變數。To use a value returned by reference from a method, you must declare a ref local variable if you intend to modify its value. 例如,如果 Planet.GetEstimatedDistance 方法以傳址方式傳回 Double 值,您可以使用下列這類程式碼將它定義為 ref 區域變數:For example, if the Planet.GetEstimatedDistance method returns a Double value by reference, you can define it as a ref local variable with code like the following:

ref int distance = plant 

如果呼叫函式已將陣列傳入 M,則不需要從修改陣列內容的 M 方法傳回多維度陣列。Returning a multi-dimensional array from a method, M, that modifies the array's contents is not necessary if the calling function passed the array into M. 您可能會從 M 針對值的良好樣式或功能流程傳回產生的陣列,但這不需要,因為 C# 會以傳值方式傳遞所有參考型別,而且陣列參考的值是陣列的指標。You may return the resulting array from M for good style or functional flow of values, but it is not necessary because C# passes all reference types by value, and the value of an array reference is the pointer to the array. 在方法 M 中,任何陣列內容變更都可以透過任何具有陣列參考的程式碼觀察到,如下列範例所示。In the method M, any changes to the array's contents are observable by any code that has a reference to the array, as shown in the following example.

static void Main(string[] args)
{
    int[,] matrix = new int[2, 2];
    FillMatrix(matrix);
    // matrix is now full of -1
}

public static void FillMatrix(int[,] matrix)
{
    for (int i = 0; i < matrix.GetLength(0); i++)
    {
        for (int j = 0; j < matrix.GetLength(1); j++)
        {
            matrix[i, j] = -1;
        }
    }
}

如需詳細資訊,請參閱 returnFor more information, see return.

非同步方法Async Methods

使用非同步功能,您就可以呼叫非同步方法,而不需要使用明確回呼或手動將您的程式碼分散到多種方法或 lambda 運算式上。By using the async feature, you can invoke asynchronous methods without using explicit callbacks or manually splitting your code across multiple methods or lambda expressions.

如果您使用 async 修飾詞來標示方法,可以在方法中使用 await 運算子。If you mark a method with the async modifier, you can use the await operator in the method. 當控制項到達 async 方法的 await 運算式時,控制項會傳回給呼叫者,方法中的進度會暫停,直到等候的工作完成。When control reaches an await expression in the async method, control returns to the caller, and progress in the method is suspended until the awaited task completes. 當工作完成時,方法中的執行可以繼續。When the task is complete, execution can resume in the method.

注意

非同步方法會在遇到第一個未完成的等候物件或是到達非同步方法的結尾時 (以先發生者為準),傳回呼叫者An async method returns to the caller when either it encounters the first awaited object that’s not yet complete or it gets to the end of the async method, whichever occurs first.

非同步方法的傳回類型可以是 Task<TResult>Task或 void。An async method can have a return type of Task<TResult>, Task, or void. void 傳回類型主要用於定義需要 void 傳回類型的事件處理常式。The void return type is used primarily to define event handlers, where a void return type is required. 傳回 void 類型的非同步方法無法等候,而且 void 傳回方法的呼叫者無法攔截方法擲回的例外狀況。An async method that returns void can't be awaited, and the caller of a void-returning method can't catch exceptions that the method throws.

在下列範例中, DelayAsync 是會傳回類型 Task<TResult>的非同步方法。In the following example, DelayAsync is an async method that has a return type of Task<TResult>. DelayAsync 具有傳回整數的 return 陳述式。DelayAsync has a return statement that returns an integer. 因此 DelayAsync 的方法宣告必須具有傳回類型 Task<int>Therefore the method declaration of DelayAsync must have a return type of Task<int>. 因為傳回類型是 Task<int>awaitDoSomethingAsync 運算式的評估會產生整數,如下列陳述式所示範: int result = await delayTaskBecause the return type is Task<int>, the evaluation of the await expression in DoSomethingAsync produces an integer as the following statement demonstrates: int result = await delayTask.

startButton_Click 方法是傳回類型為 void 的非同步方法的範例。The startButton_Click method is an example of an async method that has a return type of void. 因為 DoSomethingAsync 是非同步方法,對 DoSomethingAsync 的呼叫工作必須等候,如下列陳述式所示: await DoSomethingAsync();Because DoSomethingAsync is an async method, the task for the call to DoSomethingAsync must be awaited, as the following statement shows: await DoSomethingAsync();. startButton_Click 方法都必須定義 async 修飾詞,因為方法有 await 運算式。The startButton_Click method must be defined with the async modifier because the method has an await expression.

// using System.Diagnostics;
// using System.Threading.Tasks;

// This Click event is marked with the async modifier.
private async void startButton_Click(object sender, RoutedEventArgs e)
{
    await DoSomethingAsync();
}

private async Task DoSomethingAsync()
{
    Task<int> delayTask = DelayAsync();
    int result = await delayTask;

    // The previous two statements may be combined into
    // the following statement.
    //int result = await DelayAsync();

    Debug.WriteLine("Result: " + result);
}

private async Task<int> DelayAsync()
{
    await Task.Delay(100);
    return 5;
}

// Output:
//  Result: 5

非同步方法不可以宣告任何 refout 參數,但是可以呼叫具有這類參數的方法。An async method can't declare any ref or out parameters, but it can call methods that have such parameters.

如需非同步方法的詳細資訊,請參閱使用 async 和 await 進行非同步程式設計非同步程式中的控制流程非同步傳回型別For more information about async methods, see Asynchronous Programming with async and await, Control Flow in Async Programs, and Async Return Types.

運算式主體定義Expression Body Definitions

方法定義只是立即傳回運算式的結果,或是具有單一陳述式做為方法的主體是很常見的。It is common to have method definitions that simply return immediately with the result of an expression, or that have a single statement as the body of the method. 使用 =>定義這類方法有個語法捷徑:There is a syntax shortcut for defining such methods using =>:

public Point Move(int dx, int dy) => new Point(x + dx, y + dy);   
public void Print() => Console.WriteLine(First + " " + Last);  
// Works with operators, properties, and indexers too.  
public static Complex operator +(Complex a, Complex b) => a.Add(b);  
public string Name => First + " " + Last;   
public Customer this[long id] => store.LookupCustomer(id);  

如果方法會傳回 void 或非同步方法,則方法的主體必須是陳述式運算式 (如同 lambda)。If the method returns void or is an async method, then the body of the method must be a statement expression (same as with lambdas). 若為屬性和索引子,它們必須是唯讀,因此您不應使用 get 存取子關鍵字。For properties and indexers, they must be read only, and you don't use the get accessor keyword.

迭代器Iterators

迭代器會對集合執行自訂的反覆項目,例如清單或陣列。An iterator performs a custom iteration over a collection, such as a list or an array. 迭代器會使用 yield return 陳述式來一次傳回一個項目。An iterator uses the yield return statement to return each element one at a time. yield return 到達陳述式時,會記住在程式碼中的目前位置。When a yield return statement is reached, the current location in code is remembered. 下一次呼叫迭代器時,便會從這個位置重新開始執行。Execution is restarted from that location when the iterator is called the next time.

您會使用 foreach 陳述式,透過用戶端程式碼呼叫迭代器。You call an iterator from client code by using a foreach statement.

迭代器的傳回類型可以是 IEnumerableIEnumerable<T>IEnumeratorIEnumerator<T>The return type of an iterator can be IEnumerable, IEnumerable<T>, IEnumerator, or IEnumerator<T>.

如需詳細資訊,請參閱迭代器For more information, see Iterators.

C# 語言規格C# Language Specification

如需詳細資訊,請參閱<C# 語言規格>。For more information, see the C# Language Specification. 語言規格是 C# 語法及用法的限定來源。The language specification is the definitive source for C# syntax and usage.

請參閱See Also