[Notas de estudio de C#] Tipo de valor (2)

Insertar descripción de la imagen aquí


Tipo de estructura estructural

Un tipo de estructura ("tipo de estructura" o "tipo de estructura") es un tipo de valor que encapsula datos y funciones relacionadas. Utilice la palabra clave struct para definir un tipo de estructura:

public struct Coords
{
    
    
    public Coords(double x, double y)
    {
    
    
        X = x;
        Y = y;
    }

    public double X {
    
     get; }
    public double Y {
    
     get; }

    public override string ToString() => $"({
      
      X}, {
      
      Y})";
}

Los tipos de estructuras tienen semántica de valor. Es decir, las variables de un tipo de estructura contienen instancias del tipo. De forma predeterminada, en la asignación, los valores de las variables se copian pasando argumentos a un método y devolviendo los resultados del método. Para las variables de tipo de estructura, se copian instancias del tipo.

Utilice readonlypalabras clave para garantizar que el estado de la estructura sea inmutable. Esto asegura que los miembros dentro de la estructura no modificarán el estado de la estructura misma. Precisamente por ser un tipo de valor se puede modificar, y no queremos que se modifique.

¿Por qué no se recomienda struct?

Aquí también señalamos por qué la clase suele ser mejor que la estructura, porque la estructura es un tipo de valor. Por un lado, la asignación de la estructura se logra copiando el valor de toda la estructura. Esto significa que cuando el valor de la estructura es grande, la operación de asignación requiere copiar más datos, lo que puede consumir mucha memoria y tiempo.

Por otro lado, las estructuras se pasan por valor cuando se pasan como parámetros a métodos. Esto significa que se pasa una copia de la estructura, en lugar de la instancia original de la estructura. Esto da como resultado modificaciones en la estructura dentro del método que no afectan la instancia original.

Por el contrario, utilizar una clase como tipo de referencia evita los problemas anteriores. La asignación y transferencia de objetos de clase solo implica la copia de referencias, no la copia del objeto completo. Esto evita el consumo innecesario de memoria y tiempo. Además, la transferencia de objetos de clase se realiza por referencia, lo que significa que las modificaciones al objeto dentro del método afectarán a la instancia original.

La esencia de todos los defectos es que la estructura es un tipo de valor, mientras que la clase es un tipo de referencia.


tipo de tupla

La función tupla proporciona una sintaxis concisa para agrupar múltiples elementos de datos en una estructura de datos liviana. El siguiente ejemplo demuestra cómo declarar una variable tupla, inicializarla y acceder a sus miembros de datos:

(double, int) t1 = (4.5, 3);
Console.WriteLine($"Tuple with elements {
      
      t1.Item1} and {
      
      t1.Item2}.");
// Output:
// Tuple with elements 4.5 and 3.

(double Sum, int Count) t2 = (4.5, 3);
Console.WriteLine($"Sum of {
      
      t2.Count} elements is {
      
      t2.Sum}.");
// Output:
// Sum of 3 elements is 4.5.

Para definir un tipo de tupla, debe especificar los tipos de todos sus miembros de datos o, alternativamente, puede especificar los nombres de los campos. Aunque no puede definir métodos en un tipo de tupla, puede utilizar los métodos proporcionados por .NET, como se muestra en el siguiente ejemplo:

(double, int) t = (4.5, 3);
Console.WriteLine(t.ToString());
Console.WriteLine($"Hash code of {
      
      t} is {
      
      t.GetHashCode()}.");
// Output:
// (4.5, 3)
// Hash code of (4.5, 3) is 718460086.

Los tipos de tupla se utilizan a menudo cuando las funciones aceptan múltiples valores de retorno. Si desea una estructura de datos mutable con métodos, las clases son mejores que las tuplas.


Tipos de valores que aceptan valores NULL

En las variables de tipo de valor, la mayoría de los valores no pueden ser nulos, por lo que podemos usar Nullable<T>o T?definir un tipo de valor que acepte valores NULL. Pero el tipo de valor subyacente Tno puede ser en sí mismo un tipo de valor que acepte valores NULL.

Los tipos de valores que aceptan valores NULL se utilizan normalmente cuando es necesario representar valores indefinidos de un tipo de valor subyacente. Por ejemplo, un valor booleano o una variable booleana solo puede ser verdadero o falso. Sin embargo, en algunas aplicaciones, los valores de las variables pueden no estar definidos o faltar. Por ejemplo, un campo de base de datos puede contener verdadero o falso, o puede no contener ningún valor, es decir, NULL. En este caso se puede utilizar el tipo bool?.

Es decir, cuando necesitamos un valor que no admite valores NULL, pero en situaciones reales puede ser un valor NULL, debemos usarT?

Debido a que los tipos de valor son implícitamente convertibles al tipo de valor anulable correspondiente, puede asignar a una variable de un tipo anulable tal como lo haría con su tipo de valor subyacente. También se puede asignar un valor nulo. Por ejemplo:

double? pi = 3.14;
char? letter = 'a';

int m2 = 10;
int? m = m2;

bool? flag = null;

// An array of a nullable value type:
int?[] arr = new int?[10];

La representación del valor predeterminado de un tipo que acepta valores NULL null, es decir, es una instancia de su Nullable<T>.HasValuepropiedad return false.

Generalmente hay tres formas de determinar si un valor que acepta valores NULL está vacío:

int? a = 42;
if (a is int valueOfA) // valueOfA代表A的ASCII码对应值
{
    
    
}
if (a is null)
{
    
    
}
或者
if (a.HasValue)
{
    
    
}
或者
if (a != null)
{
    
    
}

Convertir de tipo de valor que acepta valores NULL a tipo subyacente

Si desea asignar un valor de un tipo de valor que acepta valores NULL a una variable de tipo de valor que no admite NULL, es posible que deba especificar un valor para asignar en lugar de NULL.

int? a = 28;
-- 使用??操作符,使用方法是a = x ?? y 或x ??= y
-- a = x??y当x为空,则a=y ,x非空则a= x
-- x??= y当x为空则x=y,非空则不处理
int b = a ?? -1;
Console.WriteLine($"b is {
      
      b}");  // output: b is 28

int? c = null;
int d = c ?? -1;
Console.WriteLine($"d is {
      
      d}");  // output: d is -1

Tenga en cuenta que, de hecho, Ty T?no son el mismo tipo de valor, por lo que si ambos son tipos de valor, está bien usar la conversión, pero si convierte un valor nulo a un tipo no nulo, se informará un error:

int? n = null;

//int m1 = n;    // Doesn't compile
int n2 = (int)n; // Compiles, but throws an exception if n is null

operador izado

Cualquier Toperador admitido por el tipo en sí T?puede ejecutarse normalmente si el tipo está incluido en la operación. Estos operadores serán promovidos y el resultado de la operación será anulable, pero el tipo aún debe cumplir con la Tconversión de tipo durante la operación (por ejemplo int+float=浮点型,所以int?+folat?=浮点型?).

int? a = 10;
float? b = null;
double? c = 0;

c = a + b;  // a is null
print(c); --Null

El cálculo del valor bool es ligeramente especial y generalmente se ajusta al algoritmo bool (resumí la introducción a Lua en mis notas de estudio de Lua ) :

bool? a = true;
bool? b = null;
bool? c = true;
c = a & b;
Debug.Log(c); --null
c = a | b;
Debug.Log(c); --true

Para los operadores de comparación <、>、<= 和 >=, si uno o ambos operandos son nulos, el resultado es false; de lo contrario, se comparan los valores contenidos de los operandos. nullLa única operación de comparación que se puede realizar con valores ==es la suma !=.

int? a = 10;
Console.WriteLine($"{
      
      a} >= null is {
      
      a >= null}");
Console.WriteLine($"{
      
      a} < null is {
      
      a < null}");
Console.WriteLine($"{
      
      a} == null is {
      
      a == null}");
// Output:
// 10 >= null is False
// 10 < null is False
// 10 == null is False

int? b = null;
int? c = null;
Console.WriteLine($"null >= null is {
      
      b >= c}");
Console.WriteLine($"null == null is {
      
      b == c}");
// Output:
// null >= null is False
// null == null is True

Cómo determinar los tipos de valores que aceptan valores NULL

IsNullable(typeof(T?))

Console.WriteLine($"int? is {
      
      (IsNullable(typeof(int?)) ? "nullable" : "non nullable")} value type");
Console.WriteLine($"int is {
      
      (IsNullable(typeof(int)) ? "nullable" : "non-nullable")} value type");

bool IsNullable(Type type) => Nullable.GetUnderlyingType(type) != null;

// Output:
// int? is nullable value type
// int is non-nullable value type

Al obtener un tipo de valor que acepta valores NULL, tenga en cuenta que solo se puede usar typeof()y no usar GetType(), y este último solo puede devolver el tipo de la clase base:

int? a = 17;
Type typeOfA = a.GetType();
Console.WriteLine(typeOfA.FullName);
// Output:
// System.Int32

Además, la palabra clave is no puede determinar Ty T?, por defecto son del mismo tipo.

int? a = 42;
if (a is int valueOfA)
{
    
    
   print(a); --结果打印 42
}

¿Por qué se recomienda usar menos?T?

T?Aunque es posible evitar que los tipos de valor acepten valores nulos, deberíamos intentar evitar su uso T?porque este tipo en realidad está Tencajonando y desempaquetando el tipo. Cuando declaramos esta variable, el compilador la encuadrará T?, y cuando operemos, T?el compilador la desempaquetará. De hecho, es como una clase que tiene Totra variable . NullPara evitar el impacto de las operaciones de empaquetar y desempaquetar en la memoria, trate de no usarla si es posible.

Boxeo y unboxing

Dado que T?ha sido encajonado, si lo volvemos a encajonar, se juzgará la siguiente situación:

  • Si HasValuese devuelve false, se genera una referencia nula.
  • Si HasValuese devuelve true, el valor correspondiente del tipo de valor subyacente Tse encuadrará, pero Nullable<T>la instancia de no se encuadrará. (Es decir, reboxear Tel valor correspondiente del tipo)

Un valor encuadrado de tipo de valor Tse puede descomprimir en el tipo de valor que acepta valores NULL correspondiente T?, como se muestra en el siguiente ejemplo:

int a = 41;
object aBoxed = a; 
int? aNullable = (int?)aBoxed; -- 把已装箱的a取消装箱并重新装箱为int?
Console.WriteLine($"Value of aNullable: {
      
      aNullable}");

object aNullableBoxed = aNullable;   -- HasValue=true,则基础类型int将重新被装箱
if (aNullableBoxed is int valueOfA)
{
    
    
    Console.WriteLine($"aNullableBoxed is boxed int: {
      
      valueOfA}");
}

int? b = null;
object aNullableBoxed = b;   -- HasValue=false,则生成空引用
if (aNullableBoxed == null)
{
    
    
    Console.WriteLine($"aNullableBoxed is boxed int: {
      
      valueOfA}");
}
// Output:
// Value of aNullable: 41
// aNullableBoxed is boxed int: 41
// aNullableBoxed is boxed int: 41

Supongo que te gusta

Origin blog.csdn.net/milu_ELK/article/details/132063715
Recomendado
Clasificación