Converting a list to a CSV string
If your items contain a comma, carriage return or other special CSV character, you must delimit it with quotation marks.

namespace CsvStuff
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    internal static class CsvConstants
    {
        public static char[] TrimEnd { get; } = { ' ', ',' };

        public static char[] CsvChars { get; } = { ',', '"', ' ', '\n', '\r' };
    }

    public abstract class CsvBase&lt;T&gt;
    {
        private readonly IEnumerable&lt;T&gt; values;

        private readonly Func&lt;T, object&gt; getItem;

        protected CsvBase(IEnumerable&lt;T&gt; values, Func&lt;T, object&gt; getItem)
        {
            this.values = values;
            this.getItem = getItem;
        }

        public override string ToString()
        {
            var builder = new StringBuilder();

            foreach (var item in
                from element in this.values.Select(this.getItem)
                where element != null
                select element.ToString())
            {
                this.Build(builder, item).Append(", ");
            }

            return builder.ToString().TrimEnd(CsvConstants.TrimEnd);
        }

        protected abstract StringBuilder Build(StringBuilder builder, string item);
    }

    public class CsvBare&lt;T&gt; : CsvBase&lt;T&gt;
    {
        public CsvBare(IEnumerable&lt;T&gt; values, Func&lt;T, object&gt; getItem) : base(values, getItem)
        {
        }

        protected override StringBuilder Build(StringBuilder builder, string item)
        {
            return builder.Append(item);
        }
    }

    public sealed class CsvTrimBare&lt;T&gt; : CsvBare&lt;T&gt;
    {
        public CsvTrimBare(IEnumerable&lt;T&gt; values, Func&lt;T, object&gt; getItem) : base(values, getItem)
        {
        }

        protected override StringBuilder Build(StringBuilder builder, string item)
        {
            return base.Build(builder, item.Trim());
        }
    }

    public class CsvRfc4180&lt;T&gt; : CsvBase&lt;T&gt;
    {
        public CsvRfc4180(IEnumerable&lt;T&gt; values, Func&lt;T, object&gt; getItem) : base(values, getItem)
        {
        }

        protected override StringBuilder Build(StringBuilder builder, string item)
        {
            item = item.Replace("\"", "\"\"");
            return item.IndexOfAny(CsvConstants.CsvChars) &gt;= 0
                ? builder.Append("\"").Append(item).Append("\"")
                : builder.Append(item);
        }
    }

    public sealed class CsvTrimRfc4180&lt;T&gt; : CsvRfc4180&lt;T&gt;
    {
        public CsvTrimRfc4180(IEnumerable&lt;T&gt; values, Func&lt;T, object&gt; getItem) : base(values, getItem)
        {
        }

        protected override StringBuilder Build(StringBuilder builder, string item)
        {
            return base.Build(builder, item.Trim());
        }
    }

    public class CsvAlwaysQuote&lt;T&gt; : CsvBare&lt;T&gt;
    {
        public CsvAlwaysQuote(IEnumerable&lt;T&gt; values, Func&lt;T, object&gt; getItem) : base(values, getItem)
        {
        }

        protected override StringBuilder Build(StringBuilder builder, string item)
        {
            return builder.Append("\"").Append(item.Replace("\"", "\"\"")).Append("\"");
        }
    }

    public sealed class CsvTrimAlwaysQuote&lt;T&gt; : CsvAlwaysQuote&lt;T&gt;
    {
        public CsvTrimAlwaysQuote(IEnumerable&lt;T&gt; values, Func&lt;T, object&gt; getItem) : base(values, getItem)
        {
        }

        protected override StringBuilder Build(StringBuilder builder, string item)
        {
            return base.Build(builder, item.Trim());
        }
    }

    public static class CsvExtensions
    {
        public static string ToCsv&lt;T&gt;(this IEnumerable&lt;T&gt; source, Func&lt;T, object&gt; getItem, Type csvProcessorType)
        {
            if ((source == null)
                || (getItem == null)
                || (csvProcessorType == null)
                || !csvProcessorType.IsSubclassOf(typeof(CsvBase&lt;T&gt;)))
            {
                return string.Empty;
            }

            return csvProcessorType
                .GetConstructor(new[] { source.GetType(), getItem.GetType() })
                ?.Invoke(new object[] { source, getItem })
                .ToString();
        }

        private static void Main()
        {
            var words = new[] { ",this", "   is   ", "a", "test", "Super, \"luxurious\" truck" };

            Console.WriteLine(words.ToCsv(word =&gt; word, typeof(CsvAlwaysQuote&lt;string&gt;)));
            Console.WriteLine(words.ToCsv(word =&gt; word, typeof(CsvRfc4180&lt;string&gt;)));
            Console.WriteLine(words.ToCsv(word =&gt; word, typeof(CsvBare&lt;string&gt;)));
            Console.WriteLine(words.ToCsv(word =&gt; word, typeof(CsvTrimAlwaysQuote&lt;string&gt;)));
            Console.WriteLine(words.ToCsv(word =&gt; word, typeof(CsvTrimRfc4180&lt;string&gt;)));
            Console.WriteLine(words.ToCsv(word =&gt; word, typeof(CsvTrimBare&lt;string&gt;)));
            Console.ReadLine();
        }
    }
}