В дополнение к элегантности вы можете рассмотреть скорость и возможность повторного использования для типов, отличных от String. Для элегантности я бы предложил использовать метод расширения для абстрагирования деталей, чтобы общее использование выглядело примерно так:
ns = sa.Join(" * ");
Для скорости рассмотрим следующие варианты тестов, включая некоторые решения, предложенные другими людьми, которые ответили на вопрос:
public void Test_variants()
{
const string item = "a";
const int numberOfTimes = 100000;
const string delimiter = ", ";
string[] items = new List<string>(Enumerable.Repeat(item, numberOfTimes)).ToArray();
string expected = String.Join(delimiter, items);
Time(StringJoin, items, delimiter, expected);
Time(Aggregate, items, delimiter, expected);
Time(CheckForEndInsideLoop_String, items, delimiter, expected);
Time(CheckForBeginningInsideLoop_String, items, delimiter, expected);
Time(RemoveFinalDelimiter_String, items, delimiter, expected);
Time(CheckForEndInsideLoop_StringBuilder, items, delimiter, expected);
Time(RemoveFinalDelimiter_StringBuilder, items, delimiter, expected);
}
private static void Time(Func<string[], string, string> func, string[] items, string delimiter, string expected)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
string result = func(items, delimiter);
stopwatch.Stop();
bool isValid = result == expected;
Console.WriteLine("{0}\t{1}\t{2}", stopwatch.Elapsed, isValid, func.Method.Name);
}
private static string CheckForEndInsideLoop_String(string[] items, string delimiter)
{
string result = "";
for (int i = 0; i < items.Length; i++)
{
result += items[i];
if (i != items.Length - 1)
{
result += delimiter;
}
}
return result;
}
private static string RemoveFinalDelimiter_String(string[] items, string delimiter)
{
string result = "";
for (int i = 0; i < items.Length; i++)
{
result += items[i] + delimiter;
}
return result.Substring(0, result.Length - delimiter.Length);
}
private static string CheckForBeginningInsideLoop_String(string[] items, string delimiter)
{
string result = "";
foreach (string s in items)
{
if (result.Length != 0)
{
result += delimiter;
}
result += s;
}
return result;
}
private static string CheckForEndInsideLoop_StringBuilder(string[] items, string delimiter)
{
StringBuilder result = new StringBuilder();
for (int i = 0; i < items.Length; i++)
{
result.Append(items[i]);
if (i != items.Length - 1)
{
result.Append(delimiter);
}
}
return result.ToString();
}
private static string RemoveFinalDelimiter_StringBuilder(string[] items, string delimiter)
{
StringBuilder result = new StringBuilder();
for (int i = 0; i < items.Length; i++)
{
result.Append(items[i]);
result.Append(delimiter);
}
result.Length = result.Length - delimiter.Length;
return result.ToString();
}
private static string StringJoin(string[] items, string delimiter)
{
return String.Join(delimiter, items);
}
private static string Aggregate(string[] items, string delimiter)
{
return items.Aggregate((c, s) => c + delimiter + s);
}
Результаты на моей коробке следующие:
00:00:00.0027745 True StringJoin
00:00:24.5523967 True Aggregate
00:00:47.8091632 True CheckForEndInsideLoop_String
00:00:47.4682981 True CheckForBeginningInsideLoop_String
00:00:23.7972864 True RemoveFinalDelimiter_String
00:00:00.0076439 True CheckForEndInsideLoop_StringBuilder
00:00:00.0052803 True RemoveFinalDelimiter_StringBuilder
Это означает, что лучшим вариантом, если вы работаете только со строковыми массивами, является String.Join, за которым следуют варианты StringBuilder. Обратите внимание, что проверка последнего элемента внутри цикла имеет гораздо большую разницу при работе со строками, чем при работе со StringBuilder. Производительность для реализаций, основанных на String, также немного улучшается, когда список элементов, которые должны быть разделены, невелик. Я выполнил те же тесты с параметром numberOfItems, установленным на 10, и получил следующие результаты:
00:00:00.0001788 True StringJoin
00:00:00.0014983 True Aggregate
00:00:00.0001666 True CheckForEndInsideLoop_String
00:00:00.0002202 True CheckForBeginningInsideLoop_String
00:00:00.0002061 True RemoveFinalDelimiter_String
00:00:00.0002663 True CheckForEndInsideLoop_StringBuilder
00:00:00.0002278 True RemoveFinalDelimiter_StringBuilder
Следующее, что вы можете рассмотреть, - это возможность многократного использования. Если вы хотите построить строку из списка целых чисел, разделенных разделителем, String.Join будет вариантом только после того, как вы запустите .ToString () для каждого целого числа и создадите массив строк (потому что String.Join не может воздействовать на IEnumerable ).
Итак, в заключение, вы можете рассмотреть возможность использования метода расширения по следующим направлениям, чтобы получить хорошее сочетание элегантности, скорости и возможности повторного использования:
public static string Join<T>([CanBeNull] this IEnumerable<T> items, [CanBeNull] string delimiter)
{
StringBuilder result = new StringBuilder();
if (items != null && items.Any())
{
delimiter = delimiter ?? "";
foreach (T item in items)
{
result.Append(item);
result.Append(delimiter);
}
result.Length = result.Length - delimiter.Length;
}
return result.ToString();
}
использование:
ns = sa.Join(" * ");