【问题标题】:Does adding optional parameters change method signatures and would it trigger method missing exception?添加可选参数是否会更改方法签名,是否会触发方法丢失异常?
【发布时间】:2015-07-30 19:24:55
【问题描述】:

我们有几个项目正在引用库程序集,让我们调用“myUtil”,现在几个项目中引用的方法之一是,

GetData(int p1, string p2, object p3, bool p4 = false);

现在如果我把上面的方法改成这个,

GetData(int p1, string p2, object p3, bool p4 = false, bool p5 = false);

我是否必须将“myUtil.dll”程序集更新为引用它的项目?

如果不引用它,我会得到这个错误,当我更新引用时它确实有意义并且消失了,但我无法在它说你必须更新引用的地方找到有效的引用

System.MissingMethodException:找不到方法:'Void GetData(....

【问题讨论】:

  • 请对您的要求更清楚一点。听起来您有一个引用 MyUtil.dll 的 C# 项目。在一个完全独立的项目中,您向 MyUtil.dll 添加一个新方法并重新编译它。然后,您期望 MyUtil.dll 神奇地将新方法公开给引用它的第一个项目。每当您向 .dll 项目添加方法时,都必须重新编译并将其重新添加到引用它的任何其他项目中。
  • 如果项目共享相同的解决方案,那么这将是一个不同的问题。你的问题并不清楚。
  • 重建 myUtil.dll 并且您的引用项目指向您的新程序集后,您就可以开始了。
  • @Dunken 真正寻找的是它的有效参考,添加可选参数是否使它成为一种不同的方法,然后没有呢?
  • 是的,添加一个新参数,无论是否可选,都会改变方法签名。这需要您重新部署对引用它的项目的引用。

标签: c#


【解决方案1】:

必须更新所有引用。

可选参数只是语法糖。

当你有这个时:

GetData(int p1, string p2, object p3, bool p4 = false);

然后这样称呼它:

GetData(1, "p2", obj);

编译器会这样做:

GetData(1, "p2", obj, false);

您需要重新编译和重新部署所有引用它的程序集/项目。

【讨论】:

  • 感谢 Brendan 的确认,您想添加一个有效的参考,因为我自己找不到一个
【解决方案2】:

您不需要删除并重新添加引用,但您确实需要重建所有针对 DLL 编译的项目。添加可选参数是源兼容但不是二进制兼容的更改,因为编译器在 call 站点发出不同的 IL,以正常包含默认值 - 调用本身在是否省略可选参数的 IL。

例如,考虑以下代码:

class Test
{
    static void Main()
    {
        Foo(3);
        Foo();
    }

    static void Foo(int x = 5)
    {
    }
}

Main 的 IL 如下所示:

  .method private hidebysig static void  Main() cil managed
  {
    .entrypoint
    // Code size       16 (0x10)
    .maxstack  8
    IL_0000:  nop
    IL_0001:  ldc.i4.3
    IL_0002:  call       void Test::Foo(int32)
    IL_0007:  nop
    IL_0008:  ldc.i4.5
    IL_0009:  call       void Test::Foo(int32)
    IL_000e:  nop
    IL_000f:  ret
  } // end of method Test::Main

如您所见,常量 5 在Main 方法中加载并作为参数传递,就像 3 是显式的一样。同样的事情也会发生在程序集的边界上。

同样的规则也适用于更改常量的值,以及更改可选参数的默认值 - 所有这些都需要客户端重新构建。

【讨论】:

  • 谢谢 Skeet,我正在努力为这个主题找到任何有效的阅读或参考 - 添加可选参数是否会更改方法签名,它会触发方法丢失异常吗?也许我只是为了让其他开发人员开心而获得有效参考
  • @THE:我在 C# 语言规范中看不到任何非常清晰的内容,但是是的,可选参数是签名的一部分。我已经更新了我的答案,以举例说明如何编译带有可选参数的方法的调用,因此您可以看到默认值已被烘焙到调用站点中。
【解决方案3】:

如果停止使用可选参数并改用方法重载,则可以保持兼容性:

public void GetData(int p1, string p2, object p3, bool p4 = false, bool p5 = false)
{
    //Do something
}

变成

public void GetData(int p1, string p2, object p3)
{
    GetData(p1, p2, p3, false);
}

public void GetData(int p1, string p2, object p3, bool p4)
{
    GetData(p1, p2, p3, p4, false);
}

public void GetData(int p1, string p2, object p3, bool p4, bool p5)
{
    //Do something
} 

但是,除非您重新编译所有项目,否则这不会反映默认值的更改。例如,任何旧的GetData(1, "2", null) 将始终调用新的GetData(1, "2", null, false),即使您更改了

public void GetData(int p1, string p2, object p3)
{
    GetData(p1, p2, p3, false);
}

public void GetData(int p1, string p2, object p3)
{
    GetData(p1, p2, p3, true);
}

【讨论】:

  • +1 我喜欢这种编码模式,因为它不需要客户端重新编译。 (仅用于新参数添加;我意识到更改默认值确实需要客户端重新编译)
  • ⨥1 替代方案
猜你喜欢
  • 1970-01-01
  • 2020-08-08
  • 2018-08-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-06
  • 2017-11-10
  • 2013-07-28
相关资源
最近更新 更多