【问题标题】:How to ensure compilation error on signature change where 'params' keyword is used如何确保使用“params”关键字的签名更改时出现编译错误
【发布时间】:2013-12-05 13:00:03
【问题描述】:

我有这样的方法:

public void Foo(params string[] args) {
  bar(args[0]); 
  bar(args[1]);
}

新的要求导致了这样的变化:

public void Foo(string baz, params string[] args) {
  if("do bar".Equals(baz)) {
    bar(args[0]); 
    bar(args[1]);
  }
}

问题是,即使我更改了方法签名,也没有发生编译错误,这当然是正确的,但我希望每次调用 Foo 方法时都会出现编译错误,其中参数 baz尚未指定。也就是说,如果在更改之前对 Foo 的调用是这样的:

Foo(p1,p2); //where p1 and p2 are strings

现在必须是这个:

Foo(baz,p1,p2);

如果不以这种方式更改,p1 将被分配给 baz,params 数组 args 的长度为 1,并抛出 OutOfBounds 异常。

更改签名并确保所有调用代码都相应更新的最佳方法是什么? (实际情况是Foo 存在于由许多项目共享的程序集中,这些项目自动构建在构建服务器上。因此,编译错误将是检测所有需要修改以适应更改的代码的简单方法。)

编辑: 正如 Daniel Mann 和其他人所指出的,上面的例子表明我根本不应该使用 params。所以我应该解释一下,在我的真实示例中,args 并不总是需要有两个元素,就 Foo 中的逻辑而言,args 可以包含任意数量的元素。所以假设这是 Foo:

public void Foo(string baz, params string[] args) {
  if("do bar".Equals(baz)) {
    int x = GetANumberDynamically();
    for(int i = 0; i<x; i++)
      bar(args[i]); 
  }
}

【问题讨论】:

  • 为什么不改变签名来做一些完全不同的事情(比如使用int 或其他东西)。那么所有方法都会抛出编译错误?当然,更好的方法是使用 Resharper 或类似工具进行重构。
  • 添加到@dav_i:如果您首先添加int而不是string,编译,修改所有调用代码,然后将int更改为string。另请注意,if("do bar".Equals(baz)) { 是一个非常糟糕的示例,听起来需要再次重载或进行更多重构。
  • 按照@OndrejJanacek 的建议去做。这是绝对最好的方法。
  • 只是为了确定:您的参数参数是否需要两个输入或至少两个输入?在前一种情况下,我建议根本不使用参数...
  • @dav_i 我不喜欢这样的原因是依赖项目的维护分散在不同的人身上。如果由于签名中的 int 而突然在他们的表上出现构建错误,这可能会令人困惑并且不明显他们需要做什么。我想要的是一个需要修复的编译错误,这是永久解决方案。但如果这无法实现,我想我会接受你的建议。

标签: c# params-keyword


【解决方案1】:

这是解决方案。不要更改以前的方法签名,只需添加 Obsolete 属性并指定两个参数即可。

[Obsolete("Use Foo(string, params string[]) version instead of this", true)]
public void Foo(params string[] args) {
  bar(args[0]); 
  bar(args[1]);
}

然后创建一个带有新签名的新方法。

public void Foo(string baz, params string[] args) {
  if("do bar".Equals(baz)) {
    bar(args[0]); 
    bar(args[1]);
  }
}

Obsolete 属性中的第二个参数确保编译错误。没有它只会导致编译警告。有关该属性的更多信息,请访问MSDN

编辑:

根据下面 cmets 中的讨论,Daniel Mann 提出了一个有趣的问题。

这并不能解决问题。如果你调用 Foo("a", "b") 呢?在这种情况下,它仍然会调用只有两个参数的非过时方法,并导致同样的问题。

我建议在调用bar 之前检查是否有多个参数通过args

【讨论】:

  • 那不能解决问题。如果你打电话给Foo("a", "b")呢?在这种情况下,它仍然会调用只有两个参数的非过时方法,并导致同样的问题。
  • 是的,它将选择新的重载,然后args 数组中将只有一项,在提供的示例中仍然会导致运行时错误。最初的问题是关于如何避免这类问题。
  • 是的,但这不是我的问题。这只是一个糟糕的设计,OP 假设总会有两个参数通过args
  • 这是你的问题,因为你给出的答案并没有真正回答问题。
  • 嗯,我并不经常想出每个角落案例。这就是为什么还有其他人和cmets。我会适当更新答案,谢谢指出。
【解决方案2】:

如果您有 必需 参数,最简单的解决方案是不使用 params 关键字。

显然,您希望args 至少包含两个参数。可以肯定地说,这些都是必需的。为什么没有这样的方法签名?

public void Foo(string baz, string requiredArgument1, string requiredArgument2, params string[] optionalArguments)

这消除了歧义:它总是需要至少 3 个参数。

由于某种原因,我什至没有想到的另一个选项是使用命名参数。显然,您的所有代码都必须明确地这样做,但您可以这样做:

Foo(baz: "bar", args: new [] {"a", "b", "c"});

【讨论】:

  • 关于我的虚拟代码示例,您完全正确。不幸的是,在我的真实示例中,从 Foo 的上下文中不知道需要多少个字符串。 (args 用于动态加载查询的参数。)
  • 如果 Foo 处理许多不同的上下文,那么它可能做的太多了?我仍然认为可能需要对方法签名进行更全面的重新设计。在这一点上,也许您可​​以更清楚地了解实际的预期用途
  • @JunWeiLee 它不处理不同的上下文,只是一个动态的。实际用法是 params string[] args 最终成为查询中的参数。哪个查询/参数的数量只有在运行时才知道。
猜你喜欢
  • 1970-01-01
  • 2020-09-19
  • 2022-08-24
  • 2013-06-16
  • 2013-10-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多