A partir do .NET Core 2.1, há uma nova maneira de reverter uma string usando o string.Create
método
Observe que esta solução não manipula caracteres combinados Unicode etc. corretamente, pois "Les Mise \ u0301rables" seria convertido em "selbarésiM seL". O outro responde por uma solução melhor.
public static string Reverse(string input)
{
return string.Create<string>(input.Length, input, (chars, state) =>
{
state.AsSpan().CopyTo(chars);
chars.Reverse();
});
}
Isso essencialmente copia os caracteres de input
para uma nova sequência e reverte a nova sequência no local.
Por que é string.Create
útil?
Quando criamos uma string a partir de uma matriz existente, uma nova matriz interna é alocada e os valores são copiados. Caso contrário, seria possível alterar uma sequência após sua criação (em um ambiente seguro). Ou seja, no snippet a seguir, temos que alocar uma matriz de comprimento 10 duas vezes, uma como buffer e outra como matriz interna da string.
var chars = new char[10];
// set array values
var str = new string(chars);
string.Create
essencialmente nos permite manipular a matriz interna durante o tempo de criação da string. Ou seja, não precisamos mais de um buffer e, portanto, podemos evitar a alocação dessa matriz de caracteres.
Steve Gordon escreveu sobre isso com mais detalhes aqui . Há também um artigo no MSDN .
Como usar string.Create
?
public static string Create<TState>(int length, TState state, SpanAction<char, TState> action);
O método usa três parâmetros:
- O comprimento da string a ser criada,
- os dados que você deseja usar para criar dinamicamente a nova sequência,
- e um delegado que cria a sequência final a partir dos dados, onde o primeiro parâmetro aponta para a
char
matriz interna da nova sequência e o segundo são os dados (estado) aos quais você passou string.Create
.
Dentro do delegado, podemos especificar como a nova string é criada a partir dos dados. No nosso caso, apenas copiamos os caracteres da string de entrada para os Span
usados pela nova string. Então invertemos oSpan
e, portanto, toda a cadeia é invertida.
Benchmarks
Para comparar minha maneira proposta de reverter uma string com a resposta aceita, escrevi dois benchmarks usando o 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);
}
Aqui estão os resultados na minha máquina:
| 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 |
Como você pode ver, ReverseWithStringCreate
alocamos apenas metade da memória usada pelo ReverseWithArray
método.