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

方法是包含一系列陳述式的程式碼區塊。 程式會造成呼叫方法並指定任何所需的方法引數來執行陳述式。 在 C# 中,每個執行的指示是在方法的內容中執行。

Main 方法是每個 C# 應用程式的進入點,而且它是由通用語言執行平台 (CLR) 啟動程式時呼叫。 在使用最上層陳述式的應用程式中,Main 方法由編譯器產生,包含了所有最上層陳述式。

注意

本文討論具名方法。 如需匿名函式的資訊,請參閱 Lambda 運算式

方法簽章

方法的宣告,透過在類別結構介面中指定存取層級 (例如 publicprivate)、選擇性修飾詞 (例如 abstractsealed)、傳回值、方法名稱,以及任何方法參數。 這些部份放在一起即為方法的簽章。

重要

方法的傳回類型不是方法多載用途的方法簽章的一部分。 不過,在判斷委派與所指向的方法之間的相容性時,它是方法簽章的一部分。

方法參數會放在括號中,並以逗號分隔。 空括號表示方法不需要任何參數。 這個類別包含四個方法:

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

方法存取

在物件上呼叫方法,就像是存取欄位。 在物件名稱後面加上句點、方法的名稱與括號。 引數會在括號中列出,並以逗號分隔。 因此可以如下列範例所示呼叫 Motorcycle 類別的方法:

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

方法參數與引數的比較

方法定義會指定所需的任何參數的名稱和類型。 在呼叫程式碼呼叫此方法時,它會提供對每個參數呼叫的引數的具體值。 引數都必須相容於參數類型,但在呼叫程式碼中使用的引數名稱 (如有指定) 和方法中定義的具名參數無須相同。 例如:

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 evaluates to int.
    productC = Square(productA * 3);
}

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

利用參考和值傳遞的比較

根據預設,當實值型別執行個體傳遞給方法時,會傳遞其複本,而不是執行個體本身。 因此,變更引數對於呼叫方法中的原始執行個體沒有影響。 若要利用參考來傳遞實值型別執行個體,請使用 ref 關鍵字。 如需詳細資訊,請參閱傳遞實值型別參數

傳遞參考類型的物件至方法時,會傳遞物件的參考。 也就是說,此方法接收的不是物件本身,而是指出物件位置的引數。 如果您使用此參考變更物件的成員,變更會反映在呼叫方法的引數中,即使以傳值方式傳遞物件。

您會使用 class 關鍵字建立參考型別,如下列範例所示:

public class SampleRefType
{
    public int value;
}

現在,如果您將此類型為基礎的物件傳遞至方法,即會傳遞物件的參考。 下列範例會傳遞類型 SampleRefType 的物件給方法 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;
}

此範例會執行與上一個範例基本上相同的動作,依傳值方式將引數傳遞至方法。 但是,由於使用參考類型,結果會不同。 ModifyObject 中對參數 valueobj欄位做的修改,也會變更 value 方法中引數 rtTestRefType 欄位。 TestRefType 方法會顯示 33 做為輸出。

如需如何依傳址和依傳值方式來傳遞參考型別的詳細資訊,請參閱傳遞參考型別參數參考型別

傳回值

方法可以傳回值給呼叫者。 若傳回型別 (列在方法名稱之前的類型) 不是 void,則方法可以使用 return陳述式傳回值。 具有 return 關鍵字後面接著符合傳回類型值的陳述式,會將該值傳回給方法呼叫者。

該值可以透過值或透過參考傳回給呼叫端。 如果 ref 關鍵字用於方法簽章中,並且接在每個 return 關鍵字後面,則會以傳址方式將值傳回給呼叫者。 例如下列方法的簽章和傳回陳述式,指定了方法須透過參考,將名為 estDistance 的變數傳回給呼叫端。

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

return 關鍵字也會停止執行方法。 如果傳回類型為 void,不含值的 return 陳述式對於停止方法的執行仍很有用。 若沒有 return 關鍵字,在方法到達程式碼區塊的結尾時,方法將會停止執行。 具有非 void 傳回類型的方法需要使用 return 關鍵字以傳回值。 例如,這兩種方法使用 return 關鍵字傳回整數:

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

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

若要使用從方法傳回的值,呼叫方法可以在使用相同類型值的任意位置使用方法呼叫本身即已足夠。 您也可以指派傳回值給變數。 例如,下列兩個程式碼範例會達到相同的目標:

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來儲存值是選擇性的。 它有助於程式碼的可讀性,或如果您需要儲存方法的整個範圍引數的原始值,則可能為必要。

若要從方法使用以傳址方式傳回的值,則在您想要修改 ref 區域變數的值時必須宣告該變數。 例如,如果 Planet.GetEstimatedDistance 方法以傳址方式傳回 Double 值,您可以使用下列這類程式碼將它定義為 ref 區域變數:

ref double distance = ref Planet.GetEstimatedDistance();

如果呼叫函式已將陣列傳入 M,則不需要從修改陣列內容的 M 方法傳回多維度陣列。 您可能會從 M 針對值的良好樣式或功能流程傳回產生的陣列,但這不需要,因為 C# 會以傳值方式傳遞所有參考型別,而且陣列參考的值是陣列的指標。 在方法 M 中,任何程式碼只要參考了陣列,就能觀察到陣列內容的變更,如下列範例所示:

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

非同步方法

使用非同步功能,您就可以呼叫非同步方法,而不需要使用明確回呼或手動將您的程式碼分散到多種方法或 lambda 運算式上。

如果您使用 async 修飾詞來標示方法,可以在方法中使用 await 運算子。 當控制項到達 async 方法的 await 運算式時,控制項會傳回給呼叫者,方法中的進度會暫停,直到等候的工作完成。 當工作完成時,方法中的執行可以繼續。

注意

非同步方法會在遇到第一個未完成的等候物件或是到達非同步方法的結尾時 (以先發生者為準),傳回呼叫端。

非同步方法具有的傳回型回,通常是 Task<TResult>TaskIAsyncEnumerable<T>voidvoid 傳回型別主要用於定義需要 void 傳回型別的事件處理常式。 傳回 void 的非同步方法無法等候,而且 void 傳回方法的呼叫端無法攔截方法擲回的例外狀況。 非同步方法可具備任何類似工作的傳回類型

在下列範例中, DelayAsync 是會傳回類型 Task<TResult>的非同步方法。 DelayAsync 具有傳回整數的 return 陳述式。 因此 DelayAsync 的方法宣告必須具有傳回類型 Task<int>。 因為傳回類型是 Task<int>awaitDoSomethingAsync 運算式的評估會產生整數,如下列陳述式所示範: int result = await delayTask

例如非同步方法 Main 的傳回型別為 Task。 其會執行 DoSomethingAsync 方法,再者,因為其只以單行表示,所以可以省略 asyncawait 關鍵字。 因為 DoSomethingAsync 是非同步方法,對 DoSomethingAsync 的呼叫工作必須等候,如下列陳述式所示: await DoSomethingAsync();

class Program
{
    static Task Main() => DoSomethingAsync();

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

        Console.WriteLine($"Result: {result}");
    }

    static async Task<int> DelayAsync()
    {
        await Task.Delay(100);
        return 5;
    }
}
// Example output:
//   Result: 5

非同步方法不可以宣告任何 refout 參數,但是可以呼叫具有這類參數的方法。

如需非同步方法的詳細資訊,請參閱使用 async 和 await 進行非同步程式設計非同步傳回型別

運算式主體定義

方法定義只是立即傳回運算式的結果,或是具有單一陳述式做為方法的主體是很常見的。 使用 =>定義這類方法有個語法捷徑:

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)。 若為屬性和索引子,它們必須是唯讀,因此您不應使用 get 存取子關鍵字。

迭代器

迭代器會對集合執行自訂的反覆項目,例如清單或陣列。 迭代器會使用 yield return 陳述式,一次傳回一個項目。 當到達 yield return 陳述式時,系統會記住程式碼中的目前位置。 下一次呼叫迭代器時,便會從這個位置重新開始執行。

您會使用 foreach 陳述式,透過用戶端程式碼呼叫迭代器。

迭代器的傳回型別可以是 IEnumerableIEnumerable<T>IAsyncEnumerable<T>IEnumeratorIEnumerator<T>

如需詳細資訊,請參閱 Iterator

C# 語言規格

如需詳細資訊,請參閱<C# 語言規格>。 語言規格是 C# 語法及用法的限定來源。

另請參閱