对于String.Join(String, String[])这个方法,相信大家都有所了解。唯一不足的就是这个方法只能用于字符串数组,适用场景比较少。我所遇到的场景中,更多的是对List中的某属性进行串联。尚未发现已实现该功能的方法,于是自己实现了该功能。
实现的方式是对IEnumerable进行扩展。

/// 
        /// 将数组合并成字符串,Action<,TextWriter>
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public static void Join(this IEnumerable enumerable,String separator,Action action,TextWriter output)
        {
            Func writeSp =   (x, y) => { x.Write(y); return true; };
            IEnumerator en = enumerable.GetEnumerator();
            if(en.MoveNext())
            {
                do
                {
                    action(en.Current,output);
                } while(en.MoveNext() && writeSp(output, separator));
            }
        }
        /// 
        /// 将数组合并成字符串,Action<>
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public static void Join(this IEnumerable enumerable,String separator,Action action,TextWriter output)
        {
            Func writeSp =   (x, y) => { x.Write(y); return true; };
            IEnumerator 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
                // var columns = dt.Columns.OfType();
                //// 写标题
                //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
                // var columns = dt.Columns.OfType();
                //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 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 10000 2018-11-05 22:13:58:619431 2018-11-05 22:14:01:708661 30862597
Action 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 100000 2018-11-05 22:17:45:756623 2018-11-05 22:18:14:361243 286046197
Action 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 1000000 2018-11-05 22:19:02:489665 2018-11-05 22:24:06:528472 3040388071
Action 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存在歧义,建议使用时换成其他名称。