尝试了解跨越程序集的方法签名更改

时间:2015-04-08 19:14:22

标签: c# .net dll dependencies .net-assembly

我们遇到了一个奇怪的促销问题,我希望我能用代码来解释它。我想理解为什么它的行为方式。

装配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中的差异,但未提供默认值参数,但在重新编译时,提供了默认值参数。

我想我的问题归结为:

  1. 为什么程序集2上的编译代码会根据(至少我认为)透明的方法签名而改变?
  2. 避免这种情况的方法是否只是"部署一切"?而不是尝试基于代码更改进行部署?请注意,DLL未签入,因此没有实际的代码更改"到程序集2,尽管DLL根据对程序集1的更改而有所不同。

1 个答案:

答案 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”。 (这也适用于可选参数的默认值。)