Un breve resumen de los tipos de datos de Unity C# y la asignación de memoria

C# es un lenguaje fuertemente tipado y cada variable debe especificar un tipo de datos.

Los tipos de datos de C# se dividen en tipos de valor, tipos de referencia y tipos de puntero.

Los tipos de valores incluyen enteros, puntos flotantes, caracteres, booleanos, enumeraciones, etc.;
los tipos de referencia incluyen clases, interfaces, arreglos, delegados, cadenas, etc.

Almacenamiento de datos: pila y montón

Cuatro tipos de datos se colocan principalmente en la pila y el montón: Tipo de valor, Tipo de referencia, Puntero e Instrucción.

La pila es responsable de guardar la ruta de ejecución de nuestro código (o llamada), y el montón es responsable de guardar la ruta de los objetos (o datos, hablaremos mucho sobre el montón a continuación).

Piense en una pila como una pila de cajas de arriba hacia abajo. Cada vez que se llama a un método, registramos lo que sucede en la aplicación en un cuadro en la parte superior de la pila, y solo podemos usar el cuadro en la parte superior de la pila cada vez. Cuando se haya usado el cuadro en la parte superior de nuestra pila, o el método haya terminado de ejecutarse, descartaremos el cuadro y continuaremos usando el nuevo cuadro en la parte superior de la pila.

El principio de funcionamiento del montón es similar, pero la mayoría de las veces el montón se usa para contener información en lugar de la ruta de ejecución, por lo que se puede acceder al montón en cualquier momento. En comparación con las pilas, los montones no tienen restricciones de acceso, los montones son como ropa vieja sobre la cama, no nos tomamos el tiempo para clasificarlos, eso es porque siempre podemos encontrar una prenda que necesitamos, y las pilas son como zapatos apilados en un casillero Caja, solo podemos comenzar desde la caja superior hasta encontrar la correcta.

Si necesita obtener el tamaño exacto de un tipo o una variable en una plataforma específica, puede usar el método sizeof. La expresión sizeof(type) produce el tamaño de almacenamiento en bytes para almacenar el objeto o tipo.

1. Tipos de valor

Todos los tipos de valor de C# se derivan implícitamente de System.ValueType;
determine si es un tipo de valorType.IsValueType

——Tipos de valores integrados——

Los tipos de valor incluyen entero, punto flotante, carácter, booleano, enumeración, etc.

Las variables de tipo de valor se pueden asignar directamente a un valor. Desde la perspectiva del espacio de almacenamiento de la memoria, el valor del tipo de valor se almacena en la pila, y cada vez que se accede al valor, se operará en esta memoria.
inserte la descripción de la imagen aquí

Entero (incluido el carácter de un solo carácter)

Integer es el tipo que almacena enteros. De acuerdo con el rango de valores almacenados, C# divide los enteros en tipo byte, tipo corto, tipo int, tipo largo, etc., y define números con signo y sin signo respectivamente.

Los números con signo pueden representar números negativos, los números sin signo solo pueden representar números positivos

Los tipos sin firmar short, int y long tienen el nombre del tipo prefijado con el carácter u.

El tipo de byte es especial, almacena un número sin signo y su número con signo correspondiente es sbyte.

El tipo de carácter solo puede almacenar un carácter, ocupa dos bytes y puede almacenar un carácter chino.
El tipo de carácter está representado por la palabra clave char, y los caracteres almacenados en el tipo char deben estar entre comillas simples, como 'a', '中', etc.

punto flotante

El tipo de punto flotante se refiere al tipo decimal.El tipo de punto flotante tiene float, double y decimal en el lenguaje C#. float es un tipo de punto flotante de precisión simple, double es un tipo de punto flotante de precisión doble,

Además de los números enteros, los valores que también contienen partes fraccionarias son "tipos de coma flotante" (tipos de datos de coma flotante).

Tipo de alta precisión

decimal es un tipo de punto flotante de alta precisión.

Si el valor numérico a procesar requiere alta precisión, se puede usar decimal (número de punto flotante de alta precisión), que es más preciso que float (número de punto flotante) y double (número de punto flotante de doble precisión).

booleano

El tipo booleano se declara con bool, que tiene solo dos valores, verdadero y falso.

Cuando un valor tiene solo dos estados, se puede declarar como un tipo booleano, por ejemplo, si acepta el acuerdo, si compra un artículo, etc.

Los valores booleanos también se usan a menudo en declaraciones de juicio condicional, como juzgar si un valor es par, juzgar si una fecha es un día hábil, etc.

-- Tipo de valor definido por el usuario --

Además de los tipos básicos predefinidos por C#, hay dos tipos de valores personalizados, a saber, estructuras y enumeraciones.

Tipo de estructura (derivado de System.ValueType)

 public struct book
{
    
    
   public string bookname;
   public string bookno;
   public int bookwrite;
}

Usar la estructura también es muy simple, el código es el siguiente:

  Book book;
  book.bookname = "C#入门"
  book.bookno   = "www.Microsoft.com"
  book.bookwrite= "Microsoft"

Puede usar la nueva palabra clave para inicializar estructuras, y las estructuras también pueden usar constructores.

Tipo de enumeración (derivado de System.Enum)

Enumeración, la enumeración es un tipo entero definido por el usuario. La importancia de la enumeración es que se da cuenta mejor de la legibilidad del código y la reutilización de los datos. Imagine definir el color rojo en el sistema y usar color.red para comparar Es más fácil de entender o use 255255 para entender.Use la palabra clave enum para definir una enumeración.El ejemplo es el siguiente:

public enum booktype
{
    
    
   language =0,
   internet =1,
   novel    =2
}

También es muy simple de usar, el código es el siguiente:

   booktype booktype  =booktype.language;

2. Tipos de referencia

Matriz (derivada de System.Array)
tipo definido por el usuario:
Clase: clase (derivada de System.Object);
Interfaz: interfaz (la interfaz no es una "cosa", por lo que no hay dudas de dónde derivar.
Delegado: delegado (derivado de System.Delegate)
object (alias de System.Object)
String: string (alias de System.String).

Los tipos de referencia no contienen los datos reales almacenados en la variable, pero contienen una referencia a la variable.

Cuando se crea un tipo de referencia, primero se crea una variable de referencia en la pila, luego se crea el objeto en el montón y luego se asigna la primera dirección de la memoria donde se encuentra el objeto a la variable de referencia.

En otras palabras, una variable de referencia contiene una ubicación de memoria. Cuando se usan múltiples variables, los tipos de referencia pueden apuntar a la misma ubicación de memoria. Si una variable cambia los datos en una ubicación de memoria, otras variables reflejarán automáticamente este cambio de valor.

Array (derivado de System.Array)

Tipos de referencia definibles por el usuario

clase clase delegado delegado, interfaz interfaz

Tipo de objeto

El tipo de objeto es la clase base definitiva para todos los tipos de datos en C# Common Type System (CTS). Object es un alias para la clase System.Object. Por lo tanto, a un tipo de objeto se le puede asignar un valor de cualquier otro tipo (tipo de valor, tipo de referencia, tipo predefinido o tipo definido por el usuario). Sin embargo, antes de asignar el valor, se requiere una conversión de tipo.

Cuando un tipo de valor se convierte en un tipo de objeto, se denomina boxing; por otro lado, cuando un tipo de objeto se convierte en un tipo de valor, se denomina unboxing.

object obj;
obj = 100; // 这是装箱

tipo de cadena

El tipo String le permite asignar cualquier valor de cadena a una variable. El tipo String es un alias para la clase System.String. Se deriva del tipo de objeto. Un valor de tipo Cadena se puede asignar de dos formas: comillas y @comillas.

3. Tipos de punteros

Una referencia suele ser un puntero. No usaremos punteros explícitamente, son administrados por Common Language Runtime (CLR).

Un puntero (o referencia) es diferente de un tipo de referencia porque cuando decimos que algo es un tipo de referencia queremos decir que accedemos a él a través de un puntero. Un puntero es un espacio de memoria y apunta a otro espacio de memoria. Al igual que la pila y el montón, los punteros también ocupan espacio en la memoria, pero su valor es una dirección de memoria o nulo.

Asignación de memoria cuando los tipos están anidados

Lo que es más confuso es cuando los tipos de referencia contienen tipos de valor y cuando los tipos de valor contienen tipos de referencia.
Aquí hay algunas pautas, ¡asegúrese de entenderlas con ejemplos!

  1. Los tipos de referencia siempre se colocan en el montón.

  2. Los tipos de valor y los punteros siempre se colocan donde se declaran. (Esto es un poco más complicado y requiere saber cómo funciona la pila antes de saber dónde se declaró).

  3. Cuando los datos de tipo de valor se declaran en el cuerpo de un método (función), se colocan en la pila.

  4. Los datos de tipo de valor también se colocan a veces en el montón. Recuerde esta regla: los tipos de valor siempre se colocan donde se declaran. Si los datos de un tipo de valor se declaran fuera del cuerpo del método y existen en un tipo de referencia, serán reemplazados por el tipo de referencia en el montón.

--Ejemplo--

Enlace al ejemplo original

instancia de matriz

Considere una matriz:

int[] reference = new int[100];

Por definición, las matrices son tipos de referencia, por lo que, por supuesto, las matrices int son tipos de referencia (es decir, reference.GetType().IsValueType es falso).

Los elementos de la matriz int son todos enteros Por definición, int es un tipo de valor (es decir, referencia[i].GetType().IsValueType es verdadero). Entonces, ¿el elemento de tipo de valor en la matriz de tipo de referencia está en la pila o en el montón?

Si usa WinDbg para buscar la ubicación específica de la referencia [i] en la memoria, encontrará que no están en la pila, sino en el montón administrado.

En realidad, para matrices:

TestType[] testTypes = new TestType[100];

Si TestType es un tipo de valor, se asigna espacio de almacenamiento para 100 elementos de tipo de valor en el montón administrado a la vez, y estos 100 elementos se inicializan y almacenan automáticamente en esta memoria.

Si TestType es un tipo de referencia, primero se asignará espacio para testTypes en el montón administrado y ningún elemento se inicializará automáticamente en este momento (es decir, testTypes[i] son ​​todos nulos). Cuando haya código para inicializar un elemento en el futuro, el espacio de almacenamiento de este elemento de tipo de referencia se asignará en el montón administrado.

instancia de clase

//类
public class  ReferenceTypeClass
{
    
    
    private int  _valueTypeField;
    public  ReferenceTypeClass()
     {
    
    
         _valueTypeField = 0 ;
     }
    public void  Method()
     {
    
    
        int valueTypeLocalVariable = 0 ;
     }
}
ReferenceTypeClass referenceTypeClassInstance = new ReferenceTypeClass();
//Where is _valueTypeField?

referenceTypeClassInstance.Method();
//Where is valueTypeLocalVariable?

//结构体
public struct  ValueTypeStruct
{
    
    
    private object  _referenceTypeField;
    public void  Method()
     {
    
    
         _referenceTypeField = new object ();
        object referenceTypeLocalVariable = new object ();
     }
}
ValueTypeStruct valueTypeStructInstance = new  ValueTypeStruct();
valueTypeStructInstance.Method();
//Where is _referenceTypeField?And where is referenceTypeLocalVariable?

Solo mirando valueTypeStructInstance, esta es una instancia de estructura, que parece ser arrojada a la pila en una sola pieza. Pero el campo _referenceTypeField es un tipo de referencia y la variable local referenceTypeLocalVariable también es un tipo de referencia.

Tengo el mismo problema con referenceTypeClassInstance, referenceTypeClassInstance en sí mismo es un tipo de referencia y parece que debería implementarse en el montón administrado como un todo. Pero el campo _valueTypeField es un tipo de valor, y la variable local valueTypeLocalVariable también es un tipo de valor. ¿Están en la pila o en el montón administrado?

La regla es:

Un tipo de referencia se implementa en el montón administrado;
siempre se asigna un tipo de valor donde se declara: como campo, se almacena con la variable (instancia) a la que pertenece; como variable local, se almacena en la pila .
Analicemos el código anterior. Para instancias de tipo de referencia, es decir, referenceTypeClassInstance:

Desde el contexto, referenceTypeClassInstance es una variable local, por lo que se implementa en el montón administrado y se mantiene mediante una referencia en la pila; el
campo de tipo de valor _valueTypeField es parte de la instancia de tipo de referencia referenceTypeClassInstance, por lo que se implementa en la instancia administrada junto con con la instancia de tipo de referencia referenceTypeClassInstance en el montón (algo similar al caso de las matrices);
valueTypeLocalVariable es una variable local de tipo de valor, por lo que se implementa en la pila.
Y para instancias de tipo de valor, a saber, valueTypeStruct:

Según el contexto, la instancia de tipo de valor valueTypeStructInstance en sí misma es una variable local en lugar de un campo, por lo que se encuentra en la pila;
su campo de tipo de referencia _referenceTypeField no tiene el problema de seguir, debe implementarse en el montón administrado y retenida por una referencia (la referencia es parte de valueTypeStruct, en la pila);
su variable local de tipo de referencia, referenceTypeLocalVariable, aparentemente se implementa en el montón administrado y está retenida por una referencia en la pila.
Por lo tanto, simplemente decir "los tipos de valor se almacenan en la pila, los tipos de referencia se almacenan en el montón administrado" no es correcto. Debe analizarse caso por caso.

Supongo que te gusta

Origin blog.csdn.net/weixin_44394801/article/details/119791004
Recomendado
Clasificación