【问题标题】:Functional way to create and populate arrays in C#在 C# 中创建和填充数组的函数式方法
【发布时间】:2020-02-14 14:52:56
【问题描述】:

我怎样才能以比以下命令方式更实用的方式创建和填充数组?

命令式代码

IList<Customer> customers = new List<Customer>();

// my input data is array of strings

string[] ids = {"4", "6"};
string[] names = {"John Doe", "Jane Doe"};

for (var i = 0; i<ids.Length; i++) 
{
    customers.Add(new Customer
    { 
         Id = Int32.Parse(ids[i]),
         Name = names[i],
    });
}

客户类

public class Customer
{
    public int Id { get; set; }

    public string Name { get; set; }
}

【问题讨论】:

  • 也许我理解错了,但期望的结果是什么?
  • 确实,functional way 在这里是什么意思? ZipZip().ToArray() 看起来更“实用”,但它们仍然生成和修改数组 - 由于缓冲区重新分配,可能有很多数组。 Zip() 单独是 functional 方式,因为它产生所需项目的序列而不是序列。该序列可以被其他需要这种序列的函数使用,尤其是 LINQ 函数
  • 顺便说一句 List&lt;T&gt; 不是数组。

标签: c# functional-programming


【解决方案1】:

如果 functional 是指没有循环,则可以使用 LINQ 的 Zip 组合值并生成 IEnmerable&lt;Customer&gt;

var customers=ids.Zip(names,(id,name)=> new Customer
                     { 
                         Id = Int32.Parse(ids[i]);
                         Name = names[i];
                     })
                 .ToArray();
                 //Or .ToList(); if a List<T> is preferable

我说if,因为创建数组仍然需要分配一个数组并对其进行修改。实际上它比预先创建一个数组更多昂贵,因为ToArray() 不知道一个数组有多大创建,因此它可能需要重新分配其内部缓冲区。

更实用的方法是使用由Zip 生成的IEnumerable&lt;Customer&gt;。该客户对象序列可由任何其他 LINQ 运算符或期望 IEnumerable&lt;T&gt; 的自定义方法使用。

性能更高的版本会提前创建数组并从 zip 生成的 IEnumerable 中复制值:

var customers=new Customer[ids.Length];

var values=ids.Zip(names,(id,name)=> new Customer
                                     { 
                                         Id = Int32.Parse(ids[i]);
                                         Name = names[i];
                                     });
var i=0;
foreach(var values)
{
    customers[i++]=v;
}

【讨论】:

  • 我认为这个答案大部分都属于微优化领域。除非您遇到性能问题,并且您的分析器告诉您ToList() / ToArray() 是问题所在,否则请不要进行这种努力。
  • @canton7 相反,询问 functional 是一种微优化——我们在这里试图获得什么。至于重新分配,这是一个众所周知的问题,有很多(如几十个)SO 问题询问为什么 .AddToList 会导致 OOM 异常。事实上,List&lt;T&gt; 是这样实现的,因为 重新分配 比链表快得多。
  • @canton7 此外,Zip 在这里有点功能作弊。它本身使用一个循环。也许OP想要一种递归方法?这实际上会更慢并且需要大量副本
  • 这个问题与性能无关,所以不知道您为什么将这个问题解释为与微优化有关?微优化的整个定义是,您不应该这样做除非它被证明是一个问题。 ToList() 导致 OOM 是一个问题,但对于合理数量的数据,它不会这样做。 .NET 的大部分功能性东西都是使用循环实现的,我认为没有人会争辩说这会使其不起作用
  • 您回答的第一部分很好,但我确实认为直接谈论避免使用ToList() 而不解释这不应该这样做在大多数情况下是误导性的。我想向 OP 说明这一点,以防他们养成坏习惯。
【解决方案2】:

这样的?

customers = ids.Zip(names, (id, name) => new Customer { Id = Int32.Parse(id), Name = name }).ToList();

【讨论】:

    猜你喜欢
    • 2021-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-04
    • 2020-01-11
    • 2012-01-14
    • 2011-01-30
    相关资源
    最近更新 更多