¿Cómo se implementa la clase StringBuilder? ¿Crea internamente nuevos objetos de cadena cada vez que agregamos?¿Cómo se implementa la clase StringBuilder? ¿Crea internamente nuevos objetos de cadena cada vez que agregamos?
Respuesta
En .NET 2.0 que utiliza la clase String
internamente. String
solo es inmutable fuera del espacio de nombre System
, por lo que StringBuilder
puede hacerlo.
En .NET 4.0 String
se ha cambiado para utilizar char[]
.
En 2.0 StringBuilder
veía así
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";
Pero en 4.0 se ve así:
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";
Así que, evidentemente, se cambió el uso de un string
al uso de una char[]
.
EDIT: respuesta actualizada para reflejar los cambios en .NET 4 (que acabo de descubrir).
No tenía idea ... Creo que voy a hacer algo de magia reflector para satisfacer mi curiosidad :) – cwap
@Brian: hasta donde sé, contiene una matriz 'Char' internamente, no una' Cadena' (al menos en .NET 4, quizás esto ha cambiado?) –
@Fredrik - en la implementación de MS, realmente es una 'cadena' que se muta –
En realidad, no utiliza el búfer de caracteres interno. Solo cuando la capacidad del buffer se agote, asignará un nuevo buffer. La operación de agregar simplemente agregará a este búfer, el objeto de cadena se creará cuando se le llame al método ToString(); de ahí en adelante, es recomendable para muchas concatenaciones de cadenas ya que cada cadena tradicional de concaturas crearía una nueva cadena. También puede especificar la capacidad inicial del generador de cadenas si tiene una idea aproximada para evitar asignaciones múltiples.
Editar: La gente está señalando que mi entendimiento es incorrecto. favor ignorar la respuesta (prefiero no lo elimine, - que se mantendrá como una prueba de mi ignorancia :-)
Actúa * como si * fuera un buffer de caracteres, pero realmente es una instancia de 'cadena 'mutada. Honesto. –
Gracias Marc - Estaba bajo la impresión de que utiliza el buffer de caracteres. Significa que tendría una implementación nativa para mutar el objeto de cadena. – VinayC
seguro, pero es una clase básica de framework. Tiene acceso a la implementación nativa. –
Si miro NET Reflector en .NET 2 ya veremos esto:
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;
}
Por lo que es una instancia de secuencia mutada ...
EDITAR Excepto en .NET 4 es a char[]
@Richard: gracias por la EDITACIÓN. No sabía ese hecho. –
Si desea ver una de las implementaciones posibles (que es similar a la que se envía con la implementación de Microsoft hasta v3.5) puede ver the source of the Mono one en github.
he hecho una pequeña muestra para demostrar cómo funciona StringBuilder en .NET 4. El contrato es
public interface ISimpleStringBuilder
{
ISimpleStringBuilder Append(string value);
ISimpleStringBuilder Clear();
int Lenght { get; }
int Capacity { get; }
}
y esta es una aplicación muy básica
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();
}
}
}
Este código no se thread- seguro, no realiza ninguna validación de entrada y no está utilizando la magia interna (insegura) de System.String. Sin embargo, demuestra la idea detrás de la clase StringBuilder.
Algunas pruebas unitarias y el código de muestra completo se pueden encontrar en github.
La respuesta aceptada pierde la marca por una milla.El cambio significativo a StringBuilder
en 4.0 no es el cambio de string
inseguro a char[]
- es el hecho de que StringBuilder
es ahora en realidad una lista de StringBuilder
instancias conectadas.
La razón de este cambio debería ser obvio: ahora nunca hay una necesidad de reasignar el búfer (una operación costosa, ya que, junto con la asignación de más memoria, también hay que copiar todo el contenido de la el buffer anterior al nuevo).
Esto significa llamar ToString()
es ahora un poco más lento, ya que la cadena final tiene que ser calculada, pero haciendo un gran número de operaciones Append()
es ahora significativamente más rápido. Esto encaja con el típico caso de uso para StringBuilder
: un montón de llamadas a Append()
, seguido de una sola llamada a ToString()
.
Puede encontrar los puntos de referencia here. ¿La conclusión? La nueva lista enlazada StringBuilder
usa marginalmente más memoria, pero es significativamente más rápida para el típico caso de uso.
- 1. ¿Cómo crea BinaryFormatter.Deserialize nuevos objetos?
- 2. ¿Cómo se implementa malloc() internamente?
- 3. ¿Cómo se implementa la referencia internamente?
- 4. ¿ldstr implementa internamente newobj?
- 5. cómo C++ implementa el polimorfismo internamente?
- 6. ¿Cómo implementa .Net CLR una "Interfaz" internamente?
- 7. PRISM WPF: la navegación crea una nueva vista cada vez
- 8. ¿Cómo se implementa una clase en C?
- 9. Cómo recortar la cadena de StringBuilder?
- 10. Operación dentro cuando agregamos dos objetos enteros?
- 11. ¿Cómo crea db4o objetos?
- 12. .NET: ¿Está creando nuevos EventArgs cada vez que el evento desactive una buena práctica?
- 13. ¿Cómo se implementa set()?
- 14. ¿Cómo puedo ejecutar código en una definición de clase C# cada vez que se deserializa cualquier instancia de la clase?
- 15. ¿Ruby's Enumerable # zip crea matrices internamente?
- 16. use Logger.getLogger() cada vez que lo necesito o creo una vez por clase
- 17. ¿Cómo puedo detectar cada vez que se superponen dos UIImageView?
- 18. clase anónima de Java que implementa ActionListener?
- 19. StringBuilder: cómo obtener la cadena final?
- 20. Cuando Spring crea instancias de objetos que se inyectan
- 21. La base de datos de Android se recrea cada vez que se inicia la aplicación
- 22. ¿Se ha calculado [hash NSString] cada vez?
- 23. versión de actualización cada vez que se hace git push?
- 24. ¿Cómo se define (?) Internamente?
- 25. Restablecimiento de objetos frente a la construcción de objetos nuevos
- 26. Cómo crear una clase que sea abstracta, pero no internamente
- 27. Pérdida de memoria cada vez que se lanza UIScrollView
- 28. ¿Cómo agregamos la animación css + en jquery?
- 29. Notificación de nuevos objetos S3
- 30. Android en Eclipse se bloquea cada vez que abro main.xml
+1 Aprendí algo nuevo de esta pregunta también :) –
@Brian Rasmussen espera la respuesta de Jon Skeet. Apuesto a que será enorme y estará lleno de cosas nuevas que aprender;) – prostynick
El reflector lo revela todo. –