A partir de .NET Core 2.1, hay una nueva forma de invertir una cadena utilizando string.Create
método.
Tenga en cuenta que esta solución no maneja los caracteres combinados Unicode, etc. correctamente, ya que "Les Mise \ u0301rables" se convertiría en "selbarésiM seL". El otro responde para una mejor solución.
public static string Reverse(string input)
{
return string.Create<string>(input.Length, input, (chars, state) =>
{
state.AsSpan().CopyTo(chars);
chars.Reverse();
});
}
Esto esencialmente copia los caracteres de input
a una nueva cadena e invierte la nueva cadena en el lugar.
¿Por qué es string.Create
útil?
Cuando creamos una cadena a partir de una matriz existente, se asigna una nueva matriz interna y se copian los valores. De lo contrario, sería posible mutar una cadena después de su creación (en un entorno seguro). Es decir, en el siguiente fragmento tenemos que asignar una matriz de longitud 10 dos veces, una como el búfer y otra como la matriz interna de la cadena.
var chars = new char[10];
// set array values
var str = new string(chars);
string.Create
esencialmente nos permite manipular la matriz interna durante el tiempo de creación de la cadena. Esto es, ya no necesitamos un búfer y, por lo tanto, podemos evitar asignar esa matriz de caracteres.
Steve Gordon ha escrito sobre esto con más detalle aquí . También hay un artículo sobre MSDN .
¿Cómo usarlo string.Create
?
public static string Create<TState>(int length, TState state, SpanAction<char, TState> action);
El método toma tres parámetros:
- La longitud de la cadena para crear,
- los datos que desea usar para crear dinámicamente la nueva cadena,
- y un delegado que crea la cadena final a partir de los datos, donde el primer parámetro apunta a la
char
matriz interna de la nueva cadena y el segundo es el dato (estado) al que pasó string.Create
.
Dentro del delegado podemos especificar cómo se crea la nueva cadena a partir de los datos. En nuestro caso, simplemente copiamos los caracteres de la cadena de entrada a los Span
utilizados por la nueva cadena. Luego revertimos elSpan
y, por lo tanto, se invierte toda la cadena.
Puntos de referencia
Para comparar mi forma propuesta de invertir una cadena con la respuesta aceptada, he escrito dos puntos de referencia utilizando 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);
}
Aquí están los resultados en mi 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 puede ver, con ReverseWithStringCreate
solo asignamos la mitad de la memoria utilizada por el ReverseWithArray
método.