对于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存在歧义,建议使用时换成其他名称。