(c # 中的方法 ) Methods in (C#)

方法是包含一系列陳述式的程式碼區塊。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# 應用程式的進入點,而且它是由通用語言執行平台 (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

方法是在 class 、或中宣告,藉 record struct 由指定:Methods are declared in a class, record, or struct by specifying:

  • 選擇性的存取層級,例如 publicprivateAn optional access level, such as public or private. 預設為 privateThe default is private.
  • 選擇性修飾詞,例如 abstractsealedOptional modifiers such as abstract or sealed.
  • 傳回值,或如果方法為無,則為 voidThe return value, or void if the method has none.
  • 方法名稱。The method name.
  • 任何方法參數。Any method parameters. 方法參數會放在括號中,並以逗號分隔。Method parameters are enclosed in parentheses and are separated by commas. 空括號表示方法不需要任何參數。Empty parentheses indicate that the method requires no parameters.

這些組件一起構成方法簽章。These parts together form the method signature.

重要

方法的傳回類型不是方法多載用途的方法簽章的一部分。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.

下例定義名為 Motorcycle 的類別,包含五個方法:The following example defines a class named Motorcycle that contains five methods:

using System;

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 can override the base class implementation.
   public virtual int Drive(TimeSpan time, int speed) { /* Method statements here */ return 0; }

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

請注意,Motorcycle 類別包含多載方法 DriveNote that the Motorcycle class includes an overloaded method, Drive. 兩種方法有相同的名稱,但是必須依其參數型別區別。Two methods have the same name, but must be differentiated by their parameter types.

方法引動過程Method invocation

方法可以是「執行個體」或「靜態」。Methods can be either instance or static. 叫用執行個體方法需要您具現化物件並針對該物件呼叫方法,執行個體方法會在該執行個體及資料上運作。Invoking an instance method requires that you instantiate an object and call the method on that object; an instance method operates on that instance and its data. 您可以參考方法所屬的類型名稱來叫用靜態方法,靜態方法不操作執行個體資料。You invoke a static method by referencing the name of the type to which the method belongs; static methods do not operate on instance data. 嘗試透過物件執行個體呼叫靜態方法會產生編譯器錯誤。Attempting to call a static method through an object instance generates a compiler error.

呼叫方法就像是存取欄位。Calling a method is like accessing a field. 在物件名稱後 (如果呼叫的是執行個體方法) 或型別名稱後 (如果呼叫的是 static 方法),加上句點、方法名稱及括弧。After the object name (if you are calling an instance method) or the type name (if you are calling a static method), add a period, the name of the method, and parentheses. 引數會列在括弧中,並以逗號分隔。Arguments are listed within the parentheses and are separated by commas.

方法定義會指定所需的任何參數的名稱和類型。The method definition specifies the names and types of any parameters that are required. 在呼叫端叫用方法時,它會針對每個參數提供具體值及呼叫的引數。When a caller invokes 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 one is used in the calling code, does not have to be the same as the parameter named defined in the method. 在下例中,Square 方法包含名為 iint 型別的單一參數。In the following example, the Square method includes a single parameter of type int named i. 第一個方法呼叫會傳遞給 Square 方法型別 intnum 變數,第二個傳遞數值常數,第三個傳遞運算式。The first method call passes the Square method a variable of type int named num; the second, a numeric constant; and the third, an expression.

public class Example
{
   public static void Main()
   {
      // Call with an int variable.
      int num = 4;
      int productA = Square(num);

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

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

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

最常見的方法引動過程形式過去使用位置引數,現在則依方法參數的順序來提供引數。The most common form of method invocation used positional arguments; it supplies arguments in the same order as method parameters. 因此可以如下列範例所示呼叫 Motorcycle 類別的方法。The methods of the Motorcycle class can therefore be called as in the following example. 例如,呼叫 Drive 方法包含兩個引數,它們會對應至方法語法的兩個參數。The call to the Drive method, for example, includes two arguments that correspond to the two parameters in the method's syntax. 第一個成為 miles 參數的值,第二個是 speed 參數的值。The first becomes the value of the miles parameter, the second the value of the speed parameter.

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

叫用方法時,您也可以使用 具名引數 ,而不是位置引數。You can also use named arguments instead of positional arguments when invoking a method. 使用具名引數時,您指定參數名稱,後面接著冒號 (":") 和引數。When using named arguments, you specify the parameter name followed by a colon (":") and the argument. 方法的引數會以任意順序出現,只要有所有必要的引數。Arguments to the method can appear in any order, as long as all required arguments are present. 下例使用具名引數來叫用 TestMotorcycle.Drive 方法。The following example uses named arguments to invoke the TestMotorcycle.Drive method. 本例中,具名引數的傳遞順序與方法參數清單的順序相反。In this example, the named arguments are passed in the opposite order from the method's parameter list.

using System;

class TestMotorcycle : Motorcycle
{
   public override int Drive(int miles, int speed)
   {
      return (int) Math.Round( ((double)miles) / speed, 0);
   }

   public override double GetTopSpeed()
   {
      return 108.4;
   }

   static void Main()
   {

      TestMotorcycle moto = new TestMotorcycle();
      moto.StartEngine();
      moto.AddGas(15);
      var travelTime = moto.Drive(speed: 60, miles: 170);
      Console.WriteLine("Travel time: approx. {0} hours", travelTime);
   }
}
// The example displays the following output:
//      Travel time: approx. 3 hours

您可以使用位置引數和具名引數來叫用方法。You can invoke a method using both positional arguments and named arguments. 不過,當命名的引數位於正確位置時,位置引數只能跟在具名引數的後面。However, positional arguments can only follow named arguments when the named arguments are in the correct positions. 下例會使用一個位置引數和一個具名引數,從前一個範例叫用 TestMotorcycle.Drive 方法。The following example invokes the TestMotorcycle.Drive method from the previous example using one positional argument and one named argument.

var travelTime = moto.Drive(170, speed: 55);

繼承和覆寫方法Inherited and overridden methods

除了在型別中明確定義的成員外,型別會繼承在其基底類別中定義的成員。In addition to the members that are explicitly defined in a type, a type inherits members defined in its base classes. 因為受管理的類型系統中之所有類型,都是直接或間接繼承自 Object 類別,所以所有的類型都會繼承其成員,例如 Equals(Object)GetType()ToString()Since all types in the managed type system inherit directly or indirectly from the Object class, all types inherit its members, such as Equals(Object), GetType(), and ToString(). 下例定義 Person 類別、具現化兩個 Person 物件,並呼叫 Person.Equals 方法以判斷兩個物件是否相等。The following example defines a Person class, instantiates two Person objects, and calls the Person.Equals method to determine whether the two objects are equal. 但是 Equals 方法不是在 Person 類別中定義,它繼承自 ObjectThe Equals method, however, is not defined in the Person class; it is inherited from Object.

using System;

public class Person
{
   public String FirstName;
}

public class Example
{
   public static void Main()
   {
      var p1 = new Person();
      p1.FirstName = "John";
      var p2 = new Person();
      p2.FirstName = "John";
      Console.WriteLine("p1 = p2: {0}", p1.Equals(p2));
   }
}
// The example displays the following output:
//      p1 = p2: False

型別可以使用 override 關鍵字並提供覆寫方法的實作,來覆寫繼承的成員。Types can override inherited members by using the override keyword and providing an implementation for the overridden method. 方法簽章必須與覆寫方法的簽章相同。The method signature must be the same as that of the overridden method. 下例與前一範例相似,不同之處在於它會覆寫 Equals(Object) 方法。The following example is like the previous one, except that it overrides the Equals(Object) method. (它也會覆寫 GetHashCode() 方法,因為兩種方法都是為了提供一致的結果。)(It also overrides the GetHashCode() method, since the two methods are intended to provide consistent results.)

using System;

public class Person
{
   public String FirstName;

   public override bool Equals(object obj)
   {
      var p2 = obj as Person;
      if (p2 == null)
         return false;
      else
         return FirstName.Equals(p2.FirstName);
   }

   public override int GetHashCode()
   {
      return FirstName.GetHashCode();
   }
}

public class Example
{
   public static void Main()
   {
      var p1 = new Person();
      p1.FirstName = "John";
      var p2 = new Person();
      p2.FirstName = "John";
      Console.WriteLine("p1 = p2: {0}", p1.Equals(p2));
   }
}
// The example displays the following output:
//      p1 = p2: True

傳遞參數Passing parameters

C# 中的類型為「實值型別」「參考型別」。Types in C# are either value types or reference types. 如需內建實數值型別的清單,請參閱 類型For a list of built-in value types, see Types. 根據預設,實值型別和參考型別都會以傳值方式傳遞至方法。By default, both value types and reference types are passed to a method by value.

以傳值方式傳遞參數Passing parameters by value

以傳值方式將實值型別傳遞至方法時,會將物件複本而不是物件本身傳遞至方法。When a value type is passed to a method by value, a copy of the object instead of the object itself is passed to the method. 因此,當控制回到呼叫端時,呼叫的方法中的物件變更不會影響原始物件。Therefore, changes to the object in the called method have no effect on the original object when control returns to the caller.

下例會以傳值方式將實值型別傳遞至方法,而呼叫的方法會嘗試變更實值型別的值。The following example passes a value type to a method by value, and the called method attempts to change the value type's value. 它會定義 int 型別的變數 (它是實值型別)、將其值初始化為 20,再將它傳遞給名為 ModifyValue 的方法,此方法會將變數值變更為 30。It defines a variable of type int, which is a value type, initializes its value to 20, and passes it to a method named ModifyValue that changes the variable's value to 30. 但傳回方法時,變數的值會維持不變。When the method returns, however, the variable's value remains unchanged.

using System;

public class Example
{
   public static void Main()
   {
      int value = 20;
      Console.WriteLine("In Main, value = {0}", value);
      ModifyValue(value);
      Console.WriteLine("Back in Main, value = {0}", value);
   }

   static void ModifyValue(int i)
   {
      i = 30;
      Console.WriteLine("In ModifyValue, parameter value = {0}", i);
      return;
   }
}
// The example displays the following output:
//      In Main, value = 20
//      In ModifyValue, parameter value = 30
//      Back in Main, value = 20

當參考型別的物件以傳值方式傳遞至方法時,就會以傳值方式傳遞物件參考。When an object of a reference type is passed to a method by value, a reference to the object is passed by value. 也就是說,此方法接收的不是物件本身,而是指出物件位置的引數。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 object when control returns to the calling method. 不過,當控制回到呼叫端時,取代傳遞至方法的物件不會影響原始物件。However, replacing the object passed to the method has no effect on the original object when control returns to the caller.

下例會定義名為SampleRefType 的類別 (此為參考型別)。The following example defines a class (which is a reference type) named SampleRefType. 它會具現化 SampleRefType 物件、將 44 指派給其 value 欄位,再將物件傳遞至 ModifyObject 方法。It instantiates a SampleRefType object, assigns 44 to its value field, and passes the object to the ModifyObject method. 本例會執行基本上與上一個範例相同的動作,依傳值方式將引數傳遞至方法。This example does essentially the same thing as the previous example -- it passes an argument by value to a method. 但因為使用了參考型別,所以結果會不同。But because a reference type is used, the result is different. ModifyObject 中對 obj.value 欄位的修改,也會將 Main 方法中引數 rtvalue 欄位變更成 33,如範例輸出所示。The modification that is made in ModifyObject to the obj.value field also changes the value field of the argument, rt, in the Main method to 33, as the output from the example shows.

using System;

public class SampleRefType
{
    public int value;
}

public class Example
{
    public static void Main()
    {
        var rt = new SampleRefType();
        rt.value = 44;
        ModifyObject(rt);
        Console.WriteLine(rt.value);
    }

    static void ModifyObject(SampleRefType obj)
    {
        obj.value = 33;
    }
}

以傳址方式傳遞參數Passing parameters by reference

當您想要變更方法中的引數值,且想要在控制回到呼叫方法時反映該變更時,您必須以傳址方式傳遞參數。You pass a parameter by reference when you want to change the value of an argument in a method and want to reflect that change when control returns to the calling method. 若要以傳址方式傳遞參數,請使用 refout 關鍵字。To pass a parameter by reference, you use the ref or out keyword. 您也可以使用傳址方式傳遞值,以避免複製但仍使用關鍵字防止修改 inYou can also pass a value by reference to avoid copying but still prevent modifications using the in keyword.

下例與前例相同,唯一差異是值以傳址方式傳遞至 ModifyValue 方法。The following example is identical to the previous one, except the value is passed by reference to the ModifyValue method. ModifyValue 方法中修改參數值時,當控制回到呼叫端時會反映值的變更。When the value of the parameter is modified in the ModifyValue method, the change in value is reflected when control returns to the caller.

using System;

public class Example
{
   public static void Main()
   {
      int value = 20;
      Console.WriteLine("In Main, value = {0}", value);
      ModifyValue(ref value);
      Console.WriteLine("Back in Main, value = {0}", value);
   }

   static void ModifyValue(ref int i)
   {
      i = 30;
      Console.WriteLine("In ModifyValue, parameter value = {0}", i);
      return;
   }
}
// The example displays the following output:
//      In Main, value = 20
//      In ModifyValue, parameter value = 30
//      Back in Main, value = 30

依 ref 參數使用的常見模式包含交換變數值。A common pattern that uses by ref parameters involves swapping the values of variables. 您以傳址方式將兩個變數傳遞至方法,且該方法會交換其內容。You pass two variables to a method by reference, and the method swaps their contents. 下例會交換整數值。The following example swaps integer values.

using System;

public class Example
{
   static void Main()
   {
      int i = 2, j = 3;
      System.Console.WriteLine("i = {0}  j = {1}" , i, j);

      Swap(ref i, ref j);

      System.Console.WriteLine("i = {0}  j = {1}" , i, j);
   }

   static void Swap(ref int x, ref int y)
   {
      int temp = x;
      x = y;
      y = temp;
   }
}
// The example displays the following output:
//      i = 2  j = 3
//      i = 3  j = 2

傳遞參考型別參數可讓您變更參考本身的值,而不是其個別項目或欄位的值。Passing a reference-type parameter allows you to change the value of the reference itself, rather than the value of its individual elements or fields.

參數陣列Parameter arrays

有時候,指定方法之引數確切數目的需求會有限制。Sometimes, the requirement that you specify the exact number of arguments to your method is restrictive. 使用 params 關鍵字指出某參數是參數陣列,可使用數目可變的引數來呼叫方法。By using the params keyword to indicate that a parameter is a parameter array, you allow your method to be called with a variable number of arguments. params 關鍵字標記的參數必須是陣列型別,而且必須是方法參數清單中的最後一個參數。The parameter tagged with the params keyword must be an array type, and it must be the last parameter in the method's parameter list.

接著,呼叫者可以使用下列四種方式來叫用方法:A caller can then invoke the method in either of four ways:

  • 傳遞包含所需項目數目的適當型別陣列。By passing an array of the appropriate type that contains the desired number of elements.
  • 將適當型別各個引數的逗點分隔清單傳遞給方法。By passing a comma-separated list of individual arguments of the appropriate type to the method.
  • 傳遞 nullBy passing null.
  • 不提供引數給參數陣列。By not providing an argument to the parameter array.

下列範例會定義能從參數陣列中傳回所有母音,名為 GetVowels 的方法。The following example defines a method named GetVowels that returns all the vowels from a parameter array. Main 方法說明叫用方法的四種方法。The Main method illustrates all four ways of invoking the method. 呼叫者不需要針對包含 params 修飾詞的參數提供任何引數。Callers are not required to supply any arguments for parameters that include the params modifier. 在此情況下,此參數為空陣列。In that case, the parameter is an empty array.

using System;
using System.Linq;

class Example
{
    static void Main()
    {
        string fromArray = GetVowels(new[] { "apple", "banana", "pear" });
        Console.WriteLine($"Vowels from array: '{fromArray}'");

        string fromMultipleArguments = GetVowels("apple", "banana", "pear");
        Console.WriteLine($"Vowels from multiple arguments: '{fromMultipleArguments}'");
        
        string fromNull = GetVowels(null);
        Console.WriteLine($"Vowels from null: '{fromNull}'");

        string fromNoValue = GetVowels();
        Console.WriteLine($"Vowels from no value: '{fromNoValue}'");
    }

    static string GetVowels(params string[] input)
    {
        if (input == null || input.Length == 0)
        {
            return string.Empty;
        }

        var vowels = new char[] { 'A', 'E', 'I', 'O', 'U' };
        return string.Concat(
            input.SelectMany(
                word => word.Where(letter => vowels.Contains(char.ToUpper(letter)))));
    }
}

// The example displays the following output:
//     Vowels from array: 'aeaaaea'
//     Vowels from multiple arguments: 'aeaaaea'
//     Vowels from null: ''
//     Vowels from no value: ''

選擇性參數和引數Optional parameters and arguments

方法定義可以指定其參數為必要項目或選擇項目。A method definition can specify that its parameters are required or that they are optional. 根據預設,參數為必要項目。By default, parameters are required. 在方法定義中包含參數的預設值即可指定選擇性參數。Optional parameters are specified by including the parameter's default value in the method definition. 呼叫此方法時,如不為選擇性參數提供任何引數,則改用預設值。When the method is called, if no argument is supplied for an optional parameter, the default value is used instead.

參數的預設值必須由下列運算式種類之一指派︰The parameter's default value must be assigned by one of the following kinds of expressions:

  • 常數,例如常值字串或數字。A constant, such as a literal string or number.
  • new ValType() 形式的運算式,其中 ValType 是實值型別。An expression of the form new ValType(), where ValType is a value type. 請注意,這會叫用實值型別的隱含無參數建構函式,它不是該型別的實際成員。Note that this invokes the value type's implicit parameterless constructor, which is not an actual member of the type.
  • default(ValType) 形式的運算式,其中 ValType 是實值型別。An expression of the form default(ValType), where ValType is a value type.

如果方法同時包含必要和選擇性參數,則選擇性參數會定義在參數清單結尾,在所有必要參數的後面。If a method includes both required and optional parameters, optional parameters are defined at the end of the parameter list, after all required parameters.

下例會定義 ExampleMethod 方法,它有一個必要參數和兩個選擇性參數。The following example defines a method, ExampleMethod, that has one required and two optional parameters.

using System;

public class Options
{
   public void ExampleMethod(int required, int optionalInt = default(int),
                             string description = "Optional Description")
   {
      Console.WriteLine("{0}: {1} + {2} = {3}", description, required,
                        optionalInt, required + optionalInt);
   }
}

如果使用位置引數叫用了有多個選擇性引數的方法,則呼叫端必須為所有選擇性參數提供引數,從第一個到最後一個。If a method with multiple optional arguments is invoked using positional arguments, the caller must supply an argument for all optional parameters from the first one to the last one for which an argument is supplied. ExampleMethod 方法為例,當呼叫端為 description 參數提供引數時,它也必須為 optionalInt 參數提供一個引數。In the case of the ExampleMethod method, for example, if the caller supplies an argument for the description parameter, it must also supply one for the optionalInt parameter. opt.ExampleMethod(2, 2, "Addition of 2 and 2"); 是有效的方法呼叫,而 opt.ExampleMethod(2, , "Addition of 2 and 0"); 會產生「引數遺失」編譯器錯誤。opt.ExampleMethod(2, 2, "Addition of 2 and 2"); is a valid method call; opt.ExampleMethod(2, , "Addition of 2 and 0"); generates an "Argument missing" compiler error.

如果使用具名引數或位置和具名引數的組合來呼叫方法,則呼叫端可以省略方法呼叫中最後一個位置引數之後的任何引數。If a method is called using named arguments or a combination of positional and named arguments, the caller can omit any arguments that follow the last positional argument in the method call.

下例呼叫三次 ExampleMethod 方法。The following example calls the ExampleMethod method three times. 前兩個方法呼叫使用位置引數。The first two method calls use positional arguments. 第一個省略了這兩個選擇性引數,而第二個省略了最後一個引數。The first omits both optional arguments, while the second omits the last argument. 第三個方法呼叫會提供所需參數的位置引數,但 description 在省略引數時,會使用具名引數將值提供給參數 optionalIntThe third method call supplies a positional argument for the required parameter but uses a named argument to supply a value to the description parameter while omitting the optionalInt argument.

public class Example
{
   public static void Main()
   {
      var opt = new Options();
      opt.ExampleMethod(10);
      opt.ExampleMethod(10, 2);
      opt.ExampleMethod(12, description: "Addition with zero:");
   }
}
// The example displays the following output:
//      Optional Description: 10 + 0 = 10
//      Optional Description: 10 + 2 = 12
//      Addition with zero:: 12 + 0 = 12

使用選擇性參數會影響「多載解析」,或 C# 編譯器判斷依方法呼叫應叫用哪個特定多載的方式,如下所示︰The use of optional parameters affects overload resolution, or the way in which the C# compiler determines which particular overload should be invoked by a method call, as follows:

  • 如果每個參數都是選擇性或為依名稱或位置對應要呼叫之陳述式的單一引數,且該引數可以轉換成參數的型別,則方法、索引子或建構函式就是執行的候選項目。A method, indexer, or constructor is a candidate for execution if each of its parameters either is optional or corresponds, by name or by position, to a single argument in the calling statement, and that argument can be converted to the type of the parameter.
  • 如果找到多個候選項目,則慣用轉換的多載解析規則會套用至明確指定的引數。If more than one candidate is found, overload resolution rules for preferred conversions are applied to the arguments that are explicitly specified. 會忽略選擇性參數的省略引數。Omitted arguments for optional parameters are ignored.
  • 如果兩個候選項目的評斷結果一樣好,則偏向沒有選擇性參數的候選項目,其會在呼叫中省略引數。If two candidates are judged to be equally good, preference goes to a candidate that does not have optional parameters for which arguments were omitted in the call. 這是多載解析一般偏好參數較少之候選項目的結果。This is a consequence of a general preference in overload resolution for candidates that have fewer parameters.

傳回值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 variable, constant, or expression that matches the return type will return that value to the method caller. 具有非 void 傳回類型的方法需要使用 return 關鍵字以傳回值。Methods with a non-void return type are required to use the return keyword to return a value. 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.

例如,這兩種方法使用 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.

有時候,您希望自己的方法傳回的不止單一值。Sometimes, you want your method to return more than a single value. 從 C# 7.0 開始,您可以使用「Tuple 型別」和「Tuple 常值」輕鬆達到這個目標。Starting with C# 7.0, you can do this easily by using tuple types and tuple literals. Tuple 型別會定義 Tuple 項目的資料類型。The tuple type defines the data types of the tuple's elements. Tuple 常值會提供傳回 Tuple 的實際值。Tuple literals provide the actual values of the returned tuple. 在下列範例中,(string, string, string, int) 會定義由 GetPersonalInfo 方法所傳回的 Tuple 類型。In the following example, (string, string, string, int) defines the tuple type that is returned by the GetPersonalInfo method. 運算式 (per.FirstName, per.MiddleName, per.LastName, per.Age) 是 Tuple 常值,方法會傳回 PersonInfo 物件的名字、中間名和姓氏以及年齡。The expression (per.FirstName, per.MiddleName, per.LastName, per.Age) is the tuple literal; the method returns the first, middle, and last name, along with the age, of a PersonInfo object.

public (string, string, string, int) GetPersonalInfo(string id)
{
    PersonInfo per = PersonInfo.RetrieveInfoById(id);
    return (per.FirstName, per.MiddleName, per.LastName, per.Age);
}

然後呼叫端使用傳回的 Tuple 及程式碼,如下所示︰The caller can then consume the returned tuple with code like the following:

var person = GetPersonalInfo("111111111")
Console.WriteLine($"{person.Item1} {person.Item3}: age = {person.Item4}");

在 Tuple 型別定義中也可以將名稱指派給 Tuple 項目。Names can also be assigned to the tuple elements in the tuple type definition. 下例示範使用具名項目的 GetPersonalInfo 方法替代版本:The following example shows an alternate version of the GetPersonalInfo method that uses named elements:

public (string FName, string MName, string LName, int Age) GetPersonalInfo(string id)
{
    PersonInfo per = PersonInfo.RetrieveInfoById(id);
    return (per.FirstName, per.MiddleName, per.LastName, per.Age);
}

前一次的 GetPersonInfo 方法呼叫可修改如下︰The previous call to the GetPersonInfo method can then be modified as follows:

var person = GetPersonalInfo("111111111");
Console.WriteLine($"{person.FName} {person.LName}: age = {person.Age}");

如果方法將陣列當作引數傳遞並修改個別項目的值,方法就不需要傳回陣列,雖然您可選擇這樣做以取得良好的值樣式或功能性流程。If a method is passed an array as an argument and modifies the value of individual elements, it is not necessary for the method to return the array, although you may choose to do so for good style or functional flow of values. 這是因為 C# 會以傳值方式傳遞所有的參考型別,而陣列參考的值是陣列的指標。This is because C# passes all reference types by value, and the value of an array reference is the pointer to the array. 在下例中,以 DoubleValues 方法完成的 values 陣列內容變更,都可透過任何具有陣列參考的程式碼觀察到。In the following example, changes to the contents of the values array that are made in the DoubleValues method are observable by any code that has a reference to the array.


using System;

public class Example
{
   static void Main(string[] args)
   {
      int[] values = { 2, 4, 6, 8 };
      DoubleValues(values);
      foreach (var value in values)
         Console.Write("{0}  ", value);
   }

   public static void DoubleValues(int[] arr)
   {
      for (int ctr = 0; ctr <= arr.GetUpperBound(0); ctr++)
         arr[ctr] = arr[ctr] * 2;
   }
}
// The example displays the following output:
//       4  8  12  16

擴充方法Extension methods

一般來說,有兩種方式可將方法加入至現有的型別︰Ordinarily, there are two ways to add a method to an existing type:

  • 修改該型別的原始程式碼。Modify the source code for that type. 如果您未擁有型別的原始程式碼,當然就無法這樣做。You cannot do this, of course, if you do not own the type's source code. 而如果您同時新增任何私用資料欄位以支援方法,這會成為一項重大變更。And this becomes a breaking change if you also add any private data fields to support the method.
  • 在衍生類別中定義新的方法。Define the new method in a derived class. 方法不能以使用其他型別繼承的這種方式新增,例如結構和列舉。A method cannot be added in this way using inheritance for other types, such as structures and enumerations. 也不能用來將方法「新增」至密封的類別。Nor can it be used to "add" a method to a sealed class.

擴充方法可讓您將方法「新增」至現有的類型,但不必修改型別本身或在繼承的型別中實作新方法。Extension methods let you "add" a method to an existing type without modifying the type itself or implementing the new method in an inherited type. 擴充方法也不必和其擴充型別位於相同的組件中。The extension method also does not have to reside in the same assembly as the type it extends. 呼叫擴充方法就像它是型別的定義成員一樣。You call an extension method as if it were a defined member of a type.

如需詳細資訊,請參閱擴充方法For more information, see Extension Methods.

非同步方法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 運算式時,如果等候的工作未完成,控制會回到呼叫端,而有 await 關鍵字之方法中的進度會暫停,直到等候的工作完成。When control reaches an await expression in the async method, control returns to the caller if the awaited task is not completed, and progress in the method with the await keyword 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 IAsyncEnumerable<T>voidAn async method typically has a return type of Task<TResult>, Task, IAsyncEnumerable<T>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. 從 C# 7.0 開始,非同步方法可具備任何類似工作的傳回類型Starting with C# 7.0, an async method can have any task-like return type.

在下例中,DelayAsync 是包含會傳回整數之 return 陳述式的非同步方法。In the following example, DelayAsync is an async method that has a return statement that returns an integer. 因為它是非同步方法,所以其方法宣告必須有傳回型別 Task<int>Because it is an async method, its method declaration must have a return type of Task<int>. 因為傳回型別是 Task<int>,所以 DoSomethingAsyncawait 運算式的評估會產生整數,如下列 int result = await delayTask 陳述式所示。Because the return type is Task<int>, the evaluation of the await expression in DoSomethingAsync produces an integer, as the following int result = await delayTask statement demonstrates.

using System;
using System.Threading.Tasks;

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

非同步方法不可以宣告任何 inrefout 參數,但是可以呼叫具有這類參數的方法。An async method can't declare any in, 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 and Async return types.

運算式主體成員Expression-bodied members

方法定義只是立即傳回運算式的結果,或是具有單一陳述式做為方法的主體是很常見的。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, 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 do not 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 is remembered so that the caller can request the next element in the sequence.

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

如需詳細資訊,請參閱 反覆運算器。For more information, see Iterators.

另請參閱See also