分享方式:


具名和選擇性引數 (C# 程式設計手冊)

「具名引數」 變數可讓您為參數指定引數,從而比對引數和其名稱,而不是其在參數清單中的位置。 「選擇性引數」可讓您省略某些參數的引數。 這兩種技巧都可以搭配方法、索引子、建構函式和委派使用。

當您使用具名和選擇性引數時,會依照引數清單中的引數顯示順序來評估引數,不是依照參數清單的順序。

具名參數和選擇性參數可讓您為選定的參數提供引數。 此功能可以大幅減少對 COM 介面的呼叫,例如 Microsoft Office Automation API。

具名引數

具名引數可以免除比對引數與所呼叫之方法參數清單中參數順序的麻煩。 每個參數的引數都可以參數名稱指定。 例如,列印訂單詳細資料 (例如賣方名稱、訂單號碼與產品名稱) 的函式,可按照位置傳送引數,依函式定義的順序呼叫。

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

若您不記得參數的順序,但知道其名稱,可以依照任何順序傳送引數。

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

具名引數也藉由識別每個引數所代表的意義,改善程式碼的可讀性。 在下列範例方法中,sellerName 不得為 Null 或空白字元。 因為 sellerNameproductName 都是字串類型,所以可以使用具名引數來區分兩者,並減少讀取程式碼的任何人的混淆,而不是依位置傳送引數。

在下列情況下,具名引數在與位置引數搭配使用時有效:

  • 它們的後面沒有任何位置引數,或

    PrintOrderDetails("Gift Shop", 31, productName: "Red Mug");
    
  • 其會用在正確的位置。 在下列範例中,orderNum 參數位於正確位置,但未明確命名。

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

後接不依照順序之具名引數的位置引數無效。

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

範例

下列程式碼會實作本節的範例,以及一些其他範例。

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"); 
        PrintOrderDetails("Gift Shop", orderNum: 31, "Red Mug");

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

選擇性引數

方法、建構函式、索引子或委派的定義,可以指定其是否需要參數。 任何呼叫都必須提供所有必要參數的引數,但可以省略選擇性參數的引數。

每個選擇性參數都有預設值,為其定義的一部分。 如不傳送該參數的任何引數,則使用預設值。 預設值必須是下列其中一個運算式類型︰

  • 常數運算式;
  • new ValType() 形式的運算式,其中 ValType 是實值型別,例如 enumstruct
  • default(ValType) 形式的運算式,其中 ValType 是實值型別。

選擇性參數是定義在參數清單的結尾,在任何必要參數之後。 如果呼叫端為任何一個連續的選擇性參數提供引數,它就必須提供所有前面選擇性參數的引數。 引數清單中不得包含逗點分隔的間距。 例如,在下列程式碼中,執行個體方法 ExampleMethod 使用一個必要參數及兩個選擇性參數來定義。

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

以下呼叫 ExampleMethod 子句會造成編譯器錯誤,因為提供了第三個參數的引數,但未提供第二個參數的引數。

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

不過,如果您知道第三個參數的名稱,您可以使用具名引數來完成工作。

anExample.ExampleMethod(3, optionalint: 4);

IntelliSense 使用括弧表示選擇性參數,如下圖所示:

顯示 ExampleMethod 方法之 IntelliSense 快速諮詢的螢幕擷取畫面。

注意

您也可以使用 .NET OptionalAttribute 類別來宣告選擇性參數。 OptionalAttribute 參數不需要預設值。 不過,如果需要預設值,請查看 DefaultParameterValueAttribute 類別。

範例

在下例中,ExampleClass 的建構函式有一個參數,而它是選擇性的。 ExampleMethod 執行個體方法有一個必要參數 required 和兩個選擇性參數 optionalstroptionalintMain 中的程式碼會示範叫用建構函式和方法的不同方式。

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(
                $"{_name}: {required}, {optionalstr}, and {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.
}

上述程式碼顯示的一些範例,都未正確套用選擇性參數。 第一個範例示範必須為第一個參數提供引數,這是必要參數。

呼叫端資訊屬性

呼叫端資訊屬性 (例如 CallerFilePathAttributeCallerLineNumberAttributeCallerMemberNameAttributeCallerArgumentExpressionAttribute) 可用來取得方法呼叫端的相關資訊。 當您要偵錯或需要記錄方法呼叫的相關資訊時,這些屬性特別有用。

這些屬性是選擇性參數,其中包含編譯器所提供的預設值。 呼叫端不應該明確提供這些參數的值。

COM 介面

具名引數和選擇性引數全都支援動態物件,可以大幅提升和 COM API 的互通性,例如 Office Automation API。

例如,Microsoft Office Excel Range 介面的 AutoFormat 方法有七個參數,都是選擇性參數。 下圖會顯示這些參數:

顯示 AutoFormat 方法之 IntelliSense 快速諮詢的螢幕擷取畫面。

但您可以使用具名引數和選擇性引數,大大減少對 AutoFormat 的呼叫。 若您不想變更參數的預設值,具名引數和選擇性引數可讓您省略選擇性參數的引數。 在下列的呼叫中,只指定七個參數其中之一的值。

var excelApp = new Microsoft.Office.Interop.Excel.Application();
excelApp.Workbooks.Add();
excelApp.Visible = true;

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

excelApp.Range["A1", "B4"].AutoFormat( Format: myFormat );

如需詳細資訊和範例,請參閱如何在 Office 程式設計中使用具名引數和選擇性引數如何使用 C# 功能存取 Office Interop 物件

多載解析

使用具名和選擇性引數會以下列方式影響多載解析︰

  • 如果每個參數都是選擇性或為依名稱或位置對應要呼叫之陳述式的單一引數,且該引數可以轉換成參數的型別,則方法、索引子或建構函式就是執行的候選項目。
  • 如果找到多個候選項目,則慣用轉換的多載解析規則會套用至明確指定的引數。 會忽略選擇性參數的省略引數。
  • 如果兩個候選項目的評斷結果一樣好,則偏向沒有選擇性參數的候選項目,其會在呼叫中省略引數。 多載解析通常會優先選擇參數較少的候選項目。

C# 語言規格

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