方法(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# 应用程序的入口点,并在启动程序时由公共语言运行时 (CLR) 调用。The Main method is the entry point for every C# application and it's called by the common language runtime (CLR) when the program is started.

备注

本文讨论命名的方法。This article 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 doesn't 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 类型的对象传递到 ModifyObject 方法:The 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. 例如,以下方法签名和返回语句指示该方法按对调用方的引用返回变量名 estDistanceFor 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. 具有非空的返回类型的方法都需要使用 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 local 变量(如果想要修改其值)。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 local 变量: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. 当控件到达异步方法中的 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.

备注

异步方法在遇到第一个尚未完成的 awaited 对象或到达异步方法的末尾时(以先发生者为准),将返回到调用方。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.

IteratorsIterators

迭代器对集合执行自定义迭代,如列表或数组。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