c# - tipos - variable int
En C#, ¿por qué String es un tipo de referencia que se comporta como un tipo de valor? (12)
Una cadena es un tipo de referencia, aunque tiene la mayoría de las características de un tipo de valor, como ser inmutable y tener == sobrecargado para comparar el texto en lugar de asegurarse de que hagan referencia al mismo objeto.
¿Por qué la cadena no es solo un tipo de valor?
¿Cómo se puede decir que la 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.
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 valor y los tipos primitivos se debe a que deben encajar en un registro en el microprocesador. No puede empujar o desplegar algo desde / hacia la pila si toma más bits de los que tiene un registro ... las instrucciones son, por ejemplo, "pop eax", porque eax tiene 32 bits de ancho en un sistema de 32 bits.
Los tipos primitivos de punto 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 asumo que el tipo de valor es un término que se ha creado específicamente para los idiomas OOP.
Además, la forma en que se implementan las cadenas (diferente para cada plataforma) y cuando comienza a unirlas. Como usar un StringBuilder
. Le asigna un búfer para que lo copie, una vez que llegue al final, le asignará aún más memoria, con la esperanza de que, si lo hace, una gran concatenación no se verá obstaculizada.
Tal vez Jon Skeet puede ayudar aquí?
En palabras muy simples, cualquier valor que tenga un tamaño definido puede tratarse como un tipo de valor.
En realidad, las cadenas tienen muy pocas similitudes con los tipos de valor. Para empezar, no todos los tipos de valor son inmutables, puede cambiar el valor de un Int32 todo lo que quiera y seguirá siendo la misma dirección en la pila.
Las cadenas son inmutables por una muy buena razón, no tiene nada que ver con ser un tipo de referencia, pero tiene mucho que ver con la administración de la memoria. Es simplemente 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.
En cuanto a "==": Como usted dijo, "==" 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.
Es principalmente un problema de rendimiento.
El hecho de que las cadenas se comporten como LIKE el tipo de valor ayuda a escribir el código, pero si es un tipo de valor sería un gran impacto en el rendimiento.
Para una mirada en profundidad, eche un vistazo a un buen artículo sobre cadenas en el marco .net.
Esta es una respuesta tardía a una pregunta anterior, pero a todas las demás les falta el punto, que es que .NET no tuvo genéricos hasta .NET 2.0 en 2005.
String
es un tipo de referencia en lugar de un tipo de valor porque era de vital importancia para Microsoft garantizar que las cadenas pudieran almacenarse de la manera más eficiente en colecciones no genéricas , como System.Collection.ArrayList
.
El almacenamiento de un tipo de valor en una colección no genérica requiere una conversión especial al object
tipo que se llama boxeo. Cuando el CLR introduce un tipo de valor, ajusta el valor dentro de un objeto System.Object
y lo almacena en el montón administrado.
Leer el valor de la colección requiere la operación inversa que se llama unboxing.
Tanto el boxeo como el unboxing tienen un costo no despreciable: el boxeo requiere una asignación adicional, el unboxing requiere la verificación de tipos.
Algunas respuestas afirman incorrectamente que la string
nunca podría haberse implementado como un tipo de valor porque su tamaño es variable. En realidad, es fácil implementar la cadena como una estructura de datos de longitud fija utilizando una estrategia de optimización de cadena pequeña: las cadenas se almacenarán en la memoria directamente como una secuencia de caracteres Unicode, excepto las cadenas grandes que se almacenarán como un puntero a un búfer externo. Ambas representaciones pueden diseñarse para tener la misma longitud fija, es decir, el tamaño de un puntero.
Si los genéricos hubieran existido desde el primer día, supongo que tener una cadena como tipo de valor probablemente hubiera sido una mejor solución, con una semántica más simple, un mejor uso de la memoria y una mejor ubicación de caché. Una List<string>
contiene solo cadenas pequeñas podría haber sido un único bloque de memoria contiguo.
La distinción entre tipos de referencia y tipos de valor es básicamente un compromiso de rendimiento en el diseño del lenguaje. Los tipos de referencia tienen cierta sobrecarga en la construcción y la destrucción y la recolección de basura, porque se crean en el montón. Por otro lado, los tipos de valor tienen una sobrecarga en las llamadas de método (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 ser conocido en el momento de la compilación, lo 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 son generalmente inmutables, ya que la semántica para tipos de valores mutables puede ser confusa.
Los tipos de referencia generalmente son mutables, pero pueden diseñarse como inmutables si tiene sentido. Las cadenas se definen como inmutables porque hacen posibles ciertas optimizaciones. Por ejemplo, si la misma cadena literal aparece varias veces en el mismo programa (lo cual es bastante común), el compilador puede reutilizar el mismo objeto.
Entonces, ¿por qué se sobrecarga "==" para comparar cadenas por 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 es casi siempre lo que se 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 una semántica de valor, pero los tipos de referencia también pueden tener una semántica de valor. Cuando un tipo tiene una semántica de valor, realmente no puede saber si la implementación subyacente es un tipo de referencia o valor, por lo que puede considerar ese detalle de implementación.
Las cadenas no son tipos de valor, ya que pueden ser enormes y deben almacenarse en el montón. Los tipos de valor están (en todas las implementaciones de CLR hasta el momento) almacenados en la pila. Las cadenas de asignación de pila romperían todo tipo de cosas: la pila es solo de 1 MB para 32 bits y 4 MB para 64 bits, tendría que marcar cada cadena, incurrir en una penalización de copia, no podría internar cadenas, y el uso de memoria sería globo, etc ...
(Edición: Se agregó una aclaración sobre el almacenamiento de tipo de valor como un detalle de implementación, lo que nos lleva a esta situación en la que tenemos un tipo con valores semánticos que no se heredan de System.ValueType. Gracias Ben.)
No es tan simple como las cadenas están formadas por matrices de caracteres. Veo 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 asignarse ... perfecto para el montón.
Es por eso que una cadena es realmente inmutable porque cuando la cambias, 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 piensa en las cadenas como una forma en que los idiomas lo protegen de tener que asignar memoria sobre la marcha (lea la programación como C)
No es un tipo de valor porque el rendimiento (¡espacio y tiempo!) Sería terrible si se tratara de un tipo de valor y su valor tuviera que copiarse cada vez que se pasara y regresara de los métodos, etc.
Tiene valor semántico para mantener al mundo sano. ¿Te imaginas lo difícil que sería codificar si
string s = "hello";
string t = "hello";
bool b = (s == t);
establecer b
para ser false
? Imagina lo difícil que sería codificar cualquier aplicación.
No solo las cadenas son tipos de referencia inmutables. Los delegados multi-cast 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 al hacer cadenas que los tipos de referencia permiten ahorrar en el tamaño del conjunto cuando se utiliza la misma cadena constante en el programa. Si usted define
string s1 = "my string";
//some code here
string s2 = "my string";
Lo más probable es que ambas instancias de la constante "mi cadena" se asignen 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 s). O utilice MemoryStreams.
Si va a crear una biblioteca, donde espera que se pasen grandes cadenas en sus funciones, defina un parámetro como StringBuilder o como Stream.