Pregunta

Una Cadena es un tipo de referencia que aunque tiene la mayoría de las características de un tipo de valor, tales como ser inmutable y habiendo == sobrecargado para comparar el texto en lugar de asegurarse de que hacen referencia al mismo objeto.

¿Por qué no de la cadena de sólo un tipo de valor, entonces?

¿Fue útil?

Solución

Las cadenas no son tipos de valor, ya que pueden ser enormes y deben almacenarse en el montón. Los tipos de valor se almacenan en la pila (en todas las implementaciones de CLR hasta el momento). La asignación de cadenas de pila rompería todo tipo de cosas: la pila es de solo 1 MB para 32 bits y 4 MB para 64 bits, tendrías que encajonar cada cadena, incurriendo en una penalización por copia, no podrías internar cadenas y uso de memoria globo, etc ...

(Editar: Se agregó una aclaración sobre el almacenamiento del tipo de valor como un detalle de implementación, lo que lleva a esta situación en la que tenemos un tipo con semántica de valor que no hereda de System.ValueType. Gracias Ben.)

Otros consejos

No es un tipo de valor porque el rendimiento (¡espacio y tiempo!) sería terrible si fuera un tipo de valor y su valor tuviera que copiarse cada vez que se pasa y se devuelve desde métodos, etc.

Tiene una semántica de valor para mantener al mundo cuerdo. ¿Te imaginas lo difícil que sería codificar si

string s = "hello";
string t = "hello";
bool b = (s == t);

establece b para ser false? Imagine lo difícil que sería codificar casi cualquier aplicación.

La distinción entre tipos de referencia y tipos de valor es básicamente una compensación de rendimiento en el diseño del lenguaje. Los tipos de referencia tienen algunos gastos generales en la construcción y destrucción y la recolección de basura, ya que se crean en el montón. Por otro lado, los tipos de valor tienen una sobrecarga en las llamadas a métodos (si el tamaño de los datos es mayor que un puntero), porque todo el objeto se copia en lugar de solo un puntero. Debido a que las cadenas pueden ser (y típicamente son) mucho más grandes que el tamaño de un puntero, están diseñadas como tipos de referencia. Además, como señaló Servy, el tamaño de un tipo de valor debe conocerse en el momento de la compilación, que no siempre es el caso de las cadenas.

La cuestión de la mutabilidad es un tema aparte. Tanto los tipos de referencia como los tipos de valor pueden ser mutables o inmutables. Sin embargo, los tipos de valor suelen ser inmutables, ya que la semántica para los tipos de valor mutable puede ser confusa.

Los tipos de referencia son generalmente mutables, pero pueden diseñarse como inmutables si tiene sentido. Las cadenas se definen como inmutables porque hacen posibles ciertas optimizaciones. Por ejemplo, si el mismo literal de cadena aparece varias veces en el mismo programa (que es bastante común), el compilador puede reutilizar el mismo objeto.

Entonces, ¿por qué es " == " sobrecargado para comparar cadenas de texto? Porque es la semántica más útil. Si dos cadenas son iguales por texto, pueden o no ser la misma referencia de objeto debido a las optimizaciones. Por lo tanto, comparar referencias es bastante inútil, mientras que comparar texto casi siempre es lo que desea.

Hablando de manera más general, Strings tiene lo que se denomina semántica de valor . Este es un concepto más general que los tipos de valor, que es un detalle de implementación específico de C #. Los tipos de valor tienen semántica de valor, pero los tipos de referencia también pueden tener semántica de valor. Cuando un tipo tiene semántica de valor, realmente no se puede saber si la implementación subyacente es un tipo de referencia o un tipo de valor, por lo que puede considerar que es un detalle de implementación.

Esta es una tardía respuesta a una vieja pregunta, pero todas las otras respuestas son falta el punto, que es que .NETA no tienen los medicamentos genéricos hasta que .NET 2.0 en el año 2005.

String es un tipo de referencia en lugar de un tipo de valor porque es de crucial importancia para Microsoft para asegurarse de que las cadenas pueden ser almacenados de la manera más eficiente en la no-colecciones genéricas, tales como System.Collection.ArrayList.

Para almacenar un valor de tipo en una colección no genérica requiere de una especial conversión al tipo de object que se llama boxeo.Cuando el CLR cuadros de un tipo de valor, se ajusta el valor en el interior de un System.Object y la almacena en el montón administrado.

Leer el valor de la colección requiere la inversa de la operación, que se llama unboxing.

Ambos boxing y unboxing no tienen costo insignificante:el boxeo requiere una asignación adicional, unboxing requiere la comprobación de tipos.

Algunas de las respuestas afirman incorrectamente que string nunca podría haber sido implementado como un tipo de valor debido a que su tamaño es variable.En realidad es fácil de implementar cadena como una longitud fija de estructura de datos, usando una Pequeña Cadena de la estrategia de Optimización:las cadenas se almacenan directamente en memoria como una secuencia de caracteres Unicode, excepto para las grandes cadenas que se almacenan como un puntero a un buffer externo.Ambas representaciones pueden ser diseñados para tener la misma longitud fija, es decir,el tamaño de un puntero.

Si genéricos había existido desde el primer día, supongo que tiene la cadena como un tipo de valor probablemente habría sido una mejor solución, con más simples semántica, mejor uso de la memoria y la mejor de caché de la localidad.Un List<string> contiene sólo pequeñas cadenas de caracteres podría haber sido un único bloque contiguo de memoria.

No solo las cadenas son tipos de referencia inmutables. Delegados multidifusión también. Por eso es seguro escribir

protected void OnMyEventHandler()
{
     delegate handler = this.MyEventHandler;
     if (null != handler)
     {
        handler(this, new EventArgs());
     }
}

Supongo que las cadenas son inmutables porque este es el método más seguro para trabajar con ellas y asignar memoria. ¿Por qué no son tipos de valor? Los autores anteriores tienen razón sobre el tamaño de la pila, etc. También agregaría que hacer que las cadenas sean tipos de referencia permiten ahorrar en el tamaño del ensamblaje cuando se usa la misma cadena constante en el programa. Si define

string s1 = "my string";
//some code here
string s2 = "my string";

Lo más probable es que ambas instancias de " my string " constante se asignará en su ensamblaje solo una vez.

Si desea administrar cadenas como el tipo de referencia habitual, coloque la cadena dentro de un nuevo StringBuilder (cadena). O use MemoryStreams.

Si va a crear una biblioteca, donde espera que se pasen cadenas enormes en sus funciones, defina un parámetro como StringBuilder o Stream.

Además, la forma en que se implementan las cadenas (diferentes para cada plataforma) y cuándo comienza a unirlas. Como usar un StringBuilder. Le asigna un búfer para que copie, una vez que llegue al final, le asigna aún más memoria, con la esperanza de que si realiza una gran concatenación no se verá obstaculizado.

¿Quizás Jon Skeet puede ayudarnos aquí?

Es principalmente un problema de rendimiento.

Hacer que las cadenas se comporten como un tipo de valor ayuda al escribir código, pero tenerlo como un tipo de valor supondría un gran impacto en el rendimiento.

Para una mirada en profundidad, eche un vistazo a un buen artículo en cadenas en el marco .net.

¿Cómo puede saber que string es un tipo de referencia? No estoy seguro de que importe cómo se implementa. Las cadenas en C # son inmutables precisamente para que no tenga que preocuparse por este problema.

En realidad, las cadenas tienen muy pocas semejanzas con los tipos de valor. Para empezar, no todos los tipos de valores son inmutables, puede cambiar el valor de un Int32 todo lo que quiera y seguiría siendo la misma dirección en la pila.

Las cadenas son inmutables por una muy buena razón, no tiene nada que ver con que sea un tipo de referencia, pero tiene mucho que ver con la administración de memoria. Es más eficiente crear un nuevo objeto cuando cambia el tamaño de la cadena que cambiar las cosas en el montón administrado. Creo que estás mezclando tipos de valor / referencia y conceptos de objetos inmutables.

Hasta " == " ;: Como dijiste " == " es una sobrecarga del operador, y nuevamente se implementó por una muy buena razón para hacer que el marco sea más útil cuando se trabaja con cadenas.

En palabras muy simples, cualquier valor que tenga un tamaño definido puede tratarse como un tipo de valor.

No es tan simple como las cadenas están formadas por matrices de caracteres. Miro las cadenas como matrices de caracteres []. Por lo tanto, están en el montón porque la ubicación de la memoria de referencia se almacena en la pila y apunta al comienzo de la ubicación de la memoria de la matriz en el montón. El tamaño de la cadena no se conoce antes de que se asigne ... perfecto para el montón.

Es por eso que una cadena es realmente inmutable porque cuando la cambia, incluso si es del mismo tamaño, el compilador no lo sabe y tiene que asignar una nueva matriz y asignar caracteres a las posiciones en la matriz. Tiene sentido si piensas en las cadenas como una forma en que los lenguajes te protegen de tener que asignar memoria sobre la marcha (lee C como programación)

A riesgo de obtener otro voto negativo misterioso ... el hecho de que muchos mencionen la pila y la memoria con respecto a los tipos de valores y los tipos primitivos se debe a que deben caber en un registro en el microprocesador. No puede empujar o hacer estallar algo hacia / desde la pila si toma más bits de los que tiene un registro ... las instrucciones son, por ejemplo, & Quot; pop eax & Quot; - porque eax tiene 32 bits de ancho en un sistema de 32 bits.

Los tipos primitivos de coma flotante son manejados por la FPU, que tiene 80 bits de ancho.

Todo esto se decidió mucho antes de que existiera un lenguaje OOP para ofuscar la definición de tipo primitivo y supongo que tipo de valor es un término que se ha creado específicamente para lenguajes OOP.

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