c# - 为什么我不能给一个默认值作为除 null 之外的可选参数?

标签 c# optional-parameters

我想要一个可选参数并将其设置为我确定的默认值,当我这样做时:

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/

相关文章:

c# - Linq 中不区分大小写的 "contains"

c# - 多个可选参数调用函数

c# - 可选的非系统类型参数

c# - 如何在 ASP.NET 中更改超链接的颜色

c# - 通过 .NET 7 中的应用程序设置文件设置变量的问题

c# - C# 中的动态控件名称

c# - 在 XML 文档中使用 <see cref =""/> 和可选的可为空参数

c# - 当前上下文中不存在名称 'zipfile'

URL 上的 Swift Nil 异常

c# - 托管 C++/CLI 方法中的可选参数