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

100

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, interfaceetc. O esto es incorrecto, también?

¿Me lo puedes explicar de forma profesional?

tugberk
fuente
3
Como pequeña nota, creo que la pregunta se hace sobre C #, pero en realidad se trata de C # + .NET. No puede analizar C # sin analizar .NET. No volveré a plantear la pregunta porque podría haber algunos puntos en analizar uno sin analizar el otro (iteradores y cierres, te estoy mirando)
xanatos
@xanatos, lo más apropiado es una pregunta sobre la CLI que C #, VB.Net y Net tienen en común. Debería haber una etiqueta para CLI, pero CLI se toma para otra cosa. Existe CLR pero eso es una implementación, no un estándar.
user34660

Respuestas:

172

Sus ejemplos son un poco extraños porque mientras int, booly floatson tipos específicos, las interfaces y los delegados son tipos de tipos, al igual que structy enumson 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.

Jon Skeet
fuente
2
Es importante tener en cuenta que hay tres tipos primarios distintos de semántica que una cosa puede ofrecer: semántica inmutable, semántica de valor mutable y semántica de referencia mutable. Conceptualmente, el tipo de semántica que implementa una cosa es ortogonal a si se almacena como un objeto de pila independiente o como una variable / campo (estructura). En la práctica, mientras que las estructuras que no exponen sus campos pueden implementar cualquier tipo de semántica, el hecho de que .net permita el intercambio promiscuo de referencias del montón significa que los objetos del montón no pueden implementar la semántica de valores mutables.
supercat
No entendí esta parte - 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?
RBT
2
@RBT: los tipos de datos no solo están separados en "tipo de referencia" y "tipo de valor". También están separados en "clase, estructura, enumeración, delegado, interfaz". intes una estructura, stringes una clase, Actiones 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.
Jon Skeet
@JonSkeet Posiblemente la respuesta en esta publicación sea ​​un poco engañosa.
RBT
@RBT: Yo diría que está algo mal redactado, pero no horrible.
Jon Skeet
26

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: intel valor de an dentro de una función como variable local se almacenaría en la pila, mientras que intel 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:

  1. 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.

  2. 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:

  1. 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.

  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, 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.

Durai Amuthan.H
fuente
5
No es necesariamente cierto que los tipos de referencia se almacenen en el montón y los tipos de valor se almacenen en la pila. Lea yoda.arachsys.com/csharp/memory.html si desea obtener más información.
Rhys
1
Hay muchos malentendidos en esta respuesta. Lea Jeff Richters CLR a través de C #. Los tipos de valor se almacenan en la pila de subprocesos y no están sujetos a la recolección de basura (GC); no tienen nada que ver con GC. Los tipos de referencia se almacenan en el montón administrado y, por lo tanto, están sujetos a GC. Si un tipo de referencia tiene una referencia raíz, no se puede recopilar y se promueve a las generaciones 0, 1 y 2. Si no tiene una referencia raíz, se puede recolectar basura y luego pasa por este proceso llamado Resurrección donde es asesinado y devuelto a la vida y finalmente recogido.
Jeremy Thompson
13

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:

  1. asigne memoria (digamos comenzando en la ubicación de memoria 1000 por 5 bytes) y ponga H (en 1000), e (en 1001), l (en 1002), l (en 1003) yo (en 1004).
  2. asignar en algún lugar de la memoria (digamos en la ubicación 0500) y asignarlo como la variable a.
    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 Hola en la memoria). Por lo tanto, la variable a tiene una referencia a la ubicación de la memoria inicial 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. asigne una ubicación de memoria, digamos a las 0500 y asígnela a la variable a (el mismo alias)
  2. ponga el valor 1 en él (en la ubicación de memoria 0500).
    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.
Jimmy Chandra
fuente
1
Puede estar interesado en blogs.msdn.com/b/ericlippert/archive/2009/02/17/…
Jon Skeet
@Jon, bueno, eso invalida lo que estaba diciendo, LOL. Pero, como dije, es enormemente simplificado comprender algo entre los dos tipos que, en mi caso, me parecieron útiles. Al menos así es como lo imaginé en mi mente :).
Jimmy Chandra
8

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:

Dim A as Integer
DIm B as Integer

A = 3
B = A 

Ha hecho lo siguiente:

  1. Se crearon 2 espacios en la memoria suficientes para contener valores enteros de 32 bits.
  2. Colocó un valor de 3 en la asignación de memoria asignada a A
  3. Colocó 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 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:

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 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:

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 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:

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 vuelva a hacer ESTO:

p2 = p1

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:

p2.Name = "Jimi Hendrix"

MsgBox(p1.Name)
MsgBox(p2.Name)

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. . .

XIV Soluciones
fuente
No sé por qué no recibió ningún voto a favor. Buena respuesta, me ayudó a entender con ejemplos claros y sencillos.
Harry
En cuanto a los conceptos de tipo de valor frente a tipo de referencia son uniformes en todo .net, en realidad se definen en la especificación Common Language Infrastructure (CLI), el estándar Ecma 335 (también un estándar ISO). Ese es el estándar para la parte estándar de .Net. El estándar Ecma 334 (también un estándar ISO) es el lenguaje C # y establece explícitamente que las implementaciones de C # deben depender de la CLI o admitir una forma alternativa de obtener las características CLI mínimas requeridas por este estándar C # . Sin embargo, VB.Net no es un estándar, es propiedad de Microsoft.
user34660
5

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)

Mohamed Elmasry
fuente
2

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 ...

Dhinagaran P
fuente
1

"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í .

Lucas S.
fuente
1
No me gusta esa explicación porque parece que la asignación funciona de manera diferente para los tipos de referencia y los tipos de valores. No es así. En ambos casos, hace que el valor de la variable "objetivo" sea igual a la expresión: el valor se copia. La diferencia está en cuál es ese valor: para los tipos de referencia, el valor que se copia es una referencia. Sin embargo, ese sigue siendo el valor de la variable.
Jon Skeet
Estoy de acuerdo contigo y ya sabía que podría ser diferente, como puedes leer en este artículo . Pero, solo estoy repasando la guía de Microsoft sobre el tema y también sobre cómo suele leer en los libros. ¡Por favor no me culpes! :)
Lucas S.
Oh, claro ... hay muchas partes de la documentación de MSDN donde se pueden encontrar fallas :)
Jon Skeet
1

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.

Nime Cloud
fuente
0

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".

xanatos
fuente
1
Está mal en más formas que esotéricas. Es fundamentalmente incorrecto, diría yo, porque los valores de tipo de referencia también se pasan por valor; es solo que el valor es una referencia, no un objeto. Consulte pobox.com/~skeet/csharp/parameters.html . Ah, y las variables locales también pueden terminar en el montón, por ejemplo, si se capturan o forman parte de un bloque iterador.
Jon Skeet
Los bloques de iteradores se convierten en clases, por lo que "detrás de ti" están "campos de una clase". Lo mismo para los cierres. Sí ... Olvidé escribir la distinción entre el "puntero" (la referencia) y el "puntiagudo"
xanatos
@xanatos: Claro, son campos de una clase después de la compilación, pero siguen siendo variables locales en el código fuente. Tampoco llamaría a las referencias en sí mismas "tipos de valor". Creo que sé de dónde vienes, pero no creo que sea una buena idea enturbiar las aguas de esta manera.
Jon Skeet
@jon Sí ... Son un tercer tipo, porque los punteros son "opacos" en .net y no derivan de ValueType. Pero son más similares a los tipos de valores que a las referencias. Puede "ref" y "eliminarlos". Tuve que enturbiar las aguas porque "alguien" tenía que manipular el funcionamiento de los iteradores.
xanatos
Mirando el artículo al que ahora apunto, encontré: "Hay tres tipos de valores: (1) instancias de tipos de valor, (2) instancias de tipos de referencia y (3) referencias. (El código en C # no puede manipular instancias de tipos de referencia directamente; siempre lo hace a través de una referencia. En el código inseguro, los tipos de puntero se tratan como tipos de valor con el fin de determinar los requisitos de almacenamiento de sus valores. ) ".
xanatos
0

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.

Super gato
fuente
0

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 #

Marcelo Cavalini
fuente
Este es un sitio de preguntas y respuestas solo en inglés, desafortunadamente = \. Sin embargo, gracias por intentar responder. Cree respuestas completas, con enlaces solo como ayuda (pero no como la respuesta completa sostenida). Por favor, eche un vistazo a cómo responder .
Jesse
0

Supongamos que ves una expresión / variable de tipo valor y res 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 .

Por 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.

Anas Elghafari
fuente
0

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:

static void Main()
{
    var size = sizeof(int);
    Console.WriteLine($"int size:{size}");
    size = sizeof(bool);
    Console.WriteLine($"bool size:{size}");
    size = sizeof(double);
    Console.WriteLine($"double size:{size}");
    size = sizeof(char);
    Console.WriteLine($"char size:{size}");
}

La salida mostrará el número de bytes asignados por cada variable.

int size:4
bool size:1
double size:8
char size:2

La información relacionada con cada tipo es:

  • El espacio de almacenamiento requerido.
  • Los valores máximo y mínimo. Por ejemplo, el tipo Int32 acepta valores entre 2147483648 y 2147483647.
  • El tipo base del que hereda.
  • La ubicación donde se asignará la memoria para las variables en tiempo de ejecución.
  • Los tipos de operaciones permitidas.
  • 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:

    namespace System
    {
        [ComVisible(true)]
        public struct Int32 : IComparable, IFormattable, IConvertible, IComparable<Int32>, IEquatable<Int32>
        {      
            public const Int32 MaxValue = 2147483647;     
            public const Int32 MinValue = -2147483648;
            public static Int32 Parse(string s, NumberStyles style, IFormatProvider provider);    
            ... 
        }  
    }

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:

struct Point
{
    public int X;
    public int Y;
};

Tipos de valor y referencia Podemos clasificar el tipo de C # en las siguientes categorías:

  • Tipos de valor
  • Tipos de referencia

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.

namespace System
{
    [ComVisible(true)]
    public struct Boolean : IComparable, IConvertible, IComparable<Boolean>, IEquatable<Boolean>
    {
        public static readonly string TrueString;
        public static readonly string FalseString;
        public static Boolean Parse(string value);
        ...
    }
}

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.

namespace System.Collections.Generic
{
    [DebuggerDisplay("Count = {Count}")]
    [DebuggerTypeProxy(typeof(Generic.Mscorlib_CollectionDebugView<>))]
    [DefaultMember("Item")]
    public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>
    {
        ...
        public T this[int index] { get; set; }
        public int Count { get; }
        public int Capacity { get; set; }
        public void Add(T item);
        public void AddRange(IEnumerable<T> collection);
        ...
    }
}

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.

string s1 = "Hello World";
GCHandle gch = GCHandle.Alloc(s1, GCHandleType.Pinned);
IntPtr pObj = gch.AddrOfPinnedObject();
Console.WriteLine($"Memory address:{pObj.ToString()}");

La salida será

Memory address:39723832

Referencias Documentación oficial: https://docs.microsoft.com/en-us/cpp/build/reference/stack-stack-allocations?view=vs-2019

Ivan Porta
fuente
-1

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.

user34660
fuente