我想要一个可选参数并将其设置为我确定的默认值,当我这样做时:
private void Process(Foo f = new Foo())
{
}
我收到以下错误(Foo
是一个类):
'f' is type of Foo, A default parameter of a reference type other than string can only be initialized with null.
如果我将 Foo
更改为 struct
那么它可以工作,但只有默认的 parameterless 构造函数。
我阅读了文档,它清楚地表明我不能这样做,但它没有提到为什么?,为什么存在这个限制以及为什么 string
被排除在外这个?为什么可选参数的值必须是编译时常量?如果这不是常数,那么副作用会是什么?
最佳答案
起点是 CLR 对此不支持。它必须由编译器实现。你可以从一个小测试程序中看到一些东西:
class Program {
static void Main(string[] args) {
Test();
Test(42);
}
static void Test(int value = 42) {
}
}
反编译为:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 15 (0xf)
.maxstack 8
IL_0000: ldc.i4.s 42
IL_0002: call void Program::Test(int32)
IL_0007: ldc.i4.s 42
IL_0009: call void Program::Test(int32)
IL_000e: ret
} // end of method Program::Main
.method private hidebysig static void Test([opt] int32 'value') cil managed
{
.param [1] = int32(0x0000002A)
// Code size 1 (0x1)
.maxstack 8
IL_0000: ret
} // end of method Program::Test
请注意,在编译器完成后,这两个调用语句之间没有任何区别。应用默认值并在调用站点执行此操作的是编译器。
另请注意,当 Test() 方法实际存在于另一个程序集中时,这仍然需要工作。这意味着默认值需要在元数据中编码。请注意 .param
指令是如何做到这一点的。 CLI 规范 (Ecma-335) 在 II.15.4.1.4 节中记录了它
This directive stores in the metadata a constant value associated with method parameter number Int32, see §II.22.9. While the CLI requires that a value be supplied for the parameter, some tools can use the presence of this attribute to indicate that the tool rather than the user is intended to supply the value of the parameter. Unlike CIL instructions, .param uses index 0 to specify the return value of the method, index 1 to specify the first parameter of the method, index 2 to specify the second parameter of the method, and so on.
[Note: The CLI attaches no semantic whatsoever to these values—it is entirely up to compilers to implement any semantic they wish (e.g., so-called default argument values). end note]
引用的第 II.22.9 节详细介绍了常量值的含义。最相关的部分:
Type shall be exactly one of: ELEMENT_TYPE_BOOLEAN, ELEMENT_TYPE_CHAR, ELEMENT_TYPE_I1, ELEMENT_TYPE_U1, ELEMENT_TYPE_I2, ELEMENT_TYPE_U2, ELEMENT_TYPE_I4, ELEMENT_TYPE_U4, ELEMENT_TYPE_I8, ELEMENT_TYPE_U8, ELEMENT_TYPE_R4, ELEMENT_TYPE_R8, or ELEMENT_TYPE_STRING; or ELEMENT_TYPE_CLASS with a Value of zero
所以这就是问题所在,甚至引用匿名辅助方法都没有好方法,因此某种代码提升技巧也不起作用。
值得注意的是,这不是问题,您始终可以为引用类型的参数实现任意默认值。例如:
private void Process(Foo f = null)
{
if (f == null) f = new Foo();
}
这是很合理的。以及您想要在方法而不是调用站点中的代码类型。
关于c# - 为什么我不能给一个默认值作为除 null 之外的可选参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23036827/