Come viene implementata la classe StringBuilder? Crea internamente nuovi oggetti stringa ogni volta che aggiungiamo?Come viene implementata la classe StringBuilder? Crea internamente nuovi oggetti stringa ogni volta che aggiungiamo?
risposta
In .NET 2.0 utilizza internamente la classe String
. String
è immutabile solo all'esterno dello spazio dei nomi System
, quindi StringBuilder
può farlo.
In .NET 4.0 String
è stato modificato per utilizzare char[]
.
In 2.0 StringBuilder
si presentava così
public sealed class StringBuilder : ISerializable
{
// Fields
private const string CapacityField = "Capacity";
internal const int DefaultCapacity = 0x10;
internal IntPtr m_currentThread;
internal int m_MaxCapacity;
internal volatile string m_StringValue; // HERE ----------------------
private const string MaxCapacityField = "m_MaxCapacity";
private const string StringValueField = "m_StringValue";
private const string ThreadIDField = "m_currentThread";
Ma in 4.0 Sembra che questo:
public sealed class StringBuilder : ISerializable
{
// Fields
private const string CapacityField = "Capacity";
internal const int DefaultCapacity = 0x10;
internal char[] m_ChunkChars; // HERE --------------------------------
internal int m_ChunkLength;
internal int m_ChunkOffset;
internal StringBuilder m_ChunkPrevious;
internal int m_MaxCapacity;
private const string MaxCapacityField = "m_MaxCapacity";
internal const int MaxChunkSize = 0x1f40;
private const string StringValueField = "m_StringValue";
private const string ThreadIDField = "m_currentThread";
Così evidentemente è stata cambiata da utilizzando un string
di utilizzare un char[]
.
MODIFICA: risposta aggiornata per riflettere i cambiamenti in .NET 4 (che ho appena scoperto).
Non ne avevo idea .. Pensa che farò un po 'di magia riflettente per soddisfare la mia curiosità :) – cwap
@Brian: per quanto ne so detiene internamente una matrice 'Char', non una' String' (almeno in .NET 4, forse questo è cambiato?) –
@Fredrik - nell'implementazione MS, è davvero una 'stringa' che viene mutata –
Non proprio: utilizza il buffer dei caratteri interno. Solo quando la capacità del buffer si esaurisce, allocherà un nuovo buffer. L'operazione di aggiunta verrà semplicemente aggiunta a questo buffer, l'oggetto stringa verrà creato quando il metodo ToString() viene chiamato su di esso; d'ora in poi, è consigliabile per molte concatenazioni di stringhe poiché ogni concatop tradizionale stringa creerebbe una nuova stringa. È anche possibile specificare la capacità iniziale del generatore di stringhe se si ha una vaga idea su di esso per evitare allocazioni multiple.
Modifica: Le persone sottolineano che la mia comprensione è sbagliata. Si prega di ignorare la risposta (Io invece non eliminarlo - si presenterà come una prova della mia ignoranza :-)
Agisce * come se fosse un buffer di caratteri, ma in realtà è un'istanza 'stringa' mutata. Onesto. –
Grazie Marc - Avevo l'impressione che usasse il buffer dei caratteri. Significa che avrebbe un'implementazione nativa per mutare l'oggetto stringa. – VinayC
sicuro, ma è una classe di base del framework. Ha accesso all'implementazione nativa. –
Se guardo .NET Reflector a .NET 2 poi mi trovo:
public StringBuilder Append(string value)
{
if (value != null)
{
string stringValue = this.m_StringValue;
IntPtr currentThread = Thread.InternalGetCurrentThread();
if (this.m_currentThread != currentThread)
{
stringValue = string.GetStringForStringBuilder(stringValue, stringValue.Capacity);
}
int length = stringValue.Length;
int requiredLength = length + value.Length;
if (this.NeedsAllocation(stringValue, requiredLength))
{
string newString = this.GetNewString(stringValue, requiredLength);
newString.AppendInPlace(value, length);
this.ReplaceString(currentThread, newString);
}
else
{
stringValue.AppendInPlace(value, length);
this.ReplaceString(currentThread, stringValue);
}
}
return this;
}
Quindi è un esempio di stringa mutato ...
EDIT Tranne che in .NET 4 è a char[]
@Richard: grazie per l'EDIT. Non lo sapevo. –
Se si desidera visualizzare una delle possibili implementazioni (che è simile a quella fornita con l'implementazione di microsoft fino alla v3.5), è possibile vedere su github the source of the Mono one.
ho fatto un piccolo campione per dimostrare come funziona StringBuilder in .NET 4. Il contratto è
public interface ISimpleStringBuilder
{
ISimpleStringBuilder Append(string value);
ISimpleStringBuilder Clear();
int Lenght { get; }
int Capacity { get; }
}
e questa è un'implementazione molto semplice
public class SimpleStringBuilder : ISimpleStringBuilder
{
public const int DefaultCapacity = 32;
private char[] _internalBuffer;
public int Lenght { get; private set; }
public int Capacity { get; private set; }
public SimpleStringBuilder(int capacity)
{
Capacity = capacity;
_internalBuffer = new char[capacity];
Lenght = 0;
}
public SimpleStringBuilder() : this(DefaultCapacity) { }
public ISimpleStringBuilder Append(string value)
{
char[] data = value.ToCharArray();
//check if space is available for additional data
InternalEnsureCapacity(data.Length);
foreach (char t in data)
{
_internalBuffer[Lenght] = t;
Lenght++;
}
return this;
}
public ISimpleStringBuilder Clear()
{
_internalBuffer = new char[Capacity];
Lenght = 0;
return this;
}
public override string ToString()
{
//use only non-null ('\0') characters
var tmp = new char[Lenght];
for (int i = 0; i < Lenght; i++)
{
tmp[i] = _internalBuffer[i];
}
return new string(tmp);
}
private void InternalExpandBuffer()
{
//double capacity by default
Capacity *= 2;
//copy to new array
var tmpBuffer = new char[Capacity];
for (int i = 0; i < _internalBuffer.Length; i++)
{
char c = _internalBuffer[i];
tmpBuffer[i] = c;
}
_internalBuffer = tmpBuffer;
}
private void InternalEnsureCapacity(int additionalLenghtRequired)
{
while (Lenght + additionalLenghtRequired > Capacity)
{
//not enough space in the current buffer
//double capacity
InternalExpandBuffer();
}
}
}
Questo codice non viene filettatura sicuro, non esegue alcuna convalida dell'input e non utilizza la magia interna (non sicura) di System.String. Tuttavia dimostra l'idea alla base della classe StringBuilder.
Alcuni test di unità e codice di esempio completo possono essere trovati a github.
La risposta accettata manca il marchio di un miglio.La modifica significativa a 4.in 4.0 non è il passaggio da un valore non sicuro a char[]
- è il fatto che StringBuilder
è ora in realtà un elenco collegato di istanze StringBuilder
.
La ragione di questo cambiamento dovrebbe essere ovvio: oggi non c'è mai la necessità di riallocare il buffer (un'operazione costosa, dal momento che, insieme con l'allocazione di più memoria, si hanno anche per copiare tutti i contenuti da il vecchio buffer a quello nuovo).
Questo significa chiamare ToString()
è ora leggermente più lento, dal momento che la stringa finale deve essere calcolata, ma facendo un gran numero di operazioni di Append()
è ora significativamente più veloce. Ciò si adatta al tipico caso d'uso per StringBuilder
: molte chiamate a Append()
, seguite da una singola chiamata a ToString()
.
È possibile trovare parametri di riferimento here. La conclusione? La nuova lista collegata StringBuilder
utilizza marginalmente più memoria, ma è significativamente più veloce per il tipico caso d'uso.
- 1. JSON.stringify sfugge le doppie virgolette ogni volta che viene stringa
- 2. Come viene implementata GetHashCode() della stringa C#?
- 3. startActivity che crea ogni volta una nuova istanza di attività
- 4. Strani, IEnumerable.ToList() crea oggetti del tutto nuovi
- 5. . NET: la creazione di nuovi eventi ogni volta che l'evento genera una buona pratica?
- 6. Cron job che crea file vuoti ogni volta che viene eseguito
- 7. Come viene implementata la proprietà di dipendenza?
- 8. Come viene definito (,) internamente?
- 9. GWT Maven Plugin viene ricompilato ogni volta
- 10. Inkscape crea immagini pixelate ogni volta che copio e incollo
- 11. Come tagliare la stringa di StringBuilder?
- 12. Lambda crea ENI ogni volta che viene richiamato: limite di battuta
- 13. Crea StringBuilder dal byte []
- 14. Come viene implementata la funzione numpy.cov()?
- 15. Come viene implementata la std :: tuple?
- 16. Come viene implementata la persistenza di neo4j?
- 17. Perdita di memoria ogni volta che viene rilasciato UIScrollView
- 18. Operazione interna quando aggiungiamo due oggetti Integer?
- 19. Come posso forzare assetic a rendere le risorse ogni volta che la pagina viene ricaricata?
- 20. Rilevamento ogni volta che il socket viene disconnesso utilizzando select()
- 21. Come viene rappresentata una stringa in Java internamente?
- 22. Perché l'implementazione interna di HashSet crea oggetti fittizi da inserire come valori in HashMap piuttosto che inserire valori null?
- 23. Come viene implementata la programmazione basata sugli eventi di Win32?
- 24. La funzione di Chrome Prerender viene annullata ogni volta
- 25. Come viene implementata la classe Object (metodi come hashCode e campi interni)?
- 26. Classe di stringa Python come StringBuilder in C#?
- 27. Esiste qualcosa come una classe che può essere implementata?
- 28. Come viene implementata la programmazione guidata dagli eventi?
- 29. In che modo l'iniezione di dipendenza viene implementata manualmente?
- 30. Il database Android viene ricreato ogni volta che viene avviata l'applicazione
+1 Ho imparato qualcosa di nuovo anche da questa domanda :) –
@Brian Rasmussen aspetta la risposta di Jon Skeet. Scommetto che sarà enorme e pieno di nuove cose da imparare;) – prostynick
Reflector rivela tutto. –