为什么不能将默认值作为null以外的可选参数提供? [英] Why can't I give a default value as optional parameter except null?

查看:171
本文介绍了为什么不能将默认值作为null以外的可选参数提供?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想要一个可选参数并将其设置为我确定的默认值,

I want to have a optional parameter and set it to default value that I determine, when I do this:

private void Process(Foo f = new Foo())
{

}

我遇到以下错误(Foo是一个类):

I'm getting the following error (Foo is a class):

'f'是Foo的类型,除字符串以外的引用类型的默认参数只能用null初始化.

'f' is type of Foo, A default parameter of a reference type other than string can only be initialized with null.

如果将Foo更改为struct,则它可以工作,但仅使用默认的 parameterless 构造函数.

If I change Foo to struct then it works but with only default parameterless constructor.

我阅读了文档,并且明确指出我无法执行此操作,但未提及为什么?,为什么存在此限制,为什么将string排除在外?为什么可选参数的值必须是 compile-time 常量?如果那不是一个常数,那么副作用是什么?

I read the documentation and it's clearly states that I cannot do this but it doesn't mention why?, Why is this restriction exists and why string is excluded from this? Why the value of an optional parameter has to be compile-time constant? If that wouldn't be a constant then what would be the side-effects ?

推荐答案

起点是CLR不支持此功能.它必须由编译器实现.您可以从一个小的测试程序中看到一些东西:

A starting point is that the CLR has no support for this. It must be implemented by the compiler. Something you can see from a little test program:

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

请注意,在完成编译后,两个调用语句之间没有什么区别.是编译器应用了默认值,并在调用站点上应用了默认值.

Note how there is no difference whatsoever between the two call statements after the compiler is done with it. It was the compiler that applied the default value and did so at the call site.

还要注意,当Test()方法实际存在于另一个程序集中时,这仍然需要工作.这意味着默认值需要在元数据中编码.请注意.param指令是如何做到的. CLI规范(Ecma-335)在II.15.4.1.4节中进行了记录

Also note that this still needs to work when the Test() method actually lives in another assembly. Which implies that the default value needs to be encoded in the metadata. Note how the .param directive did this. The CLI spec (Ecma-335) documents it in section II.15.4.1.4

此伪指令在元数据中存储与方法参数编号Int32关联的恒定值, 参见§II.22.9.尽管CLI要求为参数提供一个值,但某些工具可以使用 此属性的存在,表明该工具(而非用户)旨在提供 参数.与CIL指令不同,.param使用索引0来指定方法的返回值, 索引1指定方法的第一个参数,索引2指定方法的第二个参数 方法,等等.

[注意:CLI不对这些值附加任何语义-完全由编译器决定. 实现他们希望的任何语义(例如所谓的默认参数值).尾注]

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节详细介绍了常量值的含义.最相关的部分:

The quoted section II.22.9 goes into the detail of what a constant value means. The most relevant part:

类型应完全是以下之一: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或ELEMENT_TYPE_STRING; 或 ELEMENT_TYPE_CLASS的值为零

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

所以这就是失败的原因,甚至没有引用匿名助手方法的好方法,因此某种形式的代码提升技巧也不起作用.

So that's where the buck stops, no good way to even reference an anonymous helper method so some kind of code hoisting trick cannot work either.

值得注意的是,这不是问题,您始终可以为引用类型的参数实现任意默认值.例如:

Notable is that it just isn't a problem, you can always implement an arbitrary default value for an argument of a reference type. For example:

private void Process(Foo f = null)
{
    if (f == null) f = new Foo();

}

这是很合理的.以及您在方法中想要而不是调用站点的代码类型.

Which is quite reasonable. And the kind of code you want in the method instead of the call site.

这篇关于为什么不能将默认值作为null以外的可选参数提供?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆