对于String.Join(String, String[])这个方法,相信大家都有所了解。唯一不足的就是这个方法只能用于字符串数组,适用场景比较少。我所遇到的场景中,更多的是对List<T>中的某属性进行串联。尚未发现已实现该功能的方法,于是自己实现了该功能。
实现的方式是对IEnumerable<T>进行扩展。
/// <summary>
/// 将数组合并成字符串,Action<<typeparamref name="T"/>,TextWriter>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="enumerable"></param>
/// <param name="separator"></param>
/// <param name="action"></param>
/// <param name="output"></param>
public static void Join<T>(this IEnumerable<T> enumerable,String separator,Action<T,TextWriter> action,TextWriter output)
{
Func<TextWriter, String,Boolean> writeSp = (x, y) => { x.Write(y); return true; };
IEnumerator<T> en = enumerable.GetEnumerator();
if(en.MoveNext())
{
do
{
action(en.Current,output);
} while(en.MoveNext() && writeSp(output, separator));
}
}
/// <summary>
/// 将数组合并成字符串,Action<<typeparamref name="T"/>>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="enumerable"></param>
/// <param name="separator"></param>
/// <param name="action"></param>
/// <param name="output"></param>
public static void Join<T>(this IEnumerable<T> enumerable,String separator,Action<T> action,TextWriter output)
{
Func<TextWriter, String,Boolean> writeSp = (x, y) => { x.Write(y); return true; };
IEnumerator<T> en = enumerable.GetEnumerator();
if(en.MoveNext())
{
do
{
action(en.Current);
} while(en.MoveNext() && writeSp(output, separator));
}
}
下面通过实现“DataTable转CSV”来对这两个方法和嵌套For循环进行对比。
在这个Demo里,DataTable有3列100行数据。对3种实现方式分别进行10000、100000和1000000循环。
Demo:
[TestMethod]
public void TestMethod5()
{
DataTable dt = new DataTable("1234");
dt.Columns.Add("A");
dt.Columns.Add("B");
dt.Columns.Add("C");
for(int i = 0; i < 100; i++)
{
dt.Rows.Add("A" + i, "B" + i, "C" + i);
}
StringBuilder sb;//= new StringBuilder();
StringWriter sw;// = new StringWriter(sb);
String rowSeparator = "\n";
String columnSeparator = ",";
//Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffffff"));
DateTime time1 = DateTime.Now;
Int32 count = 1000000;
for(int c = 0; c < count; c++)
{
sb = new StringBuilder();
sw = new StringWriter(sb);
#region Action<T,TextWriter>
// var columns = dt.Columns.OfType<DataColumn>();
//// 写标题
//columns.Join(columnSeparator, (x) => x.ColumnName, sw);
//sw.Write(rowSeparator);
//// 写数据
//dt.Select().Join(rowSeparator, (x, x1) => columns.Join(columnSeparator, (y, y1) => { y1.Write('"'); y1.Write(x[y]); y1.Write('"'); }, x1), sw);
#endregion
#region Action<T>
// var columns = dt.Columns.OfType<DataColumn>();
//columns.Join(columnSeparator, (x) => sw.Write(x.ColumnName), sw);
//sw.Write(rowSeparator);
//dt.Select().Join(rowSeparator, (x) => columns.Join(columnSeparator, (y) => { sw.Write('"'); sw.Write(x[y]); sw.Write('"'); }, sw), sw);
#endregion
#region 嵌套for
Func<TextWriter, String, Boolean> func = (x, y) => { x.Write(y); return true; };
sb = new StringBuilder();
for(int i = 0; i < dt.Columns.Count && func(sw, columnSeparator); i++)
{
sw.Write(dt.Columns[i].ColumnName);
}
sw.Write(rowSeparator);
for(int i = 0; i < dt.Rows.Count && func(sw, rowSeparator); i++)
{
for(int j = 0; j < dt.Columns.Count && func(sw, columnSeparator); j++)
{
sw.Write('"');
sw.Write(dt.Rows[i][j]);
sw.Write('"');
}
}
#endregion
sw.Flush();
sw.Close();
//Console.WriteLine(sb.ToString());
}
//Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffffff"));
DateTime time2 = DateTime.Now;
Console.WriteLine($"{time1.ToString("yyyy-MM-dd HH:mm:ss:ffffff")}\t{time2.ToString("yyyy-MM-dd HH:mm:ss:ffffff")}\t{time2.Ticks - time1.Ticks}");
}
测试结果:
| 类别 |
循环次数 |
开始时间 |
结束时间 |
Ticks差 |
| Action<T,TextWriter> |
10000 |
2018-11-05 22:13:58:619431 |
2018-11-05 22:14:01:708661 |
30862597 |
| Action<T> |
10000 |
2018-11-05 22:30:01:316414 |
2018-11-05 22:30:04:540565 |
32241503 |
| 嵌套for |
10000 |
2018-11-05 22:48:20:351136 |
2018-11-05 22:48:23:832171 |
34810345 |
| Action<T,TextWriter> |
100000 |
2018-11-05 22:17:45:756623 |
2018-11-05 22:18:14:361243 |
286046197 |
| Action<T> |
100000 |
2018-11-05 22:30:38:917299 |
2018-11-05 22:31:08:680268 |
297629687 |
| 嵌套for |
100000 |
2018-11-05 22:50:11:315021 |
2018-11-05 22:50:45:257604 |
339425834 |
| Action<T,TextWriter> |
1000000 |
2018-11-05 22:19:02:489665 |
2018-11-05 22:24:06:528472 |
3040388071 |
| Action<T> |
1000000 |
2018-11-05 22:32:09:871650 |
2018-11-05 22:37:05:125522 |
2952538718 |
| 嵌套for |
1000000 |
2018-11-05 22:51:18:319466 |
2018-11-05 22:56:46:534431 |
3282149643 |
从结果中可以看出,性能差距并不大。当然,由于嵌套For循环里使用索引来寻值,所以可能存在误差。
如果有错误或者更好的方法,欢迎指正。
题外话,方法命名为Join,可能与Linq的Join存在歧义,建议使用时换成其他名称。