为什么可选参数的默认值必须由常量表达式或值类型的无参构造函数指定?

4

我知道可选参数的默认值必须由常量表达式或值类型的无参构造函数指定。但我不知道为什么!为什么我们不能使用其他东西?有没有人能解释一下原因?


2
请列出您想要用作默认值的其他特定内容,然后仔细考虑编译器需要满足的要求。 - 15ee8f99-57ff-4f92-890c-b56153
3
因为这就是他们制造的方式。 - rory.ap
1
我最好的猜测是它与实现有关:如果未指定参数的默认值,则在调用站点处进行替换。而且,假定常量值、null 和值类型的字节全为零都很容易替换,而更复杂的表达式比编译器团队支持的代价更高/更昂贵。 - Joe Sewell
1
C#一直支持应用任意默认值,这被称为方法重载。 - madreflection
可选参数不是CLR的功能。当编译器看到依赖默认值的方法调用时,它必须从程序集元数据中挖出默认值以便于编译调用。 因此,该值不能由代码计算,它必须是“编译时常量表达式”。与const声明相同的规则适用。 - Hans Passant
1
@KazemJavadi 这个答案可能会对你有所帮助:为什么必须指定可选参数 - user12031933
2个回答

5
C#的创建者决定可选方法参数不应该使类的二进制接口复杂化,这意味着它们应该是一种仅限于源代码的“语法糖”特性,利用语言中已经存在的任何其他机制,而不添加任何新的机制。这意味着默认值表达式必须在每次调用方法时编译时完全可解析。但是,这个要求仍然不应该阻止你能够实例化一个类作为参数的默认值,如果该类是已知的话。因此,实际情况比这更加复杂。
在像C#这样的语言中,当你只有DLL而没有源代码时,总是可以针对程序集进行编程。因此,“仅源代码”功能从来不是完全的源代码。为了向语言中添加可选的方法参数,必须引入一个小技巧:在方法的定义中,对于每个可选参数,编译器会发出某个属性,其内容指定参数的默认值应该是什么。该属性仅在编译时使用,而不是在运行时使用。因此,当您编写对该方法的调用并省略可选参数时,编译器会在目标程序集中查找可选参数属性,并知道它应传递什么值。
然而,在C#中,属性也具有这个限制:它们的所有参数都必须是编译时常量。(参见ECMA-335分区II§21“自定义属性”和分区II §23.3“自定义属性”。)
因此,最终答案是为什么默认参数值必须是编译时常量,因为它们的实现涉及幕后使用属性,而属性参数反过来必须是编译时常量。
属性参数为什么必须是编译时常量的技术解释基本上是因为语言的创建者不想让事情变得太复杂。属性参数必须非常简单,以至于它们可以转换为一个字节数组并放置在二进制文件中,而且在加载类时,这个字节数组必须非常容易地转换为一组参数传递给属性。值得一提的是,在Java中也是如此,其中“属性”称为“注释”。

1
谢谢回答。但是这个答案并没有解释为什么我们不能将一个类的对象用作默认参数值。编译器为什么不能在调用点像常量表达式一样替换那个对象呢?问题出在哪里?如果编译器这样做会发生什么? - user6755993
@KazemJavadi 真是的,你说得对。我加了一段话来修改我的答案,希望现在更清楚了。 - Mike Nakis
@KazemJavadi 实际上,我对我的答案感到不满意,所以我继续重新编写了它。希望现在事情更加清晰明了。 - Mike Nakis

1
这并非完全正确。使用ConstantAttributes是可能的。 Jon Skeet写了一篇关于此的博客文章
基本上,您也可以执行以下操作:
public void PrintDateTime([Optional, DateTimeConstant(635443315962469079L)] DateTime date)
{
    Console.WriteLine(date);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接