A partir de C# 9, puedes usar el modificador de registro para definir un tipo de referencia que proporcione funcionalidad incorporada para encapsular datos. C# 10 permite la sintaxis de la clase de registro como sinónimo para aclarar los tipos de referencia, y permite que la estructura de registro defina tipos de valor usando la misma funcionalidad.
Casos de uso para registros en C#(Responde ChatGPT):
El tipo de registro en C# es una nueva función introducida en la última versión. Puede utilizarse en muchos escenarios, incluyendo pero no limitado a los siguientes:
Objeto de datos inmutable: El tipo de registro es inmutable, lo que significa que, una vez creado, su valor de propiedad no puede modificarse. Esto los hace ideales para representar estructuras de datos inmutables como objetos de transferencia (DTOs) o configuraciones. Objetos valor: El tipo de registro puede usarse para representar objetos valor cuya igualdad se determina en función de sus valores de atributos en lugar de equivalencia de referencia. Esto los hace ideales para comparar y almacenar objetos de valor como rangos de fechas, puntos de coordenadas y más. Coincidencia de patrones: El tipo de registro puede usarse junto con la coincidencia de patrones, lo que lo hace más cómodo al trabajar con estructuras de datos complejas. Puedes usar la correspondencia de patrones para comprobar y extraer los valores de atributos del objeto de registro y así realizar la acción adecuada. Colecciones Inmutables: El tipo de registro puede usarse con tipos de colecciones inmutables como ImmutableList, ImmutableDictionary, etc., para crear objetos de colección inmutables. Esto garantiza que el contenido de la colección no se modifique, mejorando así la fiabilidad y seguridad del código. Estos son solo algunos de los casos de uso para el tipo de registro, y puedes decidir si usar el tipo de registro según tus necesidades específicas y la estructura del código. Diferencia entre récord y clase y estructura
La sintaxis utilizada al declarar e instanciar una clase o estructura es la misma que al registrar una operación. Simplemente reemplaza la palabra clave class por record, o usa record struct en lugar de struct. De manera similar, la clase de registro soporta la misma sintaxis para las relaciones de herencia. La diferencia entre un registro y una clase es la siguiente:
- Puedes usar parámetros posicionales en el constructor principal para crear e instanciar tipos con propiedades inmutables.
- Los métodos y operadores que indican igualdad o no igualdad de referencia en clases (por ejemplo, Object.Equals(Object) y ==) indican igualdad o no igualdad de valores en el registro.
- Puedes usar la expresión with para crear una copia de un objeto inmutable con un nuevo valor en el atributo seleccionado.
- El método ToString del registro crea una cadena de formato que muestra el nombre del tipo del objeto y los nombres y valores de todas sus propiedades comunes.
- Un registro puede heredarse de otro registro. Pero los registros no pueden heredarse de las clases, ni tampoco pueden heredarse de los registros.
- La diferencia entre la estructura del registro y la estructura es que el compilador sintetiza métodos para determinar la igualdad y ToString. El compilador sintetiza el método Deconstruct para la estructura del registro de posición.
El compilador sintetiza una propiedad común de solo inicialización para cada parámetro principal del constructor en la clase de registro. En un registro de registro, el compilador sintetiza propiedades públicas de lectura/escritura. El compilador no crea propiedades para los argumentos principales del constructor en los tipos de clase y struct que no contienen el modificador de registro.
grabar
Tipo de referencia de clase de registro (por defecto: la clase puede omitirse) Tipo de valor de estructura de registro
Azúcar de gramática registrada
En realidad es que el récord es esoSintaxis sugar, y el resultado final es código de clase o struct。 Tomemos el siguiente código como ejemplo:
Finalmente, compila al siguiente código:
usando Sistema; usando System.Collections.Generic; usando System.Diagnostics; usando System.Reflection; usando System.Runtime.CompilerServices; usando System.Security; usando System.Security.Permissions; usando System.Text; usando Microsoft.CodeAnalysis;
[ensamblaje: CompilationRelaxations(8)] [ensamblador: RuntimeCompatibility(WrapNonExceptionThrows = true)] [ensamblador: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue | DebuggableAttribute.DebuggingModes.DisableOptimizations)] [ensamblaje: PermisoSeguridad(AcciónSeguridad.RequestMinimum, SkipVerification = true)] [ensamblaje: AssemblyVersion("0.0.0.0")] [módulo: Código No Verificable] [módulo: System.Runtime.CompilerServices.RefSafetyRules(11)]
[System.Runtime.CompilerServices.NullableContext(1)] [System.Runtime.CompilerServices.Nullable(0)] Clase pública PersonaInfo: IEquable<PersonInfo>
{ [CompiladorGenerado] [DebuggerBrowsable(DebuggerBrowsableState.Never)] cadena privada de solo <FirstName>lectura k__BackingField;
[CompiladorGenerado] [DebuggerBrowsable(DebuggerBrowsableState.Never)] cadena de solo lectura <LastName>privada k__BackingField;
[CompiladorGenerado] Contrato de Igualdad de Tipo virtual protegido { [CompiladorGenerado] Obtener { return typeof(PersonInfo); } }
cadena pública Nombre { [CompiladorGenerado] Obtener { regreso <FirstName>k__BackingField; } [CompiladorGenerado] Init { <FirstName>k__BackingField = valor; } }
cadena pública Apellido { [CompiladorGenerado] Obtener { regresar <LastName>k__BackingField; } [CompiladorGenerado] Init { <LastName>k__BackingField = valor; } }
publicaPersonaInfo(cadena Nombre, cadena Apellido) { <FirstName>k__BackingField = Nombre; <LastName>k__BackingField = Apellido; base.. ctor(); }
[CompiladorGenerado] cadena de anulación pública ToString() { StringBuilder stringBuilder = nuevo StringBuilder(); stringBuilder.Append("PersonaInfo"); stringBuilder.Append(" { "); si (PrintMembers(stringBuilder)) { stringBuilder.Append(' '); } stringBuilder.Append('}'); return stringBuilder.ToString(); }
[CompiladorGenerado] PrintMembers virtuales protegidos (StringBuilder builder) { RuntimeHelpers.EnsureSufficientExecutionStack(); constructor. Append("Nombre = "); constructor. Añadir((objeto)Nombre); constructor. Append(", Apellido = "); constructor. Añadir((objeto)Apellido); regreso fiel; }
[System.Runtime.CompilerServices.NullableContext(2)] [CompiladorGenerado] operador bool estático público !=(PersonaInfo izquierda, PersonaInformación derecha) { ¡devolución! (izquierda == derecha); }
[System.Runtime.CompilerServices.NullableContext(2)] [CompiladorGenerado] operador de bool estático público ==(PersonInfo izquierda, PersonaInfo derecha) { return (objeto)left == derecha || ((objeto)izquierda != nulo y izquierda. Iguales (derecha)); }
[CompiladorGenerado] Anulación pública int GetHashCode() { return (EqualityComparer<Type>. Default.GetHashCode(EqualityContract) * -1521134295 + EqualityComparer<string>. Default.GetHashCode(<FirstName>k__BackingField)) * -1521134295 + <string>EqualityComparer. Predeterminado.GetHashCode(<LastName>k__BackingField); }
[System.Runtime.CompilerServices.NullableContext(2)] [CompiladorGenerado] public override bool Equals(object obj) { return Equals(obj como PersonInfo); }
[System.Runtime.CompilerServices.NullableContext(2)] [CompiladorGenerado] bool virtual público igual a (PersonInfo otro) { return (objeto)esto == otros || ((objeto)otro != nulo && EqualityContract == otro. EqualityContract & EqualityComparer<string>. Por defecto. Igual(<FirstName>k__BackingField), otro.<FirstName>k__BackingField) y <string>EqualityComparer. Por defecto. Igual(<LastName>k__BackingField, otro).<LastName>k__BackingField)); }
[CompiladorGenerado] PersonaInfo pública <Clone>virtual $() { return new PersonInfo(this); }
[CompiladorGenerado] PersonaInfo protegida (PersonaInformación original) { <FirstName>k__BackingField = original. <FirstName>k__BackingField; <LastName>k__BackingField = original. <LastName>k__BackingField; }
[CompiladorGenerado] empty public void Deconstruct(cadena out Nombre, cadena out Apellido) { Nombre = esto. Nombre; Apellido = esto. Apellido; }
}
Espacio de nombres Microsoft.CodeAnalysis
{ [CompiladorGenerado] [Incrustado] clase sellada interna EmbeddedAttribute : Atributo { }
}
Espacio de nombres System.Runtime.CompilerServices
{ [CompiladorGenerado] [Microsoft.CodeAnalysis.Embedded] [UsoAtributo(ObjetivosDeAtributo.Clase | AtributoObjetivos.Propiedad | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AtributosObjetivos.RetornoValor | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] clase interna sellada NullableAttribute : Atributo { byte público de solo lectura[] NullableFlags;
NullableAttribute(byte P_0) público { Byte[] array = nuevo byte[1]; arreglo[0] = P_0; NullableFlags = array; }
NullableAttribute(byte[] P_0) público { NullableFlags = P_0; } }
[CompiladorGenerado] [Microsoft.CodeAnalysis.Embedded] [UsoAtributo(ObjetivosDeAtributo.Clase | AttributeTargets.Struct | Objetivos.Atributo.Método | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] clase sellada interna NullableContextAtributo : Atributo { byte público solo lectura Flag;
NullableContextAttribute(byte P_0) público { Bandera = P_0; } }
[CompiladorGenerado] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] clase interna sellada RefSafetyRulesAtributo : Atributo { versión pública de solo lectura en Versión;
public RefSafetyRulesAttribute(int P_0) { Versión = P_0; } }
} Genera automáticamente constructores, reescribe los métodos ToString, GetHashCode, Equals y genera automáticamente algunos métodos.
p1 y p2 son en realidad dos objetos diferentes, pero como el compilador generó automáticamente el comparador (IEquatable) y reescribió los métodos Equals y GetHashCode, la salida era verdadera. Llamar al método ToString también es muy intuitivo para generar el valor registrado. Como se muestra a continuación:
grabar
Obligatorio:required indica que el campo o atributo que aplica debe ser inicializado por todos los constructores o usando el inicializador de objetos. Cualquier expresión utilizada para inicializar una nueva instancia de ese tipo debe inicializar todos los miembros necesarios. Referencia:El inicio de sesión del hipervínculo es visible.
Init: En C# 9 y posteriores, la palabra clave init define el método del accesorio en una propiedad o indexador. La librería solo de init solo asigna valores a atributos o elementos del indexador durante la construcción de objetos. Esto impone la inmutabilidad, así que una vez que un objeto está inicializado, ya no puede cambiarse. Referencia:El inicio de sesión del hipervínculo es visible.
con: Si necesitas replicar una instancia con algunas modificaciones, puedes usar la expresión con para implementar cambios no destructivos. con expresión para crear una nueva instancia de registro que sea una copia de una instancia de registro existente, modificando las propiedades y campos especificados. Referencia:El inicio de sesión del hipervínculo es visible.
Por tanto, el registro puede definirse de la siguiente manera:
Obligatorio, init, sin mencionar que, con en realidad es azúcar sintáctica, lo haráLlama al método $ generado automáticamente <Clone>y luego modificar el valor del atributo. Como sigue:
Código:
Atributo de registro
A veces necesitamos añadir algunas características a los atributos, como características de serialización JSON, información de descripción, etc.
Recursos:
El inicio de sesión del hipervínculo es visible.
El inicio de sesión del hipervínculo es visible.
|