将对象集合连接成以逗号分隔的字符串

30

我们的代码中有许多对象集合,需要从中创建逗号分隔的列表。集合的类型各不相同:它可能是我们需要某一列的 DataTable,或者一个 List<Customer> 等。

现在我们遍历这个集合并使用字符串连接,例如:

string text = "";
string separator = "";
foreach (DataRow row in table.Rows)
{
    text += separator + row["title"];
    separator = ", ";
}

有没有更好的模式?理想情况下,我希望我们能够通过只发送一个函数来重复使用该方法,以获取每个对象的正确字段/属性/列。

11个回答

99
string.Join(", ", Array.ConvertAll(somelist.ToArray(), i => i.ToString()))

1
如果将 someList 声明为 IList,它就缺少 ToArray() 方法。 - Cristian Diaconescu
4
使用 string.Join(", ", somelist.Select(t => t.ToString()).ToArray()) 将一个列表 somelist 中的元素转换为字符串,用逗号分隔每个元素,并将它们连接成一个字符串。 - cederlof
@JoshStodola:什么?你试图使用JVM进行编译吗?;p 从1.0版本开始就有了。 - leppie
1
@leppie 错了。它不在1.1中,也不在2.0中,还不在3.5中。我想你是指ConvertAll - Josh Stodola
在拥有一个泛型 List 类型为 StringBuilder 的情况下,如果您想要使用 Lambda 表达式,可以考虑另一个选项,即 .Net 3.5 及以上版本:codestring.Join(",", (from s in queries select s.ToString()).ToArray());code - Michael
显示剩余4条评论

12
static string ToCsv<T>(IEnumerable<T> things, Func<T, string> toStringMethod)
{
    StringBuilder sb = new StringBuilder();

    foreach (T thing in things)
        sb.Append(toStringMethod(thing)).Append(',');

    return sb.ToString(0, sb.Length - 1); //remove trailing ,
}

使用方法如下:

DataTable dt = ...; //datatable with some data
Console.WriteLine(ToCsv(dt.Rows, row => row["ColName"]));

或者:

List<Customer> customers = ...; //assume Customer has a Name property
Console.WriteLine(ToCsv(customers, c => c.Name));

虽然我手头没有编译器,但从理论上讲它应该是可行的。众所周知,理论和实践是相同的,但实际操作中并非如此。


这正是我在寻找的,谢谢! 理论和实践在这里并不相同,你需要为该方法提供两个重载(一个泛型和一个非泛型),就像Hosam Aly的答案中一样。尽管你的回答更易读。 - Helen Toomik
这是最好的答案,特别是如果你按照BigBlondViking的改进将其变成扩展方法。更加简洁,适用于复杂对象。 - smdrager

11

我发现使用 string.Join 和 Lambda 表达式的 Select<Func<>> 可以帮助编写更少的代码。

List<string> fruits = new List<string>();
fruits.Add("Mango");
fruits.Add("Banana");
fruits.Add("Papaya");

string commaSepFruits = string.Join(",", fruits.Select(f => "'" + f + "'"));
Console.WriteLine(commaSepFruits);

List<int> ids = new List<int>();
ids.Add(1001);
ids.Add(1002);
ids.Add(1003);

string commaSepIds = string.Join(",", ids);
Console.WriteLine(commaSepIds);

List<Customer> customers = new List<Customer>();
customers.Add(new Customer { Id = 10001, Name = "John" });
customers.Add(new Customer { Id = 10002, Name = "Robert" });
customers.Add(new Customer { Id = 10002, Name = "Ryan" });

string commaSepCustIds = string.Join(", ", customers.Select(cust => cust.Id));
string commaSepCustNames = string.Join(", ", customers.Select(cust => "'" + cust.Name + "'"));

Console.WriteLine(commaSepCustIds);
Console.WriteLine(commaSepCustNames);

Console.ReadLine();

10
// using System.Collections;
// using System.Collections.Generic;
// using System.Linq

public delegate string Indexer<T>(T obj);

public static string concatenate<T>(IEnumerable<T> collection, Indexer<T> indexer, char separator)
{
    StringBuilder sb = new StringBuilder();
    foreach (T t in collection) sb.Append(indexer(t)).Append(separator);
    return sb.Remove(sb.Length - 1, 1).ToString();
}

// version for non-generic collections
public static string concatenate<T>(IEnumerable collection, Indexer<T> indexer, char separator)
{
    StringBuilder sb = new StringBuilder();
    foreach (object t in collection) sb.Append(indexer((T)t)).Append(separator);
    return sb.Remove(sb.Length - 1, 1).ToString();
}

// example 1: simple int list
string getAllInts(IEnumerable<int> listOfInts)
{
    return concatenate<int>(listOfInts, Convert.ToString, ',');
}

// example 2: DataTable.Rows
string getTitle(DataRow row) { return row["title"].ToString(); }
string getAllTitles(DataTable table)
{
    return concatenate<DataRow>(table.Rows, getTitle, '\n');
}

// example 3: DataTable.Rows without Indexer function
string getAllTitles(DataTable table)
{
    return concatenate<DataRow>(table.Rows, r => r["title"].ToString(), '\n');
}

1
这正是我想要实现的,谢谢!虽然我发现像Matt的解决方案中使用lambda表达式更易读。 - Helen Toomik
1
我同意lambda表达式更易读,但到目前为止我只使用过.NET 2.0。希望能尽快学习它们。 - Hosam Aly
1
你可能想把分隔符从字符改为字符串,但别忘了将 "sb.Remove" 调用更改为 "sb.Remove(sb.Length - separator.Length, separator.Length)"。 - Hosam Aly

7
在.NET 4中,您只需执行string.Join(", ", table.Rows.Select(r => r["title"]))就可以了。

6
您可以编写一个函数,将IEnumerable<string>转换为逗号分隔的字符串:
public string Concat(IEnumerable<string> stringList)
{
    StringBuilder textBuilder = new StringBuilder();
    string separator = String.Empty;
    foreach(string item in stringList)
    {
        textBuilder.Append(separator);
        textBuilder.Append(item);
        separator = ", ";
    }
    return textBuilder.ToString();
}

您可以使用LINQ查询您的集合/数据集等来提供字符串列表。

虽然Join可能是我通常会选择的方式,但我总是喜欢看到在第一次迭代中使用空分隔符并在后续使用所需的分隔符的这种惯用法。它看起来比始终添加分隔符然后再将其剥离掉更加简洁。 - wageoghe
刚刚注意到我把分隔符和项目添加的顺序搞错了...太傻了!已经修复 :-) - Mendelt

2

我喜欢这篇文章中Matt Howells的回答:

我不得不将其转化为一个扩展:

public static string ToCsv<T>(this IEnumerable<T> things, Func<T, string> toStringMethod)

用法(我正在获取所有电子邮件,将它们转换为用于电子邮件的逗号分隔值(CSV)字符串)

var list = Session.Find("from User u where u.IsActive = true").Cast<User>();

return list.ToCsv(i => i.Email);

这正是我所做的。你也应该使用 sb.ToString().TrimEnd(','); 来删除尾随的逗号。这样函数更清晰,如果你传递的可枚举对象中没有元素,它的函数会抛出一个不能小于零长度的错误,因为 0 - 1 = -1。 - smdrager

2

对于集合,您也可以使用此方法,例如:

string.Join(", ", contactsCollection.Select(i => i.FirstName));

你可以选择任何你想要分离的属性。

2
作为一个旁注:我会做的第一项修改是使用StringBuilder类而不仅仅是一个字符串——这将为您节省资源。

1
string strTest = "1,2,4,6";
string[] Nums = strTest.Split(',');
Console.Write(Nums.Aggregate<string>((first, second) => first + "," + second));
//OUTPUT:
//1,2,4,6

聚合返回与输入值相同的数据类型,即我所能看到的,我无法将List<int>聚合成字符串。 - Helen Toomik
也许是 table.Rows.Select(r => r["title"].ToString()).Aggregate((a, b) => a + "," + b) 吧? - Dan Berindei

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接