首先看一个例子:
private static int s_n = 0;
private static void M(int x = 9,string s = "A",DateTime dt = default(DateTime),
Guid guid = new Guid())
{
System.Console.WriteLine("x={0},s = {1},dt={2},guid={3}", x, s, dt, guid);
}
public static void Main()
{
M();
M(8, "X");
M(5, guid: Guid.NewGuid(), dt: DateTime.Now);
M(s_n++, s_n++.ToString());
M(s: (s_n++).ToString(), x: s_n++);
}
/**在定义的方法中,如果为部分参数指定了默认值,请注意下列规则
* …
*
* 默认值必须是编译时能确定的常量值,那么,能为那些参数设置默认值呢?这些参数的类型
* 可以是c#认定的基元类型。还包括枚举类型,以及能设为null的任何引用类型。对于任何
* 值类型的一个参数,可将默认值设为值类型的一个实例,并让它的所有字段都包含零值。可以
* 用default关键字或者new关键字来表达这个意思;两种语法将生成完全一致的IL代码。在
* M方法中设置dt参数和guid参数的默认值时,分别使用的就是这两种语法。
*/
这是书中的一段话,就是这段话让人产生了疑惑。默认值必须是编译时能确定的常量。但是参数里面有 guid = new Guid(); 这明明是运行时才可以知道的,怎么能是编译时可以确定呢?
首先,我清楚Guid是一个struct值类型。现在这个new,不过是将Guid中的数据全部置为0【还是在堆栈上】,那么。。。。new 应该还是运行时吖,问题拿到群里,和众人讨论,有群友说,对于值类型,new将数据清零,这是可以预知的行为,因此可能可以在编译时确定。于是假设这个成立,我写出了const Guid g = new Guid();结果这样的语句无法通过,因为const的语义就是编译时期常量,如果new Guid()是编译时期常量,那么应该是可以通过的。这个问题矛盾了好久才发现,对于值类型,const只能修饰一些基元类型。【汗】,
同时,我写下了如下代码:
public struct MyStruct
{
private int i;
public MyStruct(int i)
{
this.i = i;
}
}
//提示: ms的默认值必须是编译时常量。
public void fun(string s = "hello", MyStruct ms = new MyStruct(5))
{
}
将其注释起来,加入:
public void fun(string s = "hello", MyStruct ms = new MyStruct())
{
}
没有问题。这也验证了群友的那句话,对于值类型,new 无参构造函数的行为是可以预知的【因为编译器不允许定义无参构造函数】。
同时也发现了一个“有趣”的问题。对于c#的值类型,即使提供了一个有参数的构造函数,依旧可以使用无参构造函数【编译器此时提供的】。而这样的代码:
public class MyClass
{
private int i;
public MyClass(int i)
{
this.i = i;
}
}
//编译出错,提示 不包含0个参数的构造函数【符合预期和c++,java中行为语义一致】
public void fun2(string s = "world", MyClass mc = new MyClass())
{
}
如果将class改为struct,那么就是没有问题的。
ps: 对于struct值类型,编译器默认是不会为其产生构造函数的。对于包含在引用类型中的值类型,是不会调用其构造函数的,除非使用者显式调用。