我们遇到了一个奇怪的促销问题,我希望我能用代码来解释它。我想理解为什么它的行为方式。
装配1
public static class Foo
{
public static string DoStuff()
{
// Do something
return "some string";
}
}
大会2:
public class Bar
{
public void SomeMethod()
{
// I realize the below is not what should be done for catching exceptions, as the exception is never thrown due to the return, and seems unnecessary either way...
// this is inherited code and has not been modified to correct.
try
{
var someValue = Foo.DoStuff();
}
catch (Exception)
{
return;
throw;
}
}
}
需求已更改,因此DoStuff需要接受一个参数,其值将稍微改变行为。请注意,只有程序集1.Foo正在改变。
New Foo
public static class Foo
{
public static string DoStuff(bool someBool = false)
{
// Do something
return "some string";
}
}
这个重新编译好,并且Assembly 2能够成功地使用更改的方法签名而无需投诉。我的办理登机手续已经完成,并且推动了有变更的项目dll(注意这只是Assembly 1 dll)。
升级后我们发现程序集2在Foo.DoStuff()调用时失败 - 遗憾的是我无法提供异常,因为上面的代码正在吞下它。
即使在程序集2中没有更改实际代码,它似乎也会对重新编译时的dll产生影响,即使方法签名 - 至少在我看来 - 是相同的,因为为新的提供了默认值参数。
我使用了dotnet peek来偷看"旧的dll"以及新的dlls" (即使没有代码更改到该程序集并且在旧DLL中注意到两个DLL中的差异,但未提供默认值参数,但在重新编译时,提供了默认值参数。
我想我的问题归结为:
答案 0 :(得分:3)
为什么程序集2上的编译代码会根据(至少我认为)透明的方法签名而发生变化?
不,不应该。如果未指定与可选参数对应的参数,则在调用站点提供默认值 - 即在您的情况下为程序集2。这就是可选参数在C#中的工作方式。这是一种痛苦,但这就是生活。
避免这种情况的方法只是“部署一切”吗?
是。基本上,程序集2中源的含义已经改变,即使代码本身没有 - 所以编译后的代码已经改变,需要重新部署。
在其他情况下,你可以看到像这样的微妙突破性变化 - 相同的旧“客户端”代码针对新的“接收”代码进行编译,但具有不同的含义。两个例子:
假设您将方法签名从Foo(long x)
更改为Foo(int x)
,但在调用网站上,您只能将其称为Foo(5)
...在两种情况下都会编译,但是不同的代码。
假设您已将方法签名从Foo(int x, int y)
更改为Foo(int y, int x)
,并且您再次调用Foo(x: 5, y: 2)
,意味着如果您明白我的意思,代码会从Foo(5, 2)
更改为Foo(2, 5)
。根据新的“接收”代码重新编译未更改的源代码将改变行为。
假设你在程序集1中有一个常量,例如public const string Foo = "Foo";
。如果将其更改为public const string Foo = "Bar"
,但不使用常量重新编译程序集,则这些程序集仍将使用值“Foo”。 (这也适用于可选参数的默认值。)