Visit the reverse-string exercise on Exercism to read the full instructions and download the exercise files.
Dig Deeper
Array.Reverse
Array.Reverse
using System;
public static class ReverseString
{
public static string Reverse(string input)
{
var chars = input.ToCharArray();
Array.Reverse(chars);
return new string(chars);
}
}
The string class’ ToCharArray() method returns the string’s characters as a char[].
The `char[]` returned by `ToCharArray()` is a **copy** of the `string`'s characters.
Modifying the values in the `char[]` does **not** update the `string` it was created from.
We then pass the char[] to the Array.Reverse() method, which will reverse the array’s content in-place (meaning the argument is modified).
Finally, we return the reversed string by calling its constructor with the (reversed) char[].
If you’re interested in how this approach’s performance compares to other approaches, check the performance approach.
LINQ
LINQ
using System.Linq;
public static class ReverseString
{
public static string Reverse(string input)
{
return new string(input.Reverse().ToArray());
}
}
The string class implements the IEnumerable<char> interface, which allows us to call LINQ’s Reverse() extension method on it.
To convert the IEnumerable<char> returned by Reverse() back to a string, we first use ToArray() to convert it to a char[].
Finally, we return the reversed string by calling its constructor with the (reversed) char[].
Shortening
There are two things we can do to further shorten this method:
- Remove the curly braces by converting to an expression-bodied method
- Use a target-typed new expression to replace
new string with just new (the compiler can figure out the type from the method’s return type)
Using this, we end up with:
public static string Reverse(string input) => new(input.Reverse().ToArray());
If you’re interested in how this approach’s performance compares to other approaches, check the performance approach.
Span
Span<T>
public static class ReverseString
{
public static string Reverse(string input)
{
Span<char> chars = stackalloc[input.Length];
for (var i = 0; i < input.Length; i++)
{
chars[input.Length - 1 - i] = input[i];
}
return new string(chars);
}
}
C# 7.2. introduced the Span<T> class, which was specifically designed to allow performant iteration/mutation of array-like objects.
The Span<T> class helps improve performance by always being allocated on the stack, and not the heap.
As objects on the stack don’t need to be garbage collected, this can help improve performance (check this blog post for more information).
How can we leverage Span<T> to reverse our string?
The string class has an AsSpan() method, but that returns a ReadOnlySpan<char>, which doesn’t allow mutation (otherwise we’d be able to indirectly modify the string).
We can work around this by manually allocating a char[] and assigning to a Span<char>:
Span<char> chars = new char[input.Length];
for (var i = 0; i < input.Length; i++)
{
chars[input.Length - 1 - i] = input[i];
}
return new string(chars);
After creating Span<char>, we use a regular for-loop to iterate over the string’s characters and assign them to the right position in the span.
Finally, we can use the string constructor overload that takes a Span<char> to create the string.
However, this is basically the same approach as the Array.Reverse() approach, but with us also having to manually write a for-loop.
We can do one better though, and that is to use stackalloc.
With stackalloc, we can assign a block of memory on the stack (whereas the array would be stored on the heap).
Span<char> chars = stackalloc char[input.Length];
With this version, the memory allocated for the Span<char> is all on the stack and no garbage collection is needed for that data.
The stack has a finite amount of memory.
This means that for large strings, the above code will result in a `StackOverflowException` being thrown.
So what is the limit for the amount of memory we can allocate?
Well, this depends on how memory has already been allocated on the stack.
That said, a small test program successfully stack-allocated memory for 750_000 characters, so you might be fine.
Alternative
It is possible to use an alternative span-based implementation that is more readable, but has the downside of being about twice as slow:
Span<char> chars = stackalloc char[input.Length];
input.AsSpan().CopyTo(chars);
chars.Reverse();
return new string(chars);
If you’re interested in how this approach’s performance compares to other approaches, check the performance approach.
Source: Exercism csharp/reverse-string