具有无限参数的C#方法或具有数组或列表的方法?


21

我最近了解到,您可以创建一些带有无限参数的方法,例如:

SomeMethod(params int[] numbers);

但是我的问题是,这和创建接收列表或数组的方法有什么区别?

SomeMethod(int[] numbers);
SomeMethod(List<int> numbers);

也许对性能有影响?我还不完全了解,也看不出您希望以哪种方式使用无限制参数的方法。

在Google上进行快速搜索没有帮助,希望您能对我有所帮助。



除了下面的答案外,params要求参数的类型为数组。如果您需要使用数组以外的其他集合,则改为提供非params参数可能更有意义。
digitlworld

1
@digitlworld:如果您对此主题感兴趣,请参阅github.com/dotnet/csharplang/issues/179进行讨论。
埃里克·利珀特

查看的签名和/或实现Console.Write
3Dave

Answers:


27

与仅创建接收列表或数组的方法有什么区别?

和...之间的不同

void M(params int[] x)

void N(int[] x)

M可以这样称呼:

M(1, 2, 3)

或像这样:

M(new int[] { 1, 2, 3 });

但是N只能以第二种方式调用,而不能以一种方式调用。

也许对性能有影响?

对性能的影响是,无论您M是以第一种方式还是以第二种方式调用,无论是通过哪种方式创建数组,都必须这样做。创建阵列会对性能产生影响,因为这需要时间和内存。请记住,应该根据绩效目标来衡量绩效影响;创建额外阵列的成本不太可能是门控因素,而门控因素是市场成功与失败之间的差异。

我还不完全了解,也看不出您希望以哪种方式使用无限制参数的方法。

这对于调用该方法的代码的作者来说,纯粹是完全方便。它更短,更容易编写

M(1, 2, 3);

而不是写作

M(new int[] { 1, 2, 3 });

它只是节省了呼叫方的一些按键。就这些。

您没有问过几个问题,但也许想知道以下答案:

这个功能叫什么?

允许在调用方传递可变数量的参数的方法称为variadic。参数方法是C#实现可变参数方法的方式。

重载解析如何与可变参数方法一起使用?

遇到重载解决方案问题时,C#将同时考虑“常规”和“扩展”两种形式,并且如果两者均适用,则“常规”形式总是取胜。例如,考虑一下:

void P(params object[] x){}

我们打个电话

P(null);

有两种适用的可能性。以“普通”形式,我们调用P并传递该数组的空引用。以“扩展”形式,我们称为P(new object[] { null })。在这种情况下,以常规形式获胜。如果我们打了电话,P(null, null)则正常形式是不适用的,默认情况下扩展形式会获胜。

挑战:假设我们有var s = new[] { "hello" };一个电话P(s);。描述呼叫现场发生的情况以及原因。您可能会感到惊讶!

挑战:假设我们同时拥有void P(object x){}void P(params object[] x){}。做P(null)什么,为什么?

挑战:假设我们同时拥有void M(string x){}void M(params string[] x){}。做M(null)什么,为什么?这与以前的情况有何不同?


CH2:P(null)主场迎战P((object)null)主场迎战P((object[])null)-我在哪里可以找到这种差异的解释?感觉好像null有一些特殊的类型,它转换为数组而不是对象(P(null)),但是发现转换为字符串或字符串数​​组时模棱两可(M(null))...感觉很奇怪,会期望在两种情况下都模棱两可或选择单参数版本(不是)。但是我想这更多是关于params某种通用性,例如&&C ++模板中的通用引用(),从而更好地匹配(在Ch2中,而不是在Ch3中)。
firda

@firda:您可以在C#规范中找到说明。奇怪的原因是:null可以转换为object,string,object []和string []。因此,对重载分辨率提出的问题是:哪种转换最好?在这种情况下,控制规则是特定的胜于一般的。我们如何分辨哪种类型更具体?规则是:所有长颈鹿都是动物,但并非所有动物都是长颈鹿,因此,长颈鹿比动物更具特异性。全部object[]object,但不是全部objectobject[],因此object[]更具体。
埃里克·利珀特

@firda:现在您知道为什么字符串不明确。这是不正确的,“所有的string[]都是string。” 实际上,NO string[]are string和NO stringare string[],因此string没有比更加具体或更笼统string[],并且当我们要求选择时会出现歧义错误。
埃里克·利珀特

所有object[]这些都是object…… object[]更具体,因此P(null)需要更具体的数组thx,这就是我所缺少的。:这里发现了一些规则docs.microsoft.com/en-us/dotnet/csharp/language-reference/...
firda

5

只是做了一些原型。答案似乎params是简单地传递数组的语法糖。这并不奇怪。我创建了同一方法的两个版本,唯一的区别是“ params”关键字。两者生成的IL相同,只是将a System.ParamArrayAttribute应用于该params版本。

此外,在调用站点生成的IL在使用手动声明new int[]的方法调用方法和仅使用params参数调用方法之间也相同。

因此,答案似乎是“便利”。在性能上似乎没有任何区别。您也可以params改为使用数组来调用函数,因此这也并不令人惊讶。归结为,对于您的方法的使用者来说,使用任意数量的参数(例如someMethod(1, 2, 3))比总是必须首先创建一个集合(例如someMethod(new List<int>() { 1, 2, 3 } ))更容易。


4

无限参数的功能在许多情况下具有以下优点:

  1. 松耦合
  2. 增强的可重用性
  3. 更好的应用程序整体性能

这是一个示例,其中无限制参数选项是一个不错的选择

考虑到需要构建用于发送电子邮件的应用程序。

发送电子邮件的功能必须能够处理“收件人”,“抄送”和“密件抄送”字段的单个或多个值。

如果将参数类型固定为所有字段(收件人,抄送,密件抄送)的数组或列表,则调用函数将被强制处理定义3个数组或列表的所有复杂性,以便调用电子邮件发件人函数。

即使呼叫者只想将电子邮件发送到一个地址,电子邮件发送者功能也将强制呼叫者定义并发送3个不同的数组作为参数。

如果电子邮件发件人功能采用了无限制的参数方法,则调用者功能无需处理所有复杂性。

无限参数方法避免了在不必要的地方创建数组或列表,从而有助于提高应用程序的运行时性能。


2

非性能,样式的角度来看,params当您要发送可选参数列表时,该关键字真的很不错。

就个人而言,我会params在代码类似于

SomeMethod('Option1', 'Option17');

void SomeMethod(params string[] options)
{
    foreach(var option in options)
    {
        switch(option): ...
    }
}

对此的好处是,我可以在各处使用此方法,而不必每次都创建数组或列表。

我会使用,array或者list当我知道我将始终通过此函数传递一组已经组合在一起的数据时,例如

List<User> users = GetUsersFromDB();
SomeMethod(users);

我看到它带来params的灵活性带来的好处。这可能对您的代码影响相对较小,但仍然是不错的工具。


1

调用约定是不同的。例如 ...

public class Test
{
    public void Method1( params int[] nums )
    {
        nums.ToList().ForEach( n => Console.WriteLine(n) );
    }

    public void Method2( List<int> nums )
    {
        nums.ForEach( n  => Console.WriteLine(n) );
    }   
}

void Main()
{   
    var t = new Test();
    t.Method1( 1, 2, 3, 4 );
    t.Method2( new List<int>() { 1, 2, 3, 4 } );
}

在第一种情况下,您可以将尽可能多的整数作为单独的参数传递给方法。在第二个中,您需要实例化一个列表并传递它。

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.