为什么要使用params关键字?


336

我知道这是一个基本问题,但我找不到答案。

为什么要使用它?如果编写使用该函数的方法或方法,则将其删除时,代码仍将完美运行,没有代码的情况下将100%正常运行。例如:

有参数:

static public int addTwoEach(params int[] args)
{
    int sum = 0;
    foreach (var item in args)
        sum += item + 2;
    return sum;
}

没有参数:

static public int addTwoEach(int[] args)
{
    int sum = 0;
    foreach (var item in args)
       sum += item + 2;
    return sum;
}

62
该方法本身的代码仍然可以正常使用... 调用代码可能不会...
Jon Skeet

7
params关键字表示可以传递给方法或不传递给方法的可选参数。没有参数关键字的数组意味着您必须将数组参数传递给方法。
艾莱娜·恩塔里亚

Python语言实现了相同的概念是那么的甜美有星号(*)作为前缀提到的参数在这里
RBT

Answers:


485

随着params您可以这样调用您的方法:

addTwoEach(1, 2, 3, 4, 5);

没有params,您不能。

此外,在两种情况下,都可以使用数组作为参数来调用该方法:

addTwoEach(new int[] { 1, 2, 3, 4, 5 });

也就是说,params允许您在调用方法时使用快捷方式。

无关的,您可以大大缩短您的方法:

public static int addTwoEach(params int[] args)
{
    return args.Sum() + 2 * args.Length;
}

17
@Ken:您可能需要导入System.Linq名称空间:)
Ranhiru Jude Cooray 2011年

49
或返回args.Sum(i => i + 2);
bornfromanegg

2
但是,委托的总和确实增加了已编译代码的复杂性,这可能会降低性能。在这种特定情况下,这并不重要,因为它不会导致任何关闭,但是值得知道编译器为做出最佳选择所做的实际工作。
艾伦·克拉克·科普兰(Allen Clark Copeland Jr)2015年

2
您也可以使用 return args.Select(x => x + 2).Sum();
bbvg

1
当您添加params锁定自己出添加额外的方法参数,而不破坏你的来电或方法的分辨率。
杨先生

93

使用params允许您不带参数调用函数。没有params

static public int addTwoEach(int[] args)
{
    int sum = 0;

    foreach (var item in args)
    {
        sum += item + 2;
    }

    return sum;
}

addtwoEach(); // throws an error

params

static public int addTwoEach(params int[] args)
{
    int sum = 0;

    foreach (var item in args)
    {
        sum += item + 2;
    }

    return sum;
}

addtwoEach(); // returns 0

通常,当参数数量可以从0变为无穷大时,可以使用params;当参数数量可以从1变为无穷大时,可以使用数组。


13
实际上,数组可以为空。new int[0]。希望这可以帮助!:)
vidstige 2013年

22

它允许您根据需要在调用中添加尽可能多的基本类型参​​数。

addTwoEach(10, 2, 4, 6)

而对于第二种形式,您必须使用数组作为参数

addTwoEach(new int[] {10,2,4,6})

17

params关键字的一种危险是,如果对方法的调用进行编码之后,

  1. 有人不小心/有意从方法签名中删除了一个/多个必需的参数,并且
  2. 在签名更改之前紧接参数之前的一个/多个必需参数params与该params参数类型兼容,

这些调用将继续使用先前用于必需参数的一个/多个表达式进行编译,这些表达式被视为可选params参数。我只是遇到了最坏的情况:params参数是Type object[]

这是值得注意的,因为开发人员习惯于编译器以更为常见的场景来拍拍手,其中从具有所有必需参数的方法中删除参数(因为预期的参数数量会发生变化)。

对我来说,这不值得捷径。 (Type)[]无需params覆盖即可将0设置为无穷大#个参数。最糟糕的情况是,您必须, new (Type) [] {}在不适用的地方添加“ 通话”。

顺便说一句,恕我直言,最安全(也是最易读的做法)是:

  1. 通过命名参数传递(我们现在可以做,甚至在C#〜2个几十年后,我们可以在VB; P)(这是因为:

    1.1。这是唯一的方法,可以确保防止在对参数进行顺序编码,对Compatible-Type和/或计数进行编码后更改了传递给Parameters的意外值之后,

    1.2。它减少了参数含义更改后的机会,因为反映新含义的可能的新标识符名称就在传递给它的值的旁边,

    1.3。它避免了必须计算逗号并从Call to Signature来回跳转,以查看为什么参数传递了什么Expression,以及

    1.3.1。顺便说一句,仅仅因为这个原因应该是充足的(以避免频繁出错违反了DRY原则只是为了方面阅读的代码更何况还修改),但这个原因可能是成倍如果有一个更重要的/更多传递的表达式本身包含逗号,即多维数组引用或多参数函数调用。在这种情况下,您甚至无法使用(即使可能,每个方法调用的每个参数仍要添加一个额外的步骤)在编辑器中的“选择”功能中查找所有事件以自动进行逗号计数。

    1.4。如果必须使用(params或不使用)可选参数,则它允许您搜索通过了特定可选参数的呼叫(因此,很可能不是或至少有可能不是默认值),

(注意:原因1.2和1.3可以缓解并减少出错的机会,即使在对初始Call进行编码时,更不用说在必须读取和/或更改Call的情况下了。)

  1. 这样做一个参数-每行-以获得更好的可读性(因为:

    2.1。它不那么混乱,并且

    2.2。它避免了向右和向左滚动(并且必须这样做-因为大多数凡人无法读取多行的左侧部分,因此请向右滚动并读取右侧部分)。

    2.3。它与我们已经为赋值语句演变成的“最佳实践”相一致,因为传递的每个参数本质上都是赋值语句(分配值或对局部变量的引用)。就像那些遵循最新编码风格的“最佳实践”的人不会梦想每行编写多个赋值语句一样,我们可能不应该(而且“最佳实践”不会赶上我的“天才”; P )在传递参数时执行此操作。

注意事项

  1. 在以下情况下,传递名称与“参数”相对应的变量无济于事:

    1.1。您传入的是文字常量(即简单的0/1,false / true或null,即使“最佳实践”也可能不需要您使用具名常量,并且无法从方法名称中轻松推断出它们的用途) ),

    1.2。该方法的级别比调用者低得多/更为通用,因此您不希望/可以将变量命名为与参数相同/相似(反之亦然),或者

    1.3。您正在重新排序/替换签名中的参数,这可能导致先前的调用仍在编译中,因为类型恰好仍然兼容。

  2. 拥有像VS这样的自动换行功能并不能消除我上面给出的8个理由中的一个(#2.2)。在VS 2015之前,它没有自动缩进(!?!实际上是MS?!?),这会增加原因#2.1的严重性。

VS应该有一个选项可以生成带有命名参数的方法调用代码段(每行一个; P),以及一个需要命名参数的编译器选项(概念上与VB中的Option Explicit相似,顺便说一句,这个需求曾被认为是正确的)同样令人发指,但现在“最佳做法” 确实要求这样做)。实际上,“回到我的日”;),在我职业生涯的短短1991年,甚至在我使用(或什至没有看到)一种带有“命名参数”的语言之前,我就遇到过反sheeple /“只是可以,但不意味着你应该” /不要盲目地“切开烤肉的末端”感觉来模拟它(使用内嵌注释),而无需任何人这样做。不必使用命名参数(以及其他保存“'precious'”的语法)源代码击键)是当大多数这些语法的开始打卡时代的遗物。有用于与现代的硬件和IDE的和更复杂的软件没有任何借口在那里可读性是很多,很多,MUCH更重要。“代码被读取的次数要多于被写入的次数”。只要您不复制非自动更新的代码,当有人(甚至您自己)以后尝试阅读它时,保存的每个击键都可能成倍增加成本。


2
我不明白 您为什么不能仅仅强制至少有一个?即使没有参数,也没有什么可以阻止您传递nullnew object[0]作为参数。
Casey 2015年

在可选参数之前没有必需参数可能太危险了(以防在对调用进行编码后删除了其中一个或多个所需参数)。这可能就是为什么我在任何有关可选参数的文档的示例代码中的可选参数之前从未见过必需参数的原因。顺便说一句,恕我直言,最安全(也是最易读的做法)是通过命名参数传递(即使在C#中,我们现在也可以在VB中使用它约二十年)。VS应该有一个选项,可以生成带有命名参数的方法调用代码片段(并且每行这样做1个参数)。
汤姆

我不太确定你的意思。拥有必需参数和可选参数的唯一方法是先指定所有必需参数。
凯西

1
例如 我声明myMethodvoid myMethod(int requiredInt, params int[] optionalInts)。I /别人代码中的一个/多个电话,即myMethod(1)myMethod(1, 21)myMethod(1, 21, 22)。我变myMethodvoid myMethod(params int[] optionalInts)。即使显然不打算将它们的第一个参数(“ 1”)作为optionalInts参数的第一个元素传递,所有这些调用仍将正确编译。
汤姆,5:

哦。好吧,好的,在这种情况下可能是不明智的。如果您需要一个字符串和0对多整数或任何其他值,我认为没有任何理由避免使用它。
凯西

10

无需创建重载方法,只需使用一个带有params的方法,如下所示

// Call params method with one to four integer constant parameters.
//
int sum0 = addTwoEach();
int sum1 = addTwoEach(1);
int sum2 = addTwoEach(1, 2);
int sum3 = addTwoEach(3, 3, 3);
int sum4 = addTwoEach(2, 2, 2, 2);

感谢您的输入,但是我认为这些重载根本不是解决方案,因为params无论是否通过,我们都只会传递一个集合类型来覆盖任何数量的集合。
2014年

您在某种程度上是对的,但使它酷的是没有输入参数的重载,例如int sum1 = addTwoEach();。
electricalbah

5

params 还允许您使用单个参数调用该方法。

private static int Foo(params int[] args) {
    int retVal = 0;
    Array.ForEach(args, (i) => retVal += i);
    return retVal;
}

Foo(1);代替Foo(new int[] { 1 });。在需要传递单个值而不是整个数组的情况下,对于速记很有用。它在方法中仍然以相同的方式处理,但是为调用此方法提供了一些便利。


5

添加params关键字本身表明您可以在调用该方法的同时传递多个参数,而无需使用该参数是不可能的。更具体:

static public int addTwoEach(params int[] args)
{
    int sum = 0;

    foreach (var item in args)
    {
        sum += item + 2;
    }

    return sum;
}

当您调用上述方法时,可以通过以下任何一种方法来调用它:

  1. addTwoEach()
  2. addTwoEach(1)
  3. addTwoEach(new int[]{ 1, 2, 3, 4 })

但是,当您删除params关键字时,只有上述方法中的第三种方法可以正常工作。对于第一种和第二种情况,您将得到一个错误。


0

还需要强调一件事。最好使用params它,因为它对性能更好。当您使用自params变量调用方法并没有传递给它时:

public void ExampleMethod(params string[] args)
{
// do some stuff
}

呼叫:

ExampleMethod();

然后,新版本的.Net Framework会执行此操作(来自.Net Framework 4.6):

ExampleMethod(Array.Empty<string>());

Array.Empty对象可以在以后由框架重用,因此无需进行冗余分配。当您这样调用此方法时,将发生这些分配:

 ExampleMethod(new string[] {});


0

另一个例子

public IEnumerable<string> Tokenize(params string[] words)
{
  ...
}

var items = Tokenize(product.Name, product.FullName, product.Xyz)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.