¿Cuál es la diferencia entre un tipo de referencia y un tipo de valor en c #?

Un tipo me hizo esta pregunta hace unos meses y no pude explicarlo en detalle. ¿Cuál es la diferencia entre un tipo de referencia y un tipo de valor en C #?

Sé que los tipos de valores son int , bool , float , etc. y los tipos de referencia son delegate , interface , etc. ¿O esto también está mal?

¿Me puedes explicar de una manera profesional?

Sus ejemplos son un poco extraños, porque aunque int , bool y float son tipos específicos, las interfaces y los delegates son tipos de tipos, al igual que struct y enum son tipos de tipos de valores.

He escrito una explicación de los tipos de referencia y los tipos de valores en este artículo . Estaría feliz de expandir cualquier fragmento que encuentres confuso.

La versión “TL; DR” es pensar 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í misma. Para un tipo de referencia, el valor es una referencia que puede ser nula o puede ser una forma de navegar hacia un objeto que contiene la información.

Por ejemplo, piense en una variable como 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 indicaciones para llegar a mi casa. Esas instrucciones son el equivalente de una referencia. En particular, dos personas podían tener diferentes papeles que contenían las mismas instrucciones para 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 tenían fotografías separadas de mi casa en el papel, una persona que coloreara el papel no cambiaría el papel de la otra persona.

Tipo de valor:

Tiene algo de valor, no direcciones de memoria

Ejemplo:

Struct

Almacenamiento:

TL; DR : el valor de una variable se almacena donde sea que se decida. Las variables locales viven en la stack, por ejemplo, pero cuando se declara dentro de una clase como miembro, vive en el montón estrechamente acoplado con la clase en la que se declara.
Más tiempo : por lo tanto, los tipos de valores se almacenan donde sea que se declaren. Por ejemplo: un valor int dentro de una función como una variable local se almacenaría en la stack, mientras que un valor de int declarado como miembro en una clase se almacenaría en el montón con la clase en la que está declarado. Un valor tipo en una clase tiene un tipo de vida que es exactamente igual a la clase en la que está declarado, y casi no requiere trabajo del recolector de basura. Aunque es más complicado, me referiría al libro de @ JonSkeet ” C # In Depth ” o su artículo ” Memory in .NET ” para una explicación más concisa.

Ventajas:

Un tipo de valor no necesita recolección de basura adicional. Recoge la basura junto con la instancia en la que vive. Las variables locales en los métodos se limpian al salir del método.

Inconvenientes:

  1. Cuando se pasan grandes conjuntos de valores a un método, la variable de recepción realmente se copia, por lo que hay dos valores redundantes en la memoria.

  2. A medida que las clases se pierden, pierde todas las ventajas

Tipo de referencia:

Tiene una dirección de memoria de un valor, no de valor

Ejemplo:

Clase

Almacenamiento:

Almacenado en montón

Ventajas:

  1. Cuando pasa una variable de referencia a un método y lo 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.

  2. Cuando el tamaño de la variable es mayor, el tipo de referencia es bueno

  3. Como las clases vienen como variables de tipo de referencia, ofrecen reutilización, lo que beneficia a la progtwigción orientada a objetos

Inconvenientes:

Más referencia de trabajo al asignar y eliminar referencias al leer el valor. Sobrecarga de extra para el recolector de basura

Me resultó más fácil entender la diferencia de los dos si sabes cómo la computadora asigna los artículos en la memoria y sabe lo que es un puntero.

La referencia generalmente se asocia con un puntero. Lo que significa que la dirección de la memoria donde reside su variable realmente tiene otra dirección de memoria del objeto real en una ubicación de memoria diferente.

El ejemplo que estoy a punto de dar es extremadamente simplificado, así que tómalo con un grano de sal.

Imagine que la memoria de la computadora es un grupo de apartados de correos en una fila (comenzando con PO Box 0001 a PO Box n) que puede contener algo dentro de ella. Si los recuadros de correos no lo hacen por usted, intente con una tabla hash o diccionario o una matriz o algo similar.

Por lo tanto, cuando haces algo como:

var a = “Hola”;

la computadora hará lo siguiente:

  1. asignar memoria (digamos comenzando en la ubicación de memoria 1000 para 5 bytes) y poner H (en 1000), e (en 1001), l (en 1002), l (en 1003) y o (en 1004).
  2. asignar en algún lugar de la memoria (por ejemplo, en la ubicación 0500) y asignarlo como la variable a.
    Entonces es como un alias (0500 es a).
  3. asigne el valor en esa ubicación de memoria (0500) a 1000 (que es donde comienza la cadena Hello en la memoria). Por lo tanto, la variable a mantiene una referencia a la ubicación de la memoria de inicio real de la cadena “Hola”.

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:

  1. asignar una ubicación de memoria digamos a las 0500 y asignarla a la variable a (la misma cosa de alias)
  2. pon el valor 1 en él (en la ubicación de la memoria 0500).
    Tenga en cuenta que no estamos asignando memoria extra para mantener el valor real (1). Por lo tanto, a realmente mantiene el valor real y es por eso que se llama tipo de valor.

Esto es de un post mío de un foro diferente, hace unos 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 .net, y los ejemplos aún se mantienen.

También es importante recordar que dentro de .net, TODOS los tipos derivan técnicamente del tipo de objeto base. Los tipos de valores están diseñados para comportarse como tales, pero al final también heredan la funcionalidad del tipo de objeto base.

R. Los tipos de valores son solo eso: representan un área distinta en la memoria donde se almacena un VALOR discreto. Los tipos de valores tienen un tamaño de memoria fijo y se almacenan en la stack, que es una colección de direcciones de tamaño fijo.

Cuando haces una statement como esta:

 Dim A as Integer DIm B as Integer A = 3 B = A 

Usted ha hecho lo siguiente:

  1. Creó 2 espacios en la memoria suficientes para contener valores enteros de 32 bits.
  2. Coloca un valor de 3 en la asignación de memoria asignada a A
  3. Coloca un valor de 3 en la asignación de memoria asignada a B asignándole el mismo valor que el contenido en A.

El valor de cada variable existe discretamente en cada ubicación de memoria.

B. Los tipos de referencia pueden ser de varios tamaños. Por lo tanto, no pueden almacenarse en la “stack” (¿recuerdan que la stack es una colección de asignaciones de memoria de tamaño fijo?). Se almacenan en el “Hep Managed”. Los punteros (o “referencias”) para cada elemento en el montón gestionado se mantienen en la stack (Como una dirección). Su código utiliza estos punteros en la stack 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” en 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 hace una statement como esta:

 Dim p1 As clsPerson p1 = New clsPerson p1.Name = "Jim Morrison" Dim p2 As Person p2 = p1 

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 lo esperaría intuitivamente. Creo que tanto p1 como p2 representan direcciones distintas en la stack. Sin embargo, ahora que ha asignado p2 el valor de p1, tanto p1 como p2 apuntan a la MISMA UBICACIÓN en el montón administrado.

Ahora, CONSIDERA ESTA situación:

 Dim p1 As clsPerson Dim p2 As clsPerson p1 = New clsPerson p1.Name = "Jim Morrison" p2 = p1 p2.Name = "Janis Joplin" 

En esta situación, ha creado una nueva instancia de la persona Clase en el Heap gestionado con un puntero p1 en la Pila que hace referencia al objeto, y le ha asignado a la Propiedad del Nombre de la instancia del objeto un valor de “Jim Morrison” nuevamente. A continuación, creó otro puntero p2 en la stack, y lo apuntó a la misma dirección en el montón administrado a la que hace referencia p1 (cuando hizo la asignación p2 = p1).

Aquí viene el giro. Cuando asigna a la propiedad Name de p2 el valor “Janis Joplin” está cambiando la propiedad Name para el objeto REFERENCED tanto para p1 como para p2, de modo que, si ejecutó el siguiente código:

 MsgBox(P1.Name) 'Will return "Janis Joplin" MsgBox(p2.Name) 'will ALSO return "Janis Joplin"Because both variables (Pointers on the Stack) reference the SAME OBJECT in memory (an Address on the Managed Heap). 

¿Eso tiene sentido?

Último. Si haces esto:

 DIm p1 As New clsPerson Dim p2 As New clsPerson p1.Name = "Jim Morrison" p2.Name = "Janis Joplin" 

Ahora tiene dos Objetos Persona distintos. Sin embargo, en el momento en que haces ESTO nuevamente:

 p2 = p1 

Ahora ha señalado ambos a “Jim Morrison”. (No estoy exactamente seguro de lo que le sucedió al Objeto en el Heap al que hace referencia p2 … CREO que ahora se ha salido del scope. Esta es una de esas áreas en las que, afortunadamente, alguien me puede aclarar …). -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:

 p2.Name = "Jimi Hendrix" MsgBox(p1.Name) MsgBox(p2.Name) 

Ambos msgBoxes ahora devolverán “Jimi Hendrix”

Esto puede ser bastante confuso por un tiempo, y lo diré una vez más, es posible que tenga algunos de los detalles incorrectos.

Buena suerte, y espero que otros que saben más que yo nos acompañen para ayudarnos a aclarar algo de esto. . .

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 que 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 valores son (int, bool, float) pero el tipo de referencia son (matriz, objetos de clase, cadena)

“Las variables que se basan en tipos de valores contienen valores directamente. Al asignar una variable de tipo de valor a otra, se copia el valor contenido. Esto difiere de la asignación de variables de tipo de referencia, que copia una referencia al objeto pero no el objeto mismo”. de la biblioteca de Microsoft.

Puede encontrar una respuesta más completa aquí y aquí .

A veces las explicaciones no ayudarán especialmente a los principiantes. Puede imaginar el tipo de valor como archivo de datos y tipo de referencia como un acceso directo a un archivo.

Por lo tanto, si copia una variable de referencia, solo copiará el enlace / puntero a una información real en algún lugar de la memoria. Si copia un tipo de valor, realmente clona los datos en la memoria.

Esto probablemente sea incorrecto en formas esotéricas, pero, para hacerlo simple:

Los tipos de valores son valores que se pasan normalmente “por valor” (para copiarlos). Los tipos de referencia se pasan “por referencia” (dando así un puntero al valor original). No hay ninguna garantía con el estándar .NET ECMA de dónde se guardan estas “cosas”. Podrías construir una implementación de .NET que no sea astackble, o una que sea inútil (la segunda sería muy compleja, pero probablemente podrías usar fibras y muchas stacks)

Las estructuras son de tipo de 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 el Tipo de valor, los “objetos referenciados” y las referencias (en C ++ se llamarían punteros a los objetos. En .NET son opacos. No sabemos cuáles son. Desde nuestro punto de vista, son “maneja” al objeto). Estos últimos son similares a los Tipos de valores (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 hay cero referencias, el GC probablemente lo recolectará.

En general (en la implementación “predeterminada” de .NET), el tipo de valor puede ir a la stack (si son campos locales) o al montón (si son campos de una clase, si son variables en una función de iterador, si son variables a las que hace referencia un cierre, si son variables en una función asíncrona (utilizando el nuevo CTP asincrónico) …). El valor referenciado solo puede ir al montón. Las referencias usan las mismas reglas que los tipos de valores.

En los casos de Tipo de valor que van en el montón porque están en una función de iterador, una función asíncrona, o se hace referencia a un cierre, si mira el archivo comstackdo verá que el comstackdor creó una clase para poner estas variables y la clase se genera 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 “correcta” “académica” “correcta”, lea ESTE:

http://blogs.msdn.com/b/ericlippert/archive/2010/09/30/the-truth-about-value-types.aspx

¡Son 15 minutos que lo estoy buscando! Es mejor que las versiones msdn, porque es un artículo condensado “listo para usar”.

La forma más simple de pensar en tipos de referencia es considerarlos como “ID de objeto”; las únicas cosas que uno puede hacer con un ID de objeto son crear uno, copiar uno, consultar o manipular el tipo de uno, o comparar dos para la igualdad. Un bash de hacer cualquier otra cosa con un ID de objeto se considerará una forma abreviada de hacer la acción indicada con el objeto al que hace referencia ese ID.

Supongamos que tengo dos variables X e Y de tipo Car: un tipo de referencia. Y pasa a tener “ID de objeto n. ° 19531”. Si digo “X = Y”, eso causará que X contenga “object ID # 19531”. Tenga en cuenta que ni X ni Y tienen un automóvil. 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 supongamos que digo X.Color = Colors.Blue. Dicha statement se considerará como una instrucción para ir a buscar “objeto ID # 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 statement en realidad no afecta a X o Y, porque ambos todavía se refieren a “ID de objeto # 19531”, que sigue siendo el mismo automóvil que Siempre ha sido.

Los tipos de variables y el valor de referencia son fáciles de aplicar y se aplican bien 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 envía parámetros por valor, por referencia, en nuestros métodos y funciones, y cómo hacer el tratamiento correcto de los pasajes de estos valores.

Leer este artículo Tipo de variable Valor y referencia en C #

Supongamos que v es una expresión / variable de tipo valor, r es una expresión / variable de tipo de referencia

  x = v update(v) //x will not change value. x stores the old value of v x = r update(r) //x now refers to the updated r. x only stored a link to r, //and r can change but the link to it doesn't . 

Entonces, una variable de tipo valor almacena el valor real (5 o “h”). Un varaible de tipo de referencia solo almacena un enlace a un cuadro metafórico donde está el valor.

En pocas palabras, los tipos de valores pasan por su valor y los tipos de referencia por su referencia (dirección de memoria).

Esto significa que los cambios realizados en los parámetros de tipo de valor (los parámetros formales) dentro de un método llamado no se reflejarán en los valores de donde se llamó al método (los parámetros reales).

Pero los cambios realizados a los parámetros de referencia dentro de un método llamado se reflejarán en los cambios en las variables declaradas en el método de llamada.

Esa es una breve explicación. Consulte aquí para comprender en detalle los tipos de valores, los tipos de referencia y los tipos de valores frente al tipo de referencia.

Tipo de valor:

  • Tamaño de la memoria fija.

  • Almacenado en la memoria de stack.

  • Mantiene el valor real.

    Ex. int, char, bool, etc …

Tipo de referencia:

  • No memoria fija.

  • Almacenado en la memoria Heap.

  • Mantiene la dirección de memoria del valor real.

    Ex. cadena, matriz, clase, etc.

No hay una sola diferencia entre los tipos de valores y los tipos de referencia, hay muchos pequeños detalles que se especifican 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. Los estándares ISO cuestan dinero.

Una diferencia es que los tipos de valores pueden estar encuadrados, pero los tipos de referencia generalmente no pueden serlo. Hay excepciones, pero son bastante técnicas.

Los tipos de valor no pueden tener constructores de instancia sin parámetro o finalizadores y no pueden referirse a sí mismos. Referirse a sí mismos significa, por ejemplo, que si hay un tipo de valor Nodo, entonces un miembro de Nodo no puede ser un Nodo . Creo que hay otros requisitos / limitaciones en las especificaciones, pero si es así, entonces no están reunidos en un solo lugar.