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



我们在升级时遇到了一个奇怪的问题,我希望我能用代码来解释它。我想了解它为什么会这样

组件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正在更改。

新Foo

public static class Foo
{
    public static string DoStuff(bool someBool = false)
    {
        // Do something
        return "some string";
    }
}

这重新编译得很好,程序集2能够成功地使用更改后的方法签名,而没有任何抱怨。我的签入已执行,并且已升级有更改的项目dll(请注意,这只是程序集1 dll)。

升级后,我们发现Assembly 2在Foo.DoStuff()调用中失败了——不幸的是,我无法提供异常,因为上面的代码正在吞噬它。

尽管程序集2中没有实际的代码更改,但它似乎在重新编译时对dll产生了影响,尽管方法签名(至少在我看来)是相同的,因为为新参数提供了默认值。

我使用dotnet peek是为了查看"旧dll"one_answers"新dll"(尽管该程序集没有代码更改,并且注意到两个dll的不同之处在于,在旧dll中,没有提供默认值参数,但在重新编译的dll中,提供了默认值参数。

我想我的问题可以归结为:

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

为什么程序集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"。(这也适用于可选参数的默认值。)

相关内容

最新更新