Pregunta

En .NET, las cadenas son inmutables y son variables de tipo referencia. A menudo, esto viene como una sorpresa para los nuevos desarrolladores .NET que pueden confundirlos con objetos de tipo de valor debido a su comportamiento. Sin embargo, aparte de la práctica de utilizar StringBuilder de larga concatenación de esp. en bucles, ¿hay alguna razón en la práctica que se necesita conocer esta distinción?

¿Qué escenarios del mundo real son ayudados o evitarse mediante la comprensión de la diferencia de valor de referencia con respecto a las cadenas de .NET vs fingiendo / malentendido que sean los tipos de valor?

¿Fue útil?

Solución

El diseño de strings fue deliberadamente tal que no debería tener que preocuparse demasiado acerca de él como programador. En muchas situaciones, esto significa que sólo se puede asignar, mover, copiar, cambiar las cadenas sin pensar demasiado de las posibles consecuencias intrincados si existía otra referencia a la cadena y sería cambiado al mismo tiempo (como ocurre con las referencias a objetos).

parámetros de cadena en una llamada al método

(EDIT: esta sección añadió más tarde)
Cuando las cadenas se pasan a un método, que se pasan por referencia. Cuando sólo se leen en el cuerpo del método, no sucede nada especial. Pero cuando se cambian, se crea una copia y la variable temporal se utiliza en el resto del método. Este proceso se denomina copy-on-write .

Lo que preocupa es que jóvenes que se utilizan para el hecho de que los objetos son referencias y que se cambian en un método que cambia el parámetro pasado. Para hacer lo mismo con las cadenas, tienen que utilizar la palabra clave ref. En realidad, esto permite que la cadena de referencia para ser transformados y regresó a la función de llamada. Si no lo hace, la cadena no puede ser cambiado por el cuerpo del método:

void ChangeBad(string s)      { s = "hello world"; }
void ChangeGood(ref string s) { s = "hello world"; }

// in calling method:
string s1 = "hi";
ChangeBad(s1);       // s1 remains "hi" on return, this is often confusing
ChangeGood(ref s1);  // s1 changes to "hello world" on return

En StringBuilder

Esta distinción es importante, pero los programadores principiantes suelen ser mejor no saber demasiado acerca de él. Usando StringBuilder cuando se hace una gran cantidad de "construir" cadena es buena, pero a menudo, su aplicación tendrá mucho más pescado para freír y el pequeño aumento en el rendimiento de StringBuilder es insignificante. Tenga cuidado con los programadores que le indican que todos manipulación de cadenas se debe hacer uso de StringBuilder.

Como regla empírica aproximada: StringBuilder tiene algún costo creación, pero Anexión es barato. Cadena tiene un costo creación barato, pero la concatenación es relativamente caro. El punto de inflexión es de alrededor de 400-500 concatenaciones, dependiendo del tamaño: después de eso, StringBuilder se vuelve más eficiente

.

Más información sobre el rendimiento StringBuilder vs cadena

EDIT: basado en un comentario de Konrad Rudolph, añadí esta sección

.

Si la regla anterior del pulgar hace que uno se pregunta, considere los siguientes explicaciones un poco más detalladas:

  • StringBuilder con muchos pequeños cadena añade la concatenación de cadenas sobrepasa con bastante rapidez (30, 50 APPENDs), pero en 2μs, incluso el aumento de rendimiento de 100% es a menudo insignificante (salvo para algunas situaciones poco frecuentes);
  • StringBuilder con algunos grandes APPENDs de cuerda (80 caracteres o cadenas más grandes) deja atrás a la concatenación de cadenas sólo después de miles, a veces centésimas de miles de iteraciones y la diferencia es a menudo sólo unos porcentajes;
  • Mezcla acciones de cuerda (reemplazar, insertar, en cadenas, la expresión regular, etc.) a menudo hace que el uso de StringBuilder o la concatenación de cadenas iguales;
  • concatenación de cadenas de constantes se puede optimizar de distancia por el compilador, el CLR o el JIT, no pueda por StringBuilder;
  • Código menudo mezcla + concatenación, StringBuilder.Append, String.Format, ToString y otras operaciones de cadena, utilizando StringBuilder en estos casos casi nunca es eficaz.

Por lo tanto, cuando es es eficiente? En los casos en que se añaden muchas pequeñas cadenas, es decir, para serializar los datos en un archivo, por ejemplo, y cuando no es necesario cambiar los datos "escrito" una vez "por escrito" a StringBuilder. Y en los casos en que muchos métodos necesitan para añadir algo, porque StringBuilder es un tipo de referencia y las cuerdas se copian cuando se cambian.

En las cadenas internadas

Un problema surge - no sólo con los programadores Junior - cuando tratan de hacer una comparación de referencia y descubrir que a veces el resultado es verdadero, y, a veces es falso, aparentemente en las mismas situaciones. ¿Que pasó? Cuando las cuerdas fueron internados por el compilador y se añaden a la estática mundial internados conjunto de cadenas, la comparación entredos cuerdas pueden apuntar a la misma dirección de memoria. Cuando (referencia!) Comparar dos cadenas iguales, una internada y uno no, dará lugar a falsas. Usar la comparación = o Equals y no jugar con ReferenceEquals cuando se trata de cadenas.

En String.Empty

En la misma liga se ajusta a un comportamiento extraño que a veces ocurre cuando se utiliza String.Empty: la String.Empty estática siempre es internado, pero una variable con un valor asignado no lo es. Sin embargo, por defecto el compilador asignará String.Empty y apuntan a la misma dirección de memoria. Resultado:. Una variable de cadena mutable, cuando se compara con ReferenceEquals, devuelve verdadero, mientras que se podría esperar falsa en lugar

// emptiness is treated differently:
string empty1 = String.Empty;
string empty2 = "";
string nonEmpty1 = "something";
string nonEmpty2 = "something";

// yields false (debug) true (release)
bool compareNonEmpty = object.ReferenceEquals(nonEmpty1, nonEmpty2);

// yields true (debug) false (release, depends on .NET version and how it's assigned)
bool compareEmpty = object.ReferenceEquals(empty1, empty2);

En profundidad

Es, básicamente, preguntó acerca de las situaciones que pueden ocurrir a los no iniciados. Creo que mi punto se reduce a evitar object.ReferenceEquals, ya que no se puede confiar cuando se utiliza con cuerdas. La razón es que la internación cadena se utiliza cuando la cadena es constante en el código, pero no siempre. No se puede confiar en este comportamiento. Aunque String.Empty y "" siempre están internados, no es cuando el compilador cree que el valor se puede cambiar. Diferentes opciones de optimización de depuración (vs liberación y otros) van a dar resultados diferentes.

Cuando lo que necesita ReferenceEquals de todos modos? Con los objetos que tiene sentido, pero con condiciones que no lo hace. Enseñar a nadie a trabajar con cuerdas para evitar su uso a menos que también entienden unsafe y cubrió a objetos.

Rendimiento

Cuando el rendimiento es importante, se puede saber que las cadenas son en realidad no inmutable y que usando StringBuilder es no siempre el más rápido enfoque .

Una gran cantidad de la información que se utiliza aquí es noreferrer">

Actualización: añadido código de ejemplo
Actualización: añade 'en profundidad' sección (espero que alguien encuentre esto útil;)
Actualización: añade algunos enlaces, sección de cuerda params
añadió Actualización: añade la estimación de cuándo cambiar de cuerdas para StringBuilder
Actualización: añade una sección adicional en el rendimiento vs StringBuilder String, después de una observación de Konrad Rudolph

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top