【问题标题】:C#: params keyword vs. listC#:参数关键字与列表
【发布时间】:2013-05-14 20:57:18
【问题描述】:

使用 params 关键字与 List 作为某些 c# 函数的输入的优缺点是什么?

主要是性能注意事项和其他权衡。

【问题讨论】:

    标签: c# optimization list params


    【解决方案1】:

    params 关键字是由 C# 编译器处理的语法糖。在引擎盖下,它实际上在转动

    void Foo(params object[] a) { ... }
    Foo(1,2,"THREE");
    

    进入

    void Foo(object[] a) { ... }
    Foo(new object[] { 1, 2, "THREE" })
    

    性能的角度来看,就像你问的那样,params 调用速度更快,因为创建数组比创建列表要快一点。上面的两个sn-ps没有性能差异。

    【讨论】:

    • 如果我们要传递一个现有的 List 引用,那么使用 params 会更慢,因为为数组分配内存会产生开销。
    • @Mert:但您还必须为列表分配内存您作为参考传递...
    • @Mert 它是否已经存在并没有什么区别,除非Foo() 使用同一个List 被多次调用。
    • @Dan,不,如果列表已经存在,您只需将其作为引用传递给将列表作为参数的方法。如果将列表传递给以 params 作为参数的方法,则需要分配空间并将该列表复制到一个数组中并将该数组用作参数。所以它有所作为。
    • @Mert, params object[]object[] 作为输入而不复制它,因此您现有的List<object> 仍然比传递现有的object[] 具有更多的开销。
    【解决方案2】:

    我个人在编写函数时使用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);
        }
    }
    

    【讨论】:

    • 那是因为创建列表的时间很慢。如果您调用一个采用 IEnumerable 的方法,例如使用 string[] 而不是 List ,则性能几乎等同于 params 方法。
    • 当然可以,但没关系!
    • 您可能正在做一些事情,但请您详细说明这一点:计算机提供的数据项(例如 File.Write)。没有File.Write,我很难区分程序员和计算机的输入。写API、SDK的时候,怎么知道是程序员还是电脑?
    • @CodingYoshi 我试图指的是例如将一个函数的输出输入另一个函数(由计算机提供)与开发人员自己输入列表(由程序员提供)跨度>
    【解决方案3】:

    好吧,使用 params 关键字,您可以将参数输入到这样的方法中:

    MethodName(1, 2, 3, 4);
    

    但是如果有一个列表,你会这样做:

    MethodName(new List<int> {1, 2, 3, 4});
    

    前者的语法比后者更清晰一些。当您只需要传入一个参数时,这很有用:

    // params
    MethodName(1);
    
    // List
    MethodName(new List<int> {1});
    

    【讨论】:

    • 看...老实说,我认为这是对所提问题的正确答案。 @YaronNaveh - 性能非常接近,可以忽略不计。在这种情况下,性能不是问题,而是设计 - 重用、维护、支持......
    【解决方案4】:

    好吧,params 在调用它时允许使用更好的语法,但列表(假设您的意思是IList&lt;&gt;)更灵活,因为不同的类可能实现该接口。传递List&lt;&gt; 仅在您需要对列表执行接口不支持的特定操作时才有意义(例如ToArray())。

    【讨论】:

    • +1 表示 IList,或任何其他相关接口,具体取决于您的收藏需要做什么。
    • 如果你只需要枚举项,那么IEnumerable是比IList更好的选择,允许你传入一堆不同的集合实现以及LINQ查询结果,等
    【解决方案5】:

    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% 确定,但我认为技术差异只是语法糖——在任何一种情况下,你都在传递一个数组输入参数是。

    【讨论】:

    • 这不是您真正应该担心的性能问题类型 - 我敢肯定,无论您考虑什么,都会出现更大的性能占用问题。性能几乎相同,因为(我认为)它主要只是一种编译器语法,与函数本身在 IL 中被调用的方式无关。
    【解决方案6】:

    params 是一种语言结构,用于采用可变数量参数的函数。它类似于 C 省略说明符 - 即 printf(char* fmt, ...)。该语言支持这种操作,不妨使用它,尤其是如果它使代码更易于阅读。

    【讨论】:

      【解决方案7】:

      就个人而言,我会跳过参数。我被它咬过一两次。如何?让我解释一下。

      你用这个签名写了一个公共方法:

      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");
      

      【讨论】:

      • 此外,参数“闻起来”就像它经常用于 Interop。
      • 好吧,如果您将定义的成员分组到一个对象中,例如Credentials,您就不会遇到这个问题。
      • Dynami,你是绝对正确的。错误的代码......参数的错误使用。
      【解决方案8】:

      我可以看到两者之间的主要区别是传递给方法的参数数量是在编译时使用params设置的,而List&lt;T&gt;则取决于运行时传入的列表。

      修复在编译时必须调用方法的参数数量是利还是弊,完全取决于您的设计和意图。根据您希望实现的目标,任何一种都可能是有益的。

      Params 有助于提高可读性,并且与您将在 C# 中使用的可选参数一样接近。如果我需要在任何时候使用未知数量的参数,我只会亲自使用List&lt;T&gt; 实现。

      编辑:刚刚发现您对性能问题的编辑。关于这个话题我不确定,但如果您可能期望使用List&lt;T&gt; 有大量“参数”,而params 有一个理智上限,因为它们必须被编码。

      【讨论】:

        【解决方案9】:

        程序员调用你的方法的性能有时可以通过使用 params 关键字来提高。

        (鉴于程序员的成本比计算机高得多,你为什么要考虑任何其他类型的性能。)

        【讨论】:

          猜你喜欢
          • 2020-09-11
          • 1970-01-01
          • 2010-11-27
          • 2023-03-31
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多