A partire da .NET Core 2.1 c'è un nuovo modo per invertire una stringa usando il string.Create
metodo
Nota che questa soluzione non gestisce correttamente la combinazione di caratteri Unicode ecc., Poiché "Les Mise \ u0301rables" verrebbe convertito in "selbarésiM seL". L' altro risponde per una soluzione migliore.
public static string Reverse(string input)
{
return string.Create<string>(input.Length, input, (chars, state) =>
{
state.AsSpan().CopyTo(chars);
chars.Reverse();
});
}
Questo essenzialmente copia i caratteri di input
una nuova stringa e inverte la nuova stringa sul posto.
Perché è string.Create
utile
Quando creiamo una stringa da un array esistente, viene allocato un nuovo array interno e i valori vengono copiati. Altrimenti, sarebbe possibile mutare una stringa dopo la sua creazione (in un ambiente sicuro). Cioè, nel frammento seguente dobbiamo allocare una matrice di lunghezza 10 due volte, una come buffer e una come matrice interna della stringa.
var chars = new char[10];
// set array values
var str = new string(chars);
string.Create
essenzialmente ci consente di manipolare l'array interno durante il tempo di creazione della stringa. Ciò significa che non abbiamo più bisogno di un buffer e possiamo quindi evitare di allocare quell'array di caratteri.
Steve Gordon ne ha scritto più in dettaglio qui . C'è anche un articolo su MSDN .
Come usare string.Create
?
public static string Create<TState>(int length, TState state, SpanAction<char, TState> action);
Il metodo accetta tre parametri:
- La lunghezza della stringa da creare,
- i dati che desideri utilizzare per creare dinamicamente la nuova stringa,
- e un delegato che crea la stringa finale dai dati, in cui il primo parametro punta alla
char
matrice interna della nuova stringa e il secondo sono i dati (stato) a cui si è passati string.Create
.
All'interno del delegato possiamo specificare come viene creata la nuova stringa dai dati. Nel nostro caso, copiamo solo i caratteri della stringa di input in quelli Span
usati dalla nuova stringa. Quindi invertiamo Span
e quindi l'intera stringa viene invertita.
Punti di riferimenti
Per confrontare il mio modo proposto di invertire una stringa con la risposta accettata, ho scritto due benchmark usando 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);
}
Ecco i risultati sulla mia macchina:
| 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 |
Come puoi vedere, con ReverseWithStringCreate
allociamo solo metà della memoria utilizzata dal ReverseWithArray
metodo.