用过载替换可选参数是一个重大变化吗?

Hei*_*nzi 30 .net c# vb.net optional-parameters

我知道在库方法添加一个可选参数是一个重大改变,

void Foo(int x)             // OLD
void Foo(int x, int y = 5)  // NEW
Run Code Online (Sandbox Code Playgroud)

因为在编译的代码中新版本被视为Foo(int, int).每次调用Foo(0)(源Foo(0, 5)代码)都由编译器转换为(编译代码).因此,使用编译调用的旧客户端Foo(0)将找不到合适的方法.


另一个方向呢?

void Foo(int x, int y = 5) { ... }    // OLD

void Foo(int x)        { Foo(x, 5); } // NEW
void Foo(int x, int y) { ... }        // NEW
Run Code Online (Sandbox Code Playgroud)

Foo(0)(源代码)仍然会编译,并且Foo(0, 5)(编译代码)仍然会找到合适的重载,因此,理论上,这应该可行.

它是否在实践中有效,即.NET运行时和C#/ VB编译器"正式支持"这种情况?或者调用带有可选参数的方法以某种方式"标记",导致它们在可选参数被重载替换时失败?


编辑:为了澄清,我问的二进制兼容性:是否有可能取代library.dll (old)library.dll (new)没有重新编译projectUsingLibrary.exe

Jus*_*vey 10

我认为这是一个很好的问题,所以这就是我的看法.

使用执行此操作的快速客户端:

        c1.Foo(1);
        c1.Foo(1, 2);
Run Code Online (Sandbox Code Playgroud)

使用可选参数时,客户端IL看起来像:

    IL_0000: nop
IL_0001: newobj instance void [ClassLibrary1]ClassLibrary1.Class1::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldc.i4.1
IL_0009: ldc.i4.5
IL_000a: callvirt instance void [ClassLibrary1]ClassLibrary1.Class1::Foo(int32, int32)
IL_000f: nop
IL_0010: ldloc.0
IL_0011: ldc.i4.1
IL_0012: ldc.i4.2
IL_0013: callvirt instance void [ClassLibrary1]ClassLibrary1.Class1::Foo(int32, int32)
IL_0018: nop
IL_0019: ret
Run Code Online (Sandbox Code Playgroud)

当使用重载时,它看起来像:

    IL_0000: nop
IL_0001: newobj instance void [ClassLibrary2]ClassLibrary2.Class2::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldc.i4.1
IL_0009: callvirt instance void [ClassLibrary2]ClassLibrary2.Class2::Foo(int32)
IL_000e: nop
IL_000f: ldloc.0
IL_0010: ldc.i4.1
IL_0011: ldc.i4.2
IL_0012: callvirt instance void [ClassLibrary2]ClassLibrary2.Class2::Foo(int32, int32)
IL_0017: nop
IL_0018: ret
Run Code Online (Sandbox Code Playgroud)

因此,如果您将实现从可选更改为重载,但是将客户端保留为原来的,那么它将有效地为您添加默认参数,并始终调用具有两个参数的函数,这可能是也可能不是期望的行为.