代码改变世界

C# 关于方法中的参数(可选参数和命名参数)

2012-05-31 10:56  Andrew.Wangxu  阅读(3578)  评论(2编辑  收藏  举报

    在阅读《CLR via C#(第三版)》第191页中看到方法参数的用法,这里将书本中的内容挑选一些关键的记录下来,内容如下:

 

    设计一个方法的参数时,可为部分或全部参数分配默认值。然后,调用这些方法的代码可以选择不指定部分实参,接受其默认值。除此之外,调用方法时,还可通过指定参数名称的方式为其传递实参。以下代码演示了可选参数和命名参数的用法:

 

using System;
using System.Collections.Generic;
using System.Text;

namespace ParameterInMethod
{
    class Program
    {
        private static int s_n = 0;
        private static void M(int x = 9, string s = "A", DateTime dt = default(DateTime), Guid guid = new Guid())
        {
            Console.WriteLine("x={0},s={1},dt={2},guid={3}",x,s,dt,guid);
        }

        static void Main(string[] args)
        {
            // 1. 等同于M(9, "A", default(DateTime), new Guid());
            M();

            // 2. 等同于M(8, "X", default(DateTime), new Guid());
            M(8, "X");

            // 3. 等同于M(5, "A", DateTime.Now, new Guid());
            M(5, guid: Guid.NewGuid(), dt: DateTime.Now);

            // 4. 等同于M(0, "1", default(DateTime), new Guid());
            M(s_n++, s_n++.ToString());

            // 5. 等同于以下两行代码:
            // string t1 = "2"; int t2 = 3;
            // M(t2, t1, default(DateTime), new Guid());
            M(s: (s_n++).ToString(), x: s_n++);


            Console.ReadLine();

            //程序输出结果如下:
            /*
             * x=9,s=A,dt=0001/1/1 0:00:00,guid=00000000-0000-0000-0000-000000000000
             * x=8,s=X,dt=0001/1/1 0:00:00,guid=00000000-0000-0000-0000-000000000000
             * x=5,s=A,dt=2012/5/31 10:34:28,guid=cdc348c3-6435-40ed-8e37-23d90f4f05ce
             * x=0,s=1,dt=0001/1/1 0:00:00,guid=00000000-0000-0000-0000-000000000000
             * x=3,s=2,dt=0001/1/1 0:00:00,guid=00000000-0000-0000-0000-000000000000
             */

        }
    }
}

 

如你所见,如果调用时省略了一个实参,C#编译器会自动嵌入参数的默认值。

在对 M 的第4个调用中 s_n 中的当前值(0)传给x,然后 s_n 递增。随后, s_n 的当前值(1)作为一个字符串传给 s, 然后继续递增到 2. 使用已命名的参数传递实参时,编译器仍然按从左到右的顺序对实参进行求值。

在对 M 的第5个调用中, s_n 中的当前值(2)被转换成一个字符串,并保存到编译器创建的一个临时变量(t1)中。接着, s_n 递增到3,这个值保存到编译器创建另一个临时变量(t2)中。然后, s_n 继续递增到4. 最后调用M时,向它传递的实参一次是 t2,t1 一个默认DateTime 和 新建的guid。

 

规则和原则:

  • 可以为方法、构构造器方法和有参属性(C#索引器)的参数指定默认值。还可为属于委托定义一部分的参数指定默认值。然后,在调用该委托类型的一个变量时,可以省略实参,以接受默认值。
  • 有默认值的参数必须在没有默认值的所有参数之后。换言之,一旦定义了一个有默认值的参数,它右边的所有参数也必须有默认值。例如在面前的 M 方法定义中,如果删除 s 的默认值(“A”),就会出现编译错误。但这个规则有一个例外:“参数数组”这种参数必须放在所有参数(包括有默认值的这些)之后,而且数组本身不能有一个默认值。
  • 默认值必须是编译时能确定的常量值。那么,能为哪些参数设置默认值呢?这些参数的类型可以是C#认定的基元类型。还可以包括枚举类型,以及能设为 null 的任何引用类型。对于任何值类型的一个参数,可将默认值设为值类型的一个实例,并让它的所有字段都包含零值。可以用 default 关键字或者 new 关键字来表达这个意思;两种语法将生成完全一致的IL代码。在 M 方法中设置 dt 参数和 guid 参数的默认值时,分别使用的就是这两种语法。
  • 注意不要重命名参数变量。否则,任何调用者如果以传参数名的方式传递参数,都必须修改它们的代码。例如,在前面的 M 方法声明中,如果将dt变量重命名为 dateTime,对 M 的第三个调用就会造成编译错误:error CS1739:"M" 的最佳重载没有名为“dt”的参数。
  • 如果参数用 ref 或 out 关键字进行了标识,就不能设置默认值。因为没有办法为这些参数传递一个有意义的默认值。
  • C#不允许省略逗号之间实参,比如M(1, , DateTime.Now).因为会造成可读性的影响,程序员将被迫去数这些逗号。

 

大概就是以上内容,具体细节的这里并没有说的特别的全面,可以参考本文的前面说的书籍作为参考。

 

我测试的一个Demo源码下载:https://files.cnblogs.com/andrew-blog/ParameterInMethod.rar

参考:http://www.wxzzz.com/?id=100