Un tipo me hizo esta pregunta hace un par de meses y no pude explicarlo en detalle. ¿Cuál es la diferencia entre un tipo de referencia y un tipo de valor en C #?
Yo sé que los tipos de valor son int
, bool
, float
, etc y de referencia son los tipos delegate
, interface
etc. O esto es incorrecto, también?
¿Me lo puedes explicar de forma profesional?
c#
.net
value-type
reference-type
tugberk
fuente
fuente
Respuestas:
Sus ejemplos son un poco extraños porque mientras
int
,bool
yfloat
son tipos específicos, las interfaces y los delegados son tipos de tipos, al igual questruct
yenum
son tipos de tipos de valor.He escrito una explicación de los tipos de referencia y los tipos de valor en este artículo . Me complacerá ampliar cualquier parte que le resulte confusa.
La versión "TL; DR" es pensar en cuál es el valor de una variable / expresión de un tipo particular. Para un tipo de valor, el valor es la información en sí. Para un tipo de referencia, el valor es una referencia que puede ser nula o puede ser una forma de navegar a un objeto que contiene la información.
Por ejemplo, piense en una variable como si fuera una hoja de papel. Podría tener el valor "5" o "falso" escrito en él, pero no podría tener mi casa ... tendría que tener instrucciones para llegar a mi casa. Esas direcciones son el equivalente a una referencia. En particular, dos personas podrían tener diferentes hojas de papel con las mismas instrucciones para llegar a mi casa, y si una persona seguía esas instrucciones y pintaba mi casa de rojo, la segunda persona también vería ese cambio. Si ambos tuvieran imágenes separadas de mi casa en el papel, entonces una persona que coloreara su papel no cambiaría el papel de la otra persona en absoluto.
fuente
while int, bool and float are specific types, interfaces and delegates are kinds of type - just like struct and enum are kinds of value types
. ¿Qué quiere decir int, bool siendo tipos específicos? Todo en C #, por ejemplo, int, bool, float, class, interface, delegate es un tipo (tipo de datos para ser precisos). Los tipos de datos se segregan como 'Tipo de referencia' y 'Tipo de valor' en C #. Entonces, ¿por qué dices que int es un tipo específico pero que la interfaz es un tipo de tipo?int
es una estructura,string
es una clase,Action
es un delegado, etc. Tu lista de "int, bool, float, class, interface, delegate" es una lista que contiene diferentes tipos de cosas, de la misma manera que "10, int" es una lista que contiene diferentes tipos de cosas.Tipo de valor:
Tiene algún valor, no direcciones de memoria
Ejemplo:
Estructura
Almacenamiento:
TL; DR : El valor de una variable se almacena allí donde se declea. Las variables locales viven en la pila, por ejemplo, pero cuando se declaran dentro de una clase como miembro, viven en el montón estrechamente acopladas con la clase en la que están declaradas.
Más tiempo : por lo tanto, los tipos de valor se almacenan donde se declaran. Por ejemplo:
int
el valor de an dentro de una función como variable local se almacenaría en la pila, mientras queint
el valor de in declarado como miembro de una clase se almacenaría en el montón con la clase en la que se declara. Un tipo de valor en una clase tiene un tipo de vida que es exactamente el mismo que la clase en la que está declarada, por lo que el recolector de basura no requiere casi ningún trabajo. Sin embargo, es más complicado, me referiría al libro de @ JonSkeet " Memory in .NET C # In Depth ""para una explicación más concisa.Ventajas:
Un tipo de valor no necesita recolección de basura adicional. Obtiene la basura recolectada junto con la instancia en la que vive. Las variables locales en los métodos se limpian cuando el método abandona.
Inconvenientes:
Cuando se pasa un gran conjunto de valores a un método, la variable receptora realmente se copia, por lo que hay dos valores redundantes en la memoria.
A medida que se pierden clases, se pierden todos los beneficios oop.
Tipo de referencia:
Mantiene una dirección de memoria de un valor, no un valor
Ejemplo:
Clase
Almacenamiento:
Almacenado en el montón
Ventajas:
Cuando pasa una variable de referencia a un método y cambia, de hecho cambia el valor original, mientras que en los tipos de valor se toma una copia de la variable dada y ese valor se cambia.
Cuando el tamaño de la variable es mayor, el tipo de referencia es bueno
Como las clases vienen como variables de tipo de referencia, brindan capacidad de reutilización, beneficiando así la programación orientada a objetos
Inconvenientes:
Más trabajo de referencia al asignar y eliminar referencias al leer el valor. Sobrecarga adicional para el recolector de basura.
fuente
Me resultó más fácil entender la diferencia de los dos si sabe cómo la computadora asigna las cosas en la memoria y sabe qué es un puntero.
La referencia suele estar asociada a un puntero. Lo que significa que la dirección de memoria donde reside su variable en realidad contiene otra dirección de memoria del objeto real en una ubicación de memoria diferente.
El ejemplo que estoy a punto de dar está demasiado simplificado, así que tómalo con un grano de sal.
Imagínese que la memoria de la computadora es un grupo de apartados de correos en una fila (comenzando con el apartado de correos 0001 hasta el apartado de correos n) que pueden contener algo en su interior. Si los apartados de correos no lo hacen por usted, pruebe con una tabla hash, un diccionario o una matriz o algo similar.
Por lo tanto, cuando haces algo como:
var a = "Hola";
la computadora hará lo siguiente:
Es como un alias (0500 es a).
El tipo de valor mantendrá la cosa real en su ubicación de memoria.
Por lo tanto, cuando haces algo como:
var a = 1;
la computadora hará lo siguiente:
Observe que no estamos asignando memoria adicional para mantener el valor real (1). Por lo tanto, a en realidad tiene el valor real y por eso se llama tipo de valor.
fuente
Esto es de una publicación mía de un foro diferente, hace aproximadamente dos años. Si bien el lenguaje es vb.net (a diferencia de C #), los conceptos de tipo de valor frente a tipo de referencia son uniformes en todo .net, y los ejemplos siguen siendo válidos.
También es importante recordar que dentro de .net, TODOS los tipos derivan técnicamente del tipo base Object. Los tipos de valor están diseñados para comportarse como tales, pero al final también heredan la funcionalidad del tipo base Object.
R. Los tipos de valor son solo eso: representan un área distinta en la memoria donde se almacena un VALOR discreto. Los tipos de valor tienen un tamaño de memoria fijo y se almacenan en la pila, que es una colección de direcciones de tamaño fijo.
Cuando haces una declaración como esta:
Ha hecho lo siguiente:
El valor de cada variable existe discretamente en cada ubicación de la memoria.
B. Los tipos de referencia pueden ser de varios tamaños. Por lo tanto, no se pueden almacenar en la "Pila" (recuerde, ¿la pila es una colección de asignaciones de memoria de tamaño fijo?). Se almacenan en el "Montón administrado". Los punteros (o "referencias") a cada elemento del montón administrado se mantienen en la pila (como una dirección). Su código usa estos punteros en la pila para acceder a los objetos almacenados en el montón administrado. Entonces, cuando su código usa una variable de referencia, en realidad está usando un puntero (o "dirección" a una ubicación de memoria en el montón administrado).
Digamos que ha creado una clase llamada clsPerson, con una cadena Property Person.Name
En este caso, cuando haga una declaración como esta:
En el caso anterior, la propiedad p1.Name devolverá "Jim Morrison", como era de esperar. La propiedad p2.Name TAMBIÉN devolverá "Jim Morrison", como era de esperar intuitivamente. Creo que tanto p1 como p2 representan direcciones distintas en la pila. Sin embargo, ahora que ha asignado a p2 el valor de p1, tanto p1 como p2 apuntan a la MISMA UBICACIÓN en el montón administrado.
Ahora, considere ESTA situación:
En esta situación, ha creado una nueva instancia de la clase de persona en el montón administrado con un puntero p1 en la pila que hace referencia al objeto, y ha asignado a la propiedad de nombre de la instancia del objeto un valor de "Jim Morrison" nuevamente. A continuación, creó otro puntero p2 en la pila y lo apuntó a la misma dirección en el montón administrado que la referenciada por p1 (cuando realizó la asignación p2 = p1).
Aquí viene el giro. Cuando asigna la propiedad Nombre de p2 el valor "Janis Joplin", está cambiando la propiedad Nombre para el objeto REFERENCIADO por Ambos p1 y p2, de modo que, si ejecutó el siguiente código:
¿Eso tiene sentido?
Último. Si haces esto:
Ahora tiene dos Objetos Persona distintos. Sin embargo, en el momento en que vuelva a hacer ESTO:
Ahora ha señalado a ambos de nuevo a "Jim Morrison". (No estoy exactamente seguro de qué sucedió con el Objeto en el montón al que se hace referencia en p2... CREO que ahora se ha salido del alcance. Esta es una de esas áreas en las que, con suerte, alguien puede aclararme ...). -EDIT: CREO que esta es la razón por la que establecería p2 = Nothing O p2 = New clsPerson antes de realizar la nueva asignación.
Una vez más, si ahora haces ESTO:
Ambos msgBoxes ahora devolverán "Jimi Hendrix"
Esto puede ser bastante confuso por un momento, y diré una última vez, es posible que tenga algunos detalles incorrectos.
Buena suerte, y espero que otros que saben mejor que yo vendrán para ayudar a aclarar algo de esto. . .
fuente
tipo de datos de valor y tipo de datos de referencia
1) valor (contiene los datos directamente) pero referencia (se refiere a los datos)
2) en valor (cada variable tiene su propia copia) pero
en referencia (más de variable puede referirse a algunos objetos)
3) en valor (la variable de operación no puede afectar a otra variable) pero en referencia (la variable puede afectar a otra)
4) los tipos de valor son (int, bool, float) pero los tipos de referencia son (matriz, objetos de clase, cadena)
fuente
Tipo de valor:
Tamaño de memoria fijo.
Almacenado en la memoria de pila.
Mantiene el valor real.
Ex. int, char, bool, etc ...
Tipo de referencia:
No memoria fija.
Almacenado en la memoria del montón.
Mantiene la dirección de memoria del valor real.
Ex. cadena, matriz, clase, etc ...
fuente
"Las variables que se basan en tipos de valor contienen valores directamente. Asignar una variable de tipo de valor a otra copia el valor contenido. Esto difiere de la asignación de variables de tipo de referencia, que copia una referencia al objeto pero no al objeto en sí". de la biblioteca de Microsoft.
Puede encontrar una respuesta más completa aquí y aquí .
fuente
A veces, las explicaciones no ayudarán especialmente a los principiantes. Puede imaginar el tipo de valor como un archivo de datos y el tipo de referencia como un acceso directo a un archivo.
Entonces, si copia una variable de referencia, solo copia el enlace / puntero a un dato real en algún lugar de la memoria. Si copia un tipo de valor, realmente clona los datos en la memoria.
fuente
Esto probablemente esté mal en formas esotéricas, pero, para hacerlo simple:
Los tipos de valor son valores que se pasan normalmente "por valor" (por lo tanto, copiarlos). Los tipos de referencia se pasan "por referencia" (dando así un puntero al valor original). El estándar .NET ECMA no garantiza dónde se guardan estas "cosas". Podría crear una implementación de .NET que no tenga pila, o una que no tenga pila (la segunda sería muy compleja, pero probablemente podría hacerlo, utilizando fibras y muchas pilas)
Las estructuras son de tipo valor (int, bool ... son estructuras, o al menos se simulan como ...), las clases son de tipo de referencia.
Los tipos de valor descienden de System.ValueType. El tipo de referencia desciende de System.Object.
Ahora ... al final tienes Value Type, "objetos referenciados" y referencias (en C ++ se llamarían punteros a objetos. En .NET son opacos. No sabemos qué son. Desde nuestro punto de vista son "identificadores" del objeto). Estos últimos son similares a los tipos de valor (se pasan por copia). Entonces, un objeto está compuesto por el objeto (un tipo de referencia) y cero o más referencias a él (que son similares a los tipos de valor). Cuando no hay referencias, el GC probablemente las recopilará.
En general (en la implementación "predeterminada" de .NET), el tipo de valor puede ir a la pila (si son campos locales) o al montón (si son campos de una clase, si son variables en una función iteradora, si son variables referenciadas por un cierre, si son variables en una función asíncrona (usando el CTP asíncrono más nuevo) ...). El valor referenciado solo puede ir al montón. Las referencias usan las mismas reglas que los tipos de valor.
En los casos de Tipo de valor que van al montón porque están en una función de iterador, una función asíncrona o son referenciados por un cierre, si observa el archivo compilado, verá que el compilador creó una clase para poner estas variables , y la clase se construye cuando llamas a la función.
Ahora, no sé cómo escribir cosas largas y tengo mejores cosas que hacer en mi vida. Si desea una versión "precisa" "académica" "correcta", lea ESTO:
http://blogs.msdn.com/b/ericlippert/archive/2010/09/30/the-truth-about-value-types.aspx
¡Son 15 minutos lo estoy buscando! Es mejor que las versiones de msdn, porque es un artículo condensado "listo para usar".
fuente
La forma más sencilla de pensar en los tipos de referencia es considerarlos como "ID de objeto"; las únicas cosas que se pueden hacer con un ID de objeto son crear uno, copiar uno, consultar o manipular el tipo de uno o comparar dos por igualdad. Un intento de hacer cualquier otra cosa con un ID de objeto se considerará una forma abreviada de realizar la acción indicada con el objeto al que se refiere ese ID.
Supongamos que tengo dos variables X e Y de tipo Coche: un tipo de referencia. Y pasa a tener "ID de objeto # 19531". Si digo "X = Y", eso hará que X contenga "ID de objeto # 19531". Tenga en cuenta que ni X ni Y tienen coche. El automóvil, también conocido como "ID de objeto # 19531", se almacena en otro lugar. Cuando copié Y en X, todo lo que hice fue copiar el número de identificación. Ahora suponga que digo X.Color = Colors.Blue. Tal declaración será considerada como una instrucción para ir a buscar "ID de objeto # 19531" y pintarlo de azul. Tenga en cuenta que aunque X e Y ahora se refieren a un automóvil azul en lugar de uno amarillo, la declaración en realidad no afecta a X o Y, porque ambos todavía se refieren al "ID de objeto # 19531", que sigue siendo el mismo automóvil que Siempre ha sido.
fuente
Los tipos de variables y el valor de referencia son fáciles de aplicar y bien aplicados al modelo de dominio, facilitan el proceso de desarrollo.
Para eliminar cualquier mito sobre la cantidad de "tipo de valor", comentaré cómo se maneja esto en la plataforma. NET, específicamente en C # (CSharp) cuando se llama APIS y enviamos parámetros por valor, por referencia, en nuestros métodos, y funciones y como hacer el correcto tratamiento de los pasajes de estos valores.
Lea este artículo Valor de tipo de variable y referencia en C #
fuente
Supongamos que
v
es una expresión / variable de tipo valor yr
es una expresión / variable de tipo de referenciaPor tanto, una variable de tipo valor almacena el valor real (5 o "h"). Una variable de tipo de referencia solo almacena un enlace a un cuadro metafórico donde está el valor.
fuente
Antes de explicar los diferentes tipos de datos disponibles en C #, es importante mencionar que C # es un lenguaje fuertemente tipado. Esto significa que cada variable, constante, parámetro de entrada, tipo de retorno y en general cada expresión que se evalúa como un valor, tiene un tipo.
Cada tipo contiene información que será incrustada por el compilador en el archivo ejecutable como metadatos que serán utilizados por Common Language Runtime (CLR) para garantizar la seguridad del tipo cuando asigne y recupere memoria.
Si desea saber cuánta memoria asigna un tipo específico, puede usar el operador sizeof de la siguiente manera:
La salida mostrará el número de bytes asignados por cada variable.
La información relacionada con cada tipo es:
Los miembros (métodos, campos, eventos, etc.) contenidos por el tipo. Por ejemplo, si comprobamos la definición de tipo int, encontraremos la siguiente estructura y miembros:
Gestión de la memoria Cuando se ejecutan varios procesos en un sistema operativo y la cantidad de RAM no es suficiente para almacenarlo todo, el sistema operativo asigna partes del disco duro con la RAM y comienza a almacenar datos en el disco duro. El sistema operativo utilizará tablas específicas donde las direcciones virtuales se asignan a sus direcciones físicas correspondientes para realizar la solicitud. Esta capacidad para administrar la memoria se denomina memoria virtual.
En cada proceso, la memoria virtual disponible está organizada en las siguientes 6 secciones, pero por la relevancia de este tema, nos centraremos solo en la pila y el montón.
Pila La pila es una estructura de datos LIFO (último en entrar, primero en salir), con un tamaño que depende del sistema operativo (de forma predeterminada, para máquinas ARM, x86 y x64, Windows reserva 1 MB, mientras que Linux reserva de 2 MB a 8 MB según versión).
Esta sección de memoria es administrada automáticamente por la CPU. Cada vez que una función declara una nueva variable, el compilador asigna un nuevo bloque de memoria tan grande como su tamaño en la pila, y cuando la función termina, el bloque de memoria para la variable se desasigna.
Montón Esta región de memoria no es administrada automáticamente por la CPU y su tamaño es mayor que la pila. Cuando se invoca la nueva palabra clave, el compilador comienza a buscar el primer bloque de memoria libre que se ajuste al tamaño de la solicitud. y cuando lo encuentra, se marca como reservado mediante el uso de la función C incorporada malloc () y devuelve el puntero a esa ubicación. También es posible desasignar un bloque de memoria utilizando la función C incorporada free (). Este mecanismo provoca la fragmentación de la memoria y tiene que usar punteros para acceder al bloque correcto de memoria, es más lento que la pila para realizar las operaciones de lectura / escritura.
Tipos personalizados e integrados integrados Si bien C # proporciona un conjunto estándar de tipos integrados que representan números enteros, booleanos, caracteres de texto, etc., puede usar construcciones como struct, class, interface y enum para crear sus propios tipos.
Un ejemplo de tipo personalizado que utiliza la estructura de estructura es:
Tipos de valor y referencia Podemos clasificar el tipo de C # en las siguientes categorías:
Tipos de valor Los tipos de valor se derivan de la clase System.ValueType y las variables de este tipo contienen sus valores dentro de su asignación de memoria en la pila. Las dos categorías de tipos de valor son struct y enum.
El siguiente ejemplo muestra el miembro del tipo booleano. Como puede ver, no hay una referencia explícita a la clase System.ValueType, esto sucede porque esta clase es heredada por la estructura.
Tipos de referencia Por otro lado, los tipos de referencia no contienen los datos reales almacenados en una variable, sino la dirección de memoria del montón donde se almacena el valor. Las categorías de tipos de referencia son clases, delegados, matrices e interfaces.
En tiempo de ejecución, cuando se declara una variable de tipo de referencia, contiene el valor nulo hasta que se le asigna un objeto que ha sido creado usando las palabras clave new.
El siguiente ejemplo muestra los miembros del tipo genérico List.
En caso de que desee averiguar la dirección de memoria de un objeto específico, la clase System.Runtime.InteropServices proporciona una forma de acceder a los objetos administrados desde la memoria no administrada. En el siguiente ejemplo, usaremos el método estático GCHandle.Alloc () para asignar un identificador a una cadena y luego el método AddrOfPinnedObject para recuperar su dirección.
La salida será
Referencias Documentación oficial: https://docs.microsoft.com/en-us/cpp/build/reference/stack-stack-allocations?view=vs-2019
fuente
Hay muchos pequeños detalles de las diferencias entre los tipos de valores y los tipos de referencia que se establecen explícitamente en el estándar y algunos de ellos no son fáciles de entender, especialmente para los principiantes.
Consulte el estándar 33 de ECMA , Common Language Infrastructure (CLI) . La CLI también está estandarizada por ISO. Proporcionaría una referencia, pero para ECMA debemos descargar un PDF y ese enlace depende del número de versión. Las normas ISO cuestan dinero.
Una diferencia es que los tipos de valor se pueden encuadrar, pero los tipos de referencia generalmente no. Hay excepciones pero son bastante técnicas.
Los tipos de valor no pueden tener finalizadores o constructores de instancias sin parámetros y no pueden hacer referencia a sí mismos. Refiriéndose a sí mismos significa, por ejemplo, que si hay un tipo de valor Node , un miembro de Node no puede ser un Node . Creo que hay otros requisitos / limitaciones en las especificaciones, pero si es así, no están reunidos en un solo lugar.
fuente