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

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

方法 Main 是每個 C# 應用程式的進入點,而且當程式啟動時,Common Language Runtime 會 (CLR) 呼叫它。 在使用 最上層語句的應用程式中,方法 Main 是由編譯器產生,並包含所有最上層語句。

注意

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

方法簽章

方法會在 類別結構介面 中宣告,方法是指定 或 等存取層級,例如 publicprivateabstract 、、傳 sealed 回值、方法的名稱,以及任何方法參數。 這些部份放在一起即為方法的簽章。

重要

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

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

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 關鍵字後面,則會以傳址方式將值傳回給呼叫者。 例如,下列方法簽章和 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 運算式時,控制項會傳回給呼叫者,方法中的進度會暫停,直到等候的工作完成。 當工作完成時,方法中的執行可以繼續。

注意

當非同步方法遇到尚未完成的第一個等候物件,或到達非同步方法結尾時,非同步方法會傳回給呼叫端,無論第一次發生。

非同步方法通常具有 、 TaskIAsyncEnumerable<T> 或 的 Task<TResult> 傳回型別 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 和Async 傳回類型的非同步程式設計。

運算式主體定義

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

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>

如需詳細資訊,請參閱 反覆運算器

C# 語言規格

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

另請參閱