命名实参和可选实参(C# 编程指南)Named and Optional Arguments (C# Programming Guide)

C# 4 介绍命名实参和可选实参。C# 4 introduces named and optional arguments. 通过命名实参,你可以为特定形参指定实参,方法是将实参与该形参的名称关联,而不是与形参在形参列表中的位置关联。Named arguments enable you to specify an argument for a particular parameter by associating the argument with the parameter's name rather than with the parameter's position in the parameter list. 通过可选参数,你可以为某些形参省略实参。Optional arguments enable you to omit arguments for some parameters. 这两种技术都可与方法、索引器、构造函数和委托一起使用。Both techniques can be used with methods, indexers, constructors, and delegates.

使用命名参数和可选参数时,将按实参出现在实参列表(而不是形参列表)中的顺序计算这些实参。When you use named and optional arguments, the arguments are evaluated in the order in which they appear in the argument list, not the parameter list.

命名形参和可选形参一起使用时,你可以只为可选形参列表中的少数形参提供实参。Named and optional parameters, when used together, enable you to supply arguments for only a few parameters from a list of optional parameters. 此功能极大地方便了对 COM 接口(例如 Microsoft Office 自动化 API)的调用。This capability greatly facilitates calls to COM interfaces such as the Microsoft Office Automation APIs.

命名实参Named Arguments

有了命名实参,你将不再需要记住或查找形参在所调用方法的形参列表中的顺序。Named arguments free you from the need to remember or to look up the order of parameters in the parameter lists of called methods. 每个实参的形参都可按形参名称进行指定。The parameter for each argument can be specified by parameter name. 例如,通过以函数定义的顺序按位置发送实参,可以采用标准方式调用打印订单详细信息(例如卖家姓名、订单号和产品名称)的函数。For example, a function that prints order details (such as, seller name, order number & product name) can be called in the standard way by sending arguments by position, in the order defined by the function.

PrintOrderDetails("Gift Shop", 31, "Red Mug");

如果不记得形参的顺序,但却知道其名称,则可以按任意顺序发送实参。If you do not remember the order of the parameters but know their names, you can send the arguments in any order.

PrintOrderDetails(orderNum: 31, productName: "Red Mug", sellerName: "Gift Shop");

PrintOrderDetails(productName: "Red Mug", sellerName: "Gift Shop", orderNum: 31);

命名实参还可以标识每个实参所表示的含义,从而改进代码的可读性。Named arguments also improve the readability of your code by identifying what each argument represents. 在下面的示例方法中,sellerName 不得为 NULL 或空白符。In the example method below, the sellerName cannot be null or white space. 由于 sellerNameproductName 都是字符串类型,所以使用命名实参而不是按位置发送实参是有意义的,可以区分这两种类型并减少代码阅读者的困惑。As both sellerName and productName are string types, instead of sending arguments by position, it makes sense to use named arguments to disambiguate the two and reduce confusion for anyone reading the code.

当命名实参与位置实参一起使用时,只要Named arguments, when used with positional arguments, are valid as long as

  • 没有后接任何位置实参或they're not followed by any positional arguments, or

PrintOrderDetails("Gift Shop", 31, productName: "Red Mug");

  • 以 C# 7.2 开头,则它们就有效并用在正确位置 。starting with C# 7.2, they're used in the correct position. 在以下示例中,形参 orderNum 位于正确的位置,但未显式命名。In the example below, the parameter orderNum is in the correct position but isn't explicitly named.

PrintOrderDetails(sellerName: "Gift Shop", 31, productName: "Red Mug");

但是,如果其后接位置实参,则无序命名实参无效。However, out-of-order named arguments are invalid if they're followed by positional arguments.

// This generates CS1738: Named argument specifications must appear after all fixed arguments have been specified.
PrintOrderDetails(productName: "Red Mug", 31, "Gift Shop");

示例Example

以下代码执行本节以及某些其他节中的示例。The following code implements the examples from this section along with some additional ones.

class NamedExample
{
    static void Main(string[] args)
    {
        // The method can be called in the normal way, by using positional arguments.
        PrintOrderDetails("Gift Shop", 31, "Red Mug");

        // Named arguments can be supplied for the parameters in any order.
        PrintOrderDetails(orderNum: 31, productName: "Red Mug", sellerName: "Gift Shop");
        PrintOrderDetails(productName: "Red Mug", sellerName: "Gift Shop", orderNum: 31);

        // Named arguments mixed with positional arguments are valid
        // as long as they are used in their correct position.
        PrintOrderDetails("Gift Shop", 31, productName: "Red Mug");
        PrintOrderDetails(sellerName: "Gift Shop", 31, productName: "Red Mug");    // C# 7.2 onwards
        PrintOrderDetails("Gift Shop", orderNum: 31, "Red Mug");                   // C# 7.2 onwards

        // However, mixed arguments are invalid if used out-of-order.
        // The following statements will cause a compiler error.
        // PrintOrderDetails(productName: "Red Mug", 31, "Gift Shop");
        // PrintOrderDetails(31, sellerName: "Gift Shop", "Red Mug");
        // PrintOrderDetails(31, "Red Mug", sellerName: "Gift Shop");
    }

    static void PrintOrderDetails(string sellerName, int orderNum, string productName)
    {
        if (string.IsNullOrWhiteSpace(sellerName))
        {
            throw new ArgumentException(message: "Seller name cannot be null or empty.", paramName: nameof(sellerName));
        }

        Console.WriteLine($"Seller: {sellerName}, Order #: {orderNum}, Product: {productName}");
    }
}

可选实参Optional Arguments

方法、构造函数、索引器或委托的定义可以指定其形参为必需还是可选。The definition of a method, constructor, indexer, or delegate can specify that its parameters are required or that they are optional. 任何调用都必须为所有必需的形参提供实参,但可以为可选的形参省略实参。Any call must provide arguments for all required parameters, but can omit arguments for optional parameters.

每个可选形参都有一个默认值作为其定义的一部分。Each optional parameter has a default value as part of its definition. 如果没有为该形参发送实参,则使用默认值。If no argument is sent for that parameter, the default value is used. 默认值必须是以下类型的表达式之一:A default value must be one of the following types of expressions:

  • 常量表达式;a constant expression;

  • new ValType() 形式的表达式,其中 ValType 是值类型,例如 enumstructan expression of the form new ValType(), where ValType is a value type, such as an enum or a struct;

  • default(ValType) 形式的表达式,其中 ValType 是值类型。an expression of the form default(ValType), where ValType is a value type.

可选参数定义于参数列表的末尾和必需参数之后。Optional parameters are defined at the end of the parameter list, after any required parameters. 如果调用方为一系列可选形参中的任意一个形参提供了实参,则它必须为前面的所有可选形参提供实参。If the caller provides an argument for any one of a succession of optional parameters, it must provide arguments for all preceding optional parameters. 实参列表中不支持使用逗号分隔的间隔。Comma-separated gaps in the argument list are not supported. 例如,在以下代码中,使用一个必选形参和两个可选形参定义实例方法 ExampleMethodFor example, in the following code, instance method ExampleMethod is defined with one required and two optional parameters.

public void ExampleMethod(int required, string optionalstr = "default string",
    int optionalint = 10)

下面对 ExampleMethod 的调用会导致编译器错误,原因是为第三个形参而不是为第二个形参提供了实参。The following call to ExampleMethod causes a compiler error, because an argument is provided for the third parameter but not for the second.

//anExample.ExampleMethod(3, ,4);

但是,如果知道第三个形参的名称,则可以使用命名实参来完成此任务。However, if you know the name of the third parameter, you can use a named argument to accomplish the task.

anExample.ExampleMethod(3, optionalint: 4);

IntelliSense 使用括号表示可选形参,如下图所示:IntelliSense uses brackets to indicate optional parameters, as shown in the following illustration:

显示 ExampleMethod 方法的 IntelliSense 快速信息的屏幕截图。

备注

此外,还可通过使用 .NET OptionalAttribute 类声明可选参数。You can also declare optional parameters by using the .NET OptionalAttribute class. OptionalAttribute 形参不需要默认值。OptionalAttribute parameters do not require a default value.

示例Example

在以下示例中,ExampleClass 的构造函数具有一个可选形参。In the following example, the constructor for ExampleClass has one parameter, which is optional. 实例方法 ExampleMethod 具有一个必选形参(required)和两个可选形参(optionalstroptionalint)。Instance method ExampleMethod has one required parameter, required, and two optional parameters, optionalstr and optionalint. Main 中的代码演示了可用于调用构造函数和方法的不同方式。The code in Main shows the different ways in which the constructor and method can be invoked.

namespace OptionalNamespace
{
    class OptionalExample
    {
        static void Main(string[] args)
        {
            // Instance anExample does not send an argument for the constructor's
            // optional parameter.
            ExampleClass anExample = new ExampleClass();
            anExample.ExampleMethod(1, "One", 1);
            anExample.ExampleMethod(2, "Two");
            anExample.ExampleMethod(3);

            // Instance anotherExample sends an argument for the constructor's
            // optional parameter.
            ExampleClass anotherExample = new ExampleClass("Provided name");
            anotherExample.ExampleMethod(1, "One", 1);
            anotherExample.ExampleMethod(2, "Two");
            anotherExample.ExampleMethod(3);

            // The following statements produce compiler errors.

            // An argument must be supplied for the first parameter, and it
            // must be an integer.
            //anExample.ExampleMethod("One", 1);
            //anExample.ExampleMethod();

            // You cannot leave a gap in the provided arguments. 
            //anExample.ExampleMethod(3, ,4);
            //anExample.ExampleMethod(3, 4);

            // You can use a named parameter to make the previous 
            // statement work.
            anExample.ExampleMethod(3, optionalint: 4);
        }
    }

    class ExampleClass
    {
        private string _name;

        // Because the parameter for the constructor, name, has a default
        // value assigned to it, it is optional.
        public ExampleClass(string name = "Default name")
        {
            _name = name;
        }

        // The first parameter, required, has no default value assigned
        // to it. Therefore, it is not optional. Both optionalstr and 
        // optionalint have default values assigned to them. They are optional.
        public void ExampleMethod(int required, string optionalstr = "default string",
            int optionalint = 10)
        {
            Console.WriteLine("{0}: {1}, {2}, and {3}.", _name, required, optionalstr,
                optionalint);
        }
    }

    // The output from this example is the following:
    // Default name: 1, One, and 1.
    // Default name: 2, Two, and 10.
    // Default name: 3, default string, and 10.
    // Provided name: 1, One, and 1.
    // Provided name: 2, Two, and 10.
    // Provided name: 3, default string, and 10.
    // Default name: 3, default string, and 4.

}

COM 接口COM Interfaces

命名实参和可选实参,以及对动态对象的支持和其他增强功能大大提高了与 COM API(例如 Office Automation API)的互操作性。Named and optional arguments, along with support for dynamic objects and other enhancements, greatly improve interoperability with COM APIs, such as Office Automation APIs.

例如,Microsoft Office Excel 的 Range 接口中的 AutoFormat 方法有七个可选形参。For example, the AutoFormat method in the Microsoft Office Excel Range interface has seven parameters, all of which are optional. 这些形参如下图所示:These parameters are shown in the following illustration:

显示 AutoFormat 方法的 IntelliSense 快速信息的屏幕截图。

在 C# 3.0 以及早期版本中,每个形参都需要一个实参,如下例所示。In C# 3.0 and earlier versions, an argument is required for each parameter, as shown in the following example.

// In C# 3.0 and earlier versions, you need to supply an argument for
// every parameter. The following call specifies a value for the first
// parameter, and sends a placeholder value for the other six. The
// default values are used for those parameters.
var excelApp = new Microsoft.Office.Interop.Excel.Application();
excelApp.Workbooks.Add();
excelApp.Visible = true;

var myFormat = 
    Microsoft.Office.Interop.Excel.XlRangeAutoFormat.xlRangeAutoFormatAccounting1;

excelApp.get_Range("A1", "B4").AutoFormat(myFormat, Type.Missing, 
    Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);

但是,可以通过使用 C# 4.0 中引入的命名实参和可选实参来大大简化对 AutoFormat 的调用。However, you can greatly simplify the call to AutoFormat by using named and optional arguments, introduced in C# 4.0. 如果不希望更改形参的默认值,则可以通过使用命名实参和可选实参来为可选形参省略实参。Named and optional arguments enable you to omit the argument for an optional parameter if you do not want to change the parameter's default value. 在下面的调用中,仅为 7 个形参中的其中一个指定了值。In the following call, a value is specified for only one of the seven parameters.

// The following code shows the same call to AutoFormat in C# 4.0. Only
// the argument for which you want to provide a specific value is listed.
excelApp.Range["A1", "B4"].AutoFormat( Format: myFormat );

有关详细信息和示例,请参阅操作说明:在 Office 编程中使用命名参数和可选参数操作说明:使用 Visual C# 功能访问 Office 互操作对象For more information and examples, see How to: Use Named and Optional Arguments in Office Programming and How to: Access Office Interop Objects by Using Visual C# Features.

重载决策Overload Resolution

使用命名实参和可选实参将在以下方面对重载决策产生影响:Use of named and optional arguments affects overload resolution in the following ways:

  • 如果方法、索引器或构造函数的每个参数是可选的,或按名称或位置对应于调用语句中的单个自变量,且该自变量可转换为参数的类型,则方法、索引器或构造函数为执行的候选项。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.

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