[Notas de estudio de C#] Tipo de referencia (1)

Insertar descripción de la imagen aquí


tipo de referencia

Una variable de tipo referencia almacena una referencia a sus datos (objeto), mientras que una variable de tipo valor contiene sus datos directamente. Para los tipos de referencia, dos variables pueden hacer referencia al mismo objeto; por lo tanto, las operaciones realizadas en una variable afectan el objeto al que hace referencia la otra variable. Para los tipos de valor, cada variable tiene su propia copia de los datos y las operaciones realizadas en una variable no afectan a la otra (excepto las variables de parámetros de entrada, referencia y salida).

En el artículo anterior, solíamos decir que los tipos de referencia son mejores que los tipos de valores. Por un lado, los objetos de tipo de referencia son similares a los punteros y diferentes variables apuntarán al mismo objeto. El tipo de valor clona una copia del valor definido y lo asigna a una nueva variable. Los tipos de referencia son mejores desde una perspectiva de rendimiento, y desde una perspectiva de memoria, los tipos de referencia se almacenan en el montón de la máquina virtual de CLR, mientras que los tipos de valor se almacenan en la pila. Los datos se pueden procesar directamente en el montón, mientras que en la pila necesitas La memoria delante de ella se saca de la pila, y también es mejor usar tipos de referencia desde la perspectiva de la memoria. La relación específica entre referencias y tipos de valores en la memoria se describirá en el futuro capítulo de GC.

Insertar descripción de la imagen aquí


clase

La clase es probablemente el tipo de referencia más familiar.

class ClassA {
    
     } --直接定义类
class DerivedClass : BaseClass {
    
     } --继承单个类
class ImplClass : IFace1, IFace2 {
    
     } -- 实现两个接口
class ImplDerivedClass : BaseClass, IFace1 {
    
     } --继承一个类,实现一个接口

En C#, solo podemos heredar una clase, pero podemos heredar múltiples interfaces. (Después de leer este artículo, supongo que has aprendido la orientación a objetos)

clase anónima

Los tipos anónimos proporcionan una manera conveniente de encapsular un conjunto de propiedades de solo lectura en un solo objeto sin definir primero explícitamente un tipo. Los nombres de tipo los genera el compilador y no se pueden utilizar en el nivel del código fuente. El compilador infiere el tipo de cada propiedad.

var v = new {
    
     Amount = 108, Message = "Hello" };
// Rest the mouse pointer over v.Amount and v.Message in the following
// statement to verify that their inferred types are int and string.
Console.WriteLine(v.Amount + v.Message);  //108Hello

Usando clases anónimas, no necesitamos definir una clase al definir nuevos objetos, esto es para facilitar la generación de algunos objetos, especialmente cuando esta clase está definida temporalmente y solo requiere atributos y no otros métodos, y es posible Cuando un objeto sólo se define una vez.

Un tipo anónimo contiene una o más propiedades públicas de solo lectura. Los miembros de clase que contienen otros tipos de objetos (como métodos o eventos) no son válidos. La expresión utilizada para inicializar una propiedad no puede ser nula, una función anónima o un tipo de puntero.

En el siguiente ejemplo, usamos prod para recorrer la colección de productos y crear una nueva clase anónima cada vez con las propiedades Color y Pirce.

var productQuery =
    from prod in products
    select new {
    
     prod.Color, prod.Price };

foreach (var v in productQuery)
{
    
    
    Console.WriteLine("Color={0}, Price={1}", v.Color, v.Price);
}

Aunque no creamos nombres de variables para las propiedades en la clase anónima, la clase anónima usó automáticamente el nombre de la variable cuando se creó como su propio nombre de variable, que se usó cuando se creó prod.Color,prod.Price. También crea variables correspondientes con el mismo nombre Colory atributos basados ​​en el nombre de la variable Price.

Los campos también pueden definirse por un objeto de otro tipo (clase, estructura u otro tipo anónimo). Se realiza mediante el uso de una variable que contiene este objeto, como se muestra en el siguiente ejemplo, donde se crean dos tipos anónimos utilizando un tipo instanciado definido por el usuario. En ambos casos, el tipo anónimo shipment y el campo shipmentWithBonusen productson de tipo Product, que contiene el valor predeterminado para cada campo. bonusLos campos serán tipos anónimos creados por el compilador.

var product = new Product();
var bonus = new {
    
     note = "You won!" };
var shipment = new {
    
     address = "Nowhere St.", product };
var shipmentWithBonus = new {
    
     address = "Somewhere St.", product, bonus };

En el ejemplo anterior, podemos ver que los atributos de la clase anónima en sí generalmente no necesitan definir tipos, y el tipo lo determina el propio compilador. Las variables con definiciones de tipos existentes son sus tipos correspondientes, por ejemplo, productel tipo es Producty bonusel tipo es una clase anónima.
Dado que el tipo de una clase anónima es indefinido, varse utilizan palabras clave para definir esta clase anónima.

Si desea modificar las propiedades en una clase anónima, puede usar la expresión with:

var apple = new {
    
     Item = "apples", Price = 1.35 };
var onSale = apple with {
    
     Price = 0.79 };
var Banana = apple with {
    
    }; // 使用with空值可以直接赋值

(Tenga en cuenta que with y record solo se pueden usar en C# 9 o superior y no están disponibles temporalmente en Unity)


Registro

Un registro es una clase o estructura, recorddeclarada agregando modificadores,

Considere utilizar registros en lugar de clases o estructuras en las siguientes situaciones:

  • Quiere definir un modelo de datos que se base en la igualdad de valores.
  • Quiere definir un tipo de objeto inmutable.

La igualdad de valores significa que dos valores comparados para determinar la igualdad pueden considerarse iguales solo si sus tipos son iguales, los nombres de los atributos son iguales y los valores de los atributos son iguales. Similar a los pares clave-valor internos que deben ser exactamente iguales. Más concretamente, la igualdad en general es igualdad de referencia, y su comparación de igualdad es: dos tipos se consideran iguales si se refieren a la misma variable.

La inmutabilidad prohíbe cambios en cualquier propiedad o valor de campo de un objeto después de su creación.

Igualdad de referencia e igualdad de valores.

Para aclarar la igualdad de referencia y la igualdad de valores, considere los siguientes ejemplos:

Product A = new Product();
Product B = new Product();
if (A == B)
{
    
    
   Debug.Log(1);  --输出不了
}

En el primer ejemplo, A==B es falso porque A y B son tipos de referencia y sus referencias no son iguales.

Product A = new Product();
Product B = A;
if (A == B)
{
    
    
   Debug.Log(1);  --输出1
}

En el segundo ejemplo, B se refiere a A y A se refiere al nuevo Producto(), por lo que las dos referencias son iguales.

No debería ser necesario explicar que los valores son iguales, es igual en el sentido ordinario.

declaración récord

var phoneNumbers = new string[2];
Person person1 = new("Nancy", "Davolio", phoneNumbers);
Person person2 = new("Nancy", "Davolio", phoneNumbers);
Console.WriteLine(person1 == person2); // output: True

person1.PhoneNumbers[0] = "555-1234";
Console.WriteLine(person1 == person2); // output: True

Console.WriteLine(ReferenceEquals(person1, person2)); // output: False

interfaz

Las interfaces son muy abstractas. Podemos usar interfacepalabras clave para definir una interfaz. En la interfaz, solo debemos definir el encabezado de la función, y su implementación interna la implementa completamente la clase que hereda la interfaz.

interface IEquatable<T>
{
    
    
    bool Equals(T obj);
}

Una interfaz también puede heredar otras interfaces. Cuando una interfaz hereda varias otras interfaces, esta interfaz se denomina interfaz derivada y las otras interfaces se denominan interfaces base. Una clase que hereda una interfaz derivada no solo necesita implementar todos los métodos en la interfaz derivada, sino que también necesita implementar todos los métodos en la interfaz base:

interface IBaseInterface
{
    
    
    void BaseMethod();
}
interface IEquatable : IBaseInterface
{
    
    
    bool Equals();
}
public class TestManager : IEquatable
{
    
    
    bool IEquatable.Equals()
    {
    
    
        throw new NotImplementedException();
    }
    void IBaseInterface.BaseMethod()
    {
    
    
        throw new NotImplementedException();
    }
}

delegado delegado

Los delegados se utilizan normalmente cuando se pasan métodos como parámetros a otros métodos. Piense en un delegado como un método que se activa cuando se llama a un evento. Es equivalente a que no tengamos que definir un parámetro de devolución de llamada dentro del evento o función, sino que solo necesitemos adjuntarle el método delegado.

En comparación con las interfaces, los delegados son más adecuados para combinaciones de métodos flexibles y se pueden agregar a múltiples eventos. Para usar mi comprensión personal como metáfora, pedir comida es un evento. Supongamos que este restaurante puede pedir comida a través de la aplicación y el camarero puede pedir comida, pero la aplicación solo puede pedir comidas preparadas y el camarero puede agregarle platos uno por uno. uno. Ahora tenemos que usar delegados e interfaces para implementar este evento respectivamente. Delegar es como tener un camarero que te ayude a pedir comida, solo necesitas decir lo que quieres comer y el camarero te lo anotará y avisará al chef. La interfaz es como un paquete con un montón de platos preparados. Si pides un juego de muslos de pollo, definitivamente te darán muslos de pollo + arroz. Si pides un juego de chuletas de cerdo, definitivamente te darán chuletas de cerdo + arroz. Usando delegación, si queremos muslos de pollo + arroz, también podemos pedirle al camarero que nos agregue dos platos (métodos), uno son muslos de pollo y el otro es arroz. Pero si queremos muchos platos y se superponen con el menú preparado, entonces la interfaz se puede implementar mejor. Pero si queremos pedir platos de manera flexible, por ejemplo, quiero muslos de pollo + arroz, que se implementa usando la interfaz, y luego quiero una chuleta de cerdo, si uso la interfaz para implementarlo, la interfaz me dará una chuleta de cerdo. + arroz. Si solo quisiera chuletas de cerdo, debería haber llamado al camarero y pedir solo una chuleta de cerdo.

Los delegados se pueden vincular a métodos. Cuando se activa el delegado, el método vinculado se activa automáticamente.

// Declare a delegate:
delegate void Del(int x);

// Define a named method:
void DoWork(int k) {
    
     /* ... */ }

// Instantiate the delegate using the method as a parameter:
Del d = obj.DoWork;

Delegación combinada/Delegación multidifusión

Se pueden fusionar diferentes comisiones:

CustomDel hiDel, byeDel, multiDel, multiMinusHiDel;

hiDel = Hello;
byeDel = Goodbye;
multiDel = hiDel + byeDel;
multiMinusHiDel = multiDel - hiDel;

La fusión de comisiones se puede realizar sumando directamente. En pocas palabras, comisión A + comisión B = fusionar comisión C, y fusionar comisión C puede usar la resta para restar la comisión A o la comisión B. La suma es como si el camarero A y el camarero B le dijeran al chef que la mesa número 1 quiere XXX platos y la mesa número 2 quiere XXX platos. El chef debe decidir qué mesa pide la comida primero y en qué mesa la sirve primero. La resta es que el camarero B se acerca y dice que la mesa N° 2 ya no lo quiere, así que no lo hagas, entonces el chef solo puede cocinar los platos para la mesa N° 1 que le encomendó el camarero A.

Cuando se llama a un delegado de multidifusión, llama a los delegados de la lista en orden. Sólo se pueden fusionar delegados del mismo tipo.

Supongo que te gusta

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