Starting with .NET Core 2.1 there is a new way to reverse a string using the string.Create
method.
Note that this solution does not handle Unicode combining characters etc. correctly, as "Les Mise\u0301rables" would be converted to "selbarésiM seL". See the other answers for a better solution.
public static string Reverse(string input)
{
return string.Create<string>(input.Length, input, (chars, state) =>
{
state.AsSpan().CopyTo(chars);
chars.Reverse();
});
}
This essentially copies the characters of input
to a new string and reverses the new string in-place.
Why is string.Create
useful?
When we create a string from an existing array, a new internal array is allocated and the values are copied. Otherwise, it would be possible to mutate a string after its creation (in a safe environment). That is, in the following snippet we have to allocate an array of length 10 twice, one as the buffer and one as the string's internal array.
var chars = new char[10];
// set array values
var str = new string(chars);
string.Create
essentially allows us to manipulate the internal array during creation time of the string. This is, we do not need a buffer anymore and can therefore avoid allocating that one char array.
Steve Gordon has written about it in more detail here. There is also an article on MSDN.
How to use string.Create
?
public static string Create<TState>(int length, TState state, SpanAction<char, TState> action);
The method takes three parameters:
- The length of the string to create,
- the data you want to use to dynamically create the new string,
- and a delegate that creates the final string from the data, where the first parameter points to the internal
char
array of the new string and the second is the data (state) you passed to string.Create
.
Inside the delegate we can specify how the new string is created from the data. In our case, we just copy the characters of the input string to the Span
used by the new string. Then we reverse the Span
and hence the whole string is reversed.
Benchmarks
To compare my proposed way of reversing a string with the accepted answer, I have written two benchmarks using 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);
}
Here are the results on my machine:
| 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 |
As you can see, with ReverseWithStringCreate
we allocate only half of the memory used by the ReverseWithArray
method.