【发布时间】:2013-05-14 20:57:18
【问题描述】:
使用 params 关键字与 List 作为某些 c# 函数的输入的优缺点是什么?
主要是性能注意事项和其他权衡。
【问题讨论】:
标签: c# optimization list params
使用 params 关键字与 List 作为某些 c# 函数的输入的优缺点是什么?
主要是性能注意事项和其他权衡。
【问题讨论】:
标签: c# optimization list params
params 关键字是由 C# 编译器处理的语法糖。在引擎盖下,它实际上在转动
void Foo(params object[] a) { ... }
Foo(1,2,"THREE");
进入
void Foo(object[] a) { ... }
Foo(new object[] { 1, 2, "THREE" })
从性能的角度来看,就像你问的那样,params 调用速度更快,因为创建数组比创建列表要快一点。上面的两个sn-ps没有性能差异。
【讨论】:
Foo() 使用同一个List 被多次调用。
params object[] 将object[] 作为输入而不复制它,因此您现有的List<object> 仍然比传递现有的object[] 具有更多的开销。
我个人在编写函数时使用params,该函数采用另一个程序员(例如String.Format)提供的大量输入,而IEnumerable 则在编写采用数据列表的函数时计算机提供的项目(例如File.Write)。
性能影响可以忽略不计。担心这样一件微不足道的事情的性能正是 Donald Knuth 在著名的“过早优化是万恶之源”中所说的。
也就是说,提问者似乎对它很感兴趣,所以你去吧:
1000 万次迭代的结果:
params took 308 ms
list took 879 ms
从这些结果中,我们可以看到 params 数组的速度快了两倍多。您可以在一秒钟内调用一千万次这两个简单的事实,这意味着您担心它完全是在浪费您的时间。使用最适合您的代码的任何内容。
测试它的代码(使用 VS2008 在发布模式下编译和运行)
class Program
{
const int COUNT = 10000000;
static IEnumerable<string> m_value = null;
static void ParamsMethod(params string[] args)
{ m_value = args; } // do something with it to stop the compiler just optimizing this method away
static void ListMethod(List<string> args)
{ m_value = args; } // do SOMETHING with it to stop the compiler just optimizing this method away
static void Main(string[] args)
{
var s = new Stopwatch();
s.Start();
for (int i = 0; i < COUNT; ++i)
ParamsMethod("a", "b", "c");
Console.WriteLine("params took {0} ms", s.ElapsedMilliseconds);
s.Reset();
s.Start();
for (int i = 0; i < COUNT; ++i)
ListMethod(new List<string> { "a", "b", "c" });
Console.WriteLine("list took {0} ms", s.ElapsedMilliseconds);
}
}
【讨论】:
File.Write,我很难区分程序员和计算机的输入。写API、SDK的时候,怎么知道是程序员还是电脑?
好吧,使用 params 关键字,您可以将参数输入到这样的方法中:
MethodName(1, 2, 3, 4);
但是如果有一个列表,你会这样做:
MethodName(new List<int> {1, 2, 3, 4});
前者的语法比后者更清晰一些。当您只需要传入一个参数时,这很有用:
// params
MethodName(1);
// List
MethodName(new List<int> {1});
【讨论】:
好吧,params 在调用它时允许使用更好的语法,但列表(假设您的意思是IList<>)更灵活,因为不同的类可能实现该接口。传递List<> 仅在您需要对列表执行接口不支持的特定操作时才有意义(例如ToArray())。
【讨论】:
params 关键字允许您动态地将可变数量的参数传递给函数,而不必担心如下编译器错误:
public string PrefixFormatString(string p, string s, params object[] par)
{
return p + string.Format(s, par);
}
...
PrefixFormatString("COM", "Output Error #{0} - Error = {1}", errNum, errStr);
如果你传递一个列表,你必须先构造列表,然后才能传递它:
public string PrefixFormatString(string p, string s, List<object> par)
{
return p + string.Format(s, par.ToArray());
}
...
List<object> l = new List<object>(new object[] { errNum, errStr });
PrefixFormatString("COM", "Output Error #{0} - Error = {1}", l);
而且它往往会隐藏函数期望的数据类型的含义。
请注意,这与传递简单的数组变量非常相似。唯一的区别是编译器会为你将参数固定到一个数组中......我不是 100% 确定,但我认为技术差异只是语法糖——在任何一种情况下,你都在传递一个数组输入参数是。
【讨论】:
params 是一种语言结构,用于采用可变数量参数的函数。它类似于 C 省略说明符 - 即 printf(char* fmt, ...)。该语言支持这种操作,不妨使用它,尤其是如果它使代码更易于阅读。
【讨论】:
就个人而言,我会跳过参数。我被它咬过一两次。如何?让我解释一下。
你用这个签名写了一个公共方法:
public static void LogInUser(string username, string password, params string[] options)
您对其进行测试,它可以工作,它已完成...并且另一个程序集/应用程序正在调用您的函数。
现在,一个月后您想更改签名以添加用户角色:
public static void LogInUser(string username, string password, string role, params string[] options)
哦,任何调用你的方法的东西都发生了变化。
LogInUser("z@z.com", "zz", "Admin", "rememberMe", "800x600");
【讨论】:
Credentials,您就不会遇到这个问题。
我可以看到两者之间的主要区别是传递给方法的参数数量是在编译时使用params设置的,而List<T>则取决于运行时传入的列表。
修复在编译时必须调用方法的参数数量是利还是弊,完全取决于您的设计和意图。根据您希望实现的目标,任何一种都可能是有益的。
Params 有助于提高可读性,并且与您将在 C# 中使用的可选参数一样接近。如果我需要在任何时候使用未知数量的参数,我只会亲自使用List<T> 实现。
编辑:刚刚发现您对性能问题的编辑。关于这个话题我不确定,但如果您可能期望使用List<T> 有大量“参数”,而params 有一个理智上限,因为它们必须被编码。
【讨论】:
程序员调用你的方法的性能有时可以通过使用 params 关键字来提高。
(鉴于程序员的成本比计算机高得多,你为什么要考虑任何其他类型的性能。)
【讨论】: