Начиная с .NET Core 2.1, появился новый способ обратить строку, используя string.Create
метод.
Обратите внимание, что это решение не обрабатывает Юникод, комбинирующий символы и т. Д. Правильно, поскольку "Les Mise \ u0301rables" будет преобразован в "selbarésiM seL". В другие ответы для лучшего решения.
public static string Reverse(string input)
{
return string.Create<string>(input.Length, input, (chars, state) =>
{
state.AsSpan().CopyTo(chars);
chars.Reverse();
});
}
По сути, это копирует символы input
в новую строку и переворачивает новую строку на месте.
Чем string.Create
полезен?
Когда мы создаем строку из существующего массива, выделяется новый внутренний массив и значения копируются. В противном случае можно было бы изменить строку после ее создания (в безопасной среде). То есть в следующем фрагменте мы должны выделить массив длиной 10 дважды, один в качестве буфера и один в качестве внутреннего массива строки.
var chars = new char[10];
// set array values
var str = new string(chars);
string.Create
по сути, позволяет нам манипулировать внутренним массивом во время создания строки. Это значит, что нам больше не нужен буфер, и поэтому мы можем избежать выделения этого одного массива символов.
Стив Гордон написал об этом более подробно здесь . Также есть статья на MSDN .
Как использовать string.Create
?
public static string Create<TState>(int length, TState state, SpanAction<char, TState> action);
Метод принимает три параметра:
- Длина строки для создания,
- данные, которые вы хотите использовать для динамического создания новой строки,
- и делегат, который создает последнюю строку из данных, где первый параметр указывает на внутренний
char
массив новой строки, а второй - на данные (состояние), которые вы передали string.Create
.
Внутри делегата мы можем указать, как новая строка создается из данных. В нашем случае мы просто копируем символы входной строки в Span
используемую новую строку. Затем мы обращаемSpan
переворачиваем и, следовательно, вся строка переворачивается.
Ориентиры
Чтобы сравнить предложенный мной способ обращения строки с принятым ответом, я написал два теста, используя BenchmarkDotNet.
public class StringExtensions
{
public static string ReverseWithArray(string input)
{
var charArray = input.ToCharArray();
Array.Reverse(charArray);
return new string(charArray);
}
public static string ReverseWithStringCreate(string input)
{
return string.Create(input.Length, input, (chars, state) =>
{
state.AsSpan().CopyTo(chars);
chars.Reverse();
});
}
}
[MemoryDiagnoser]
public class StringReverseBenchmarks
{
private string input;
[Params(10, 100, 1000)]
public int InputLength { get; set; }
[GlobalSetup]
public void SetInput()
{
// Creates a random string of the given length
this.input = RandomStringGenerator.GetString(InputLength);
}
[Benchmark(Baseline = true)]
public string WithReverseArray() => StringExtensions.ReverseWithArray(input);
[Benchmark]
public string WithStringCreate() => StringExtensions.ReverseWithStringCreate(input);
}
Вот результаты на моей машине:
| Method | InputLength | Mean | Error | StdDev | Gen 0 | Allocated |
| ---------------- | ----------- | -----------: | ---------: | --------: | -----: | --------: |
| WithReverseArray | 10 | 45.464 ns | 0.4836 ns | 0.4524 ns | 0.0610 | 96 B |
| WithStringCreate | 10 | 39.749 ns | 0.3206 ns | 0.2842 ns | 0.0305 | 48 B |
| | | | | | | |
| WithReverseArray | 100 | 175.162 ns | 2.8766 ns | 2.2458 ns | 0.2897 | 456 B |
| WithStringCreate | 100 | 125.284 ns | 2.4657 ns | 2.0590 ns | 0.1473 | 232 B |
| | | | | | | |
| WithReverseArray | 1000 | 1,523.544 ns | 9.8808 ns | 8.7591 ns | 2.5768 | 4056 B |
| WithStringCreate | 1000 | 1,078.957 ns | 10.2948 ns | 9.6298 ns | 1.2894 | 2032 B |
Как видите, с помощью ReverseWithStringCreate
мы выделяем только половину памяти, используемой ReverseWithArray
методом.