typeparam example cref c# c#-6.0

cref - summary example c#



¿Cuál es la asignación=> en C#en una firma de propiedad (5)

Esta es una nueva característica de C # 6 llamada miembro con cuerpo de expresión que le permite definir una propiedad de getter usando una función similar a lambda.

Si bien se considera azúcar sintáctico para lo siguiente, es posible que no produzcan IL idéntica:

public int MaxHealth { get { return Memory[Address].IsValid ? Memory[Address].Read<int>(Offs.Life.MaxHp) : 0; } }

Resulta que si compila ambas versiones de lo anterior y compara la IL generada para cada una, verá que son casi iguales.

Aquí está el IL para la versión clásica en esta respuesta cuando se define en una clase llamada TestClass :

.property instance int32 MaxHealth() { .get instance int32 TestClass::get_MaxHealth() } .method public hidebysig specialname instance int32 get_MaxHealth () cil managed { // Method begins at RVA 0x2458 // Code size 71 (0x47) .maxstack 2 .locals init ( [0] int32 ) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldfld class [mscorlib]System.Collections.Generic.Dictionary`2<int64, class MemoryAddress> TestClass::Memory IL_0007: ldarg.0 IL_0008: ldfld int64 TestClass::Address IL_000d: callvirt instance !1 class [mscorlib]System.Collections.Generic.Dictionary`2<int64, class MemoryAddress>::get_Item(!0) IL_0012: ldfld bool MemoryAddress::IsValid IL_0017: brtrue.s IL_001c IL_0019: ldc.i4.0 IL_001a: br.s IL_0042 IL_001c: ldarg.0 IL_001d: ldfld class [mscorlib]System.Collections.Generic.Dictionary`2<int64, class MemoryAddress> TestClass::Memory IL_0022: ldarg.0 IL_0023: ldfld int64 TestClass::Address IL_0028: callvirt instance !1 class [mscorlib]System.Collections.Generic.Dictionary`2<int64, class MemoryAddress>::get_Item(!0) IL_002d: ldarg.0 IL_002e: ldfld class Offs TestClass::Offs IL_0033: ldfld class Life Offs::Life IL_0038: ldfld int64 Life::MaxHp IL_003d: callvirt instance !!0 MemoryAddress::Read<int32>(int64) IL_0042: stloc.0 IL_0043: br.s IL_0045 IL_0045: ldloc.0 IL_0046: ret } // end of method TestClass::get_MaxHealth

Y aquí está el IL para la versión del miembro con cuerpo de expresión cuando se define en una clase llamada TestClass :

.property instance int32 MaxHealth() { .get instance int32 TestClass::get_MaxHealth() } .method public hidebysig specialname instance int32 get_MaxHealth () cil managed { // Method begins at RVA 0x2458 // Code size 66 (0x42) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldfld class [mscorlib]System.Collections.Generic.Dictionary`2<int64, class MemoryAddress> TestClass::Memory IL_0006: ldarg.0 IL_0007: ldfld int64 TestClass::Address IL_000c: callvirt instance !1 class [mscorlib]System.Collections.Generic.Dictionary`2<int64, class MemoryAddress>::get_Item(!0) IL_0011: ldfld bool MemoryAddress::IsValid IL_0016: brtrue.s IL_001b IL_0018: ldc.i4.0 IL_0019: br.s IL_0041 IL_001b: ldarg.0 IL_001c: ldfld class [mscorlib]System.Collections.Generic.Dictionary`2<int64, class MemoryAddress> TestClass::Memory IL_0021: ldarg.0 IL_0022: ldfld int64 TestClass::Address IL_0027: callvirt instance !1 class [mscorlib]System.Collections.Generic.Dictionary`2<int64, class MemoryAddress>::get_Item(!0) IL_002c: ldarg.0 IL_002d: ldfld class Offs TestClass::Offs IL_0032: ldfld class Life Offs::Life IL_0037: ldfld int64 Life::MaxHp IL_003c: callvirt instance !!0 MemoryAddress::Read<int32>(int64) IL_0041: ret } // end of method TestClass::get_MaxHealth

Consulte https://msdn.microsoft.com/en-us/magazine/dn802602.aspx para obtener más información sobre esta y otras características nuevas en C # 6.

Consulte esta publicación Diferencia entre propiedad y campo en C # 3.0+ sobre la diferencia entre un campo y un captador de propiedad en C #.

Actualizar:

Tenga en cuenta que los miembros con cuerpo de expresión se expandieron para incluir propiedades, constructores, finalizadores e indexadores en C # 7.0.

Me encontré con un código que decía

public int MaxHealth => Memory[Address].IsValid ? Memory[Address].Read<int>(Offs.Life.MaxHp) : 0;

Ahora estoy algo familiarizado con las expresiones Lambda. Simplemente no lo he visto usarlo de esta manera.

¿Cuál sería la diferencia entre la declaración anterior y

public int MaxHealth = x ? y:z;


Lo que estás viendo es un miembro con cuerpo de expresión, no una expresión lambda.

Cuando el compilador encuentra un miembro de propiedad con cuerpo de expresión, esencialmente lo convierte en un captador como este:

public int MaxHealth { get { return Memory[Address].IsValid ? Memory[Address].Read<int>(Offs.Life.MaxHp) : 0; } }

(Puede verificar esto usted mismo bombeando el código a una herramienta llamada TryRoslyn ).

Los miembros con cuerpo de expresión, como la mayoría de las características de C # 6, son solo azúcar sintáctica . Esto significa que no proporcionan una funcionalidad que de otro modo no se podría lograr a través de las funciones existentes. En cambio, estas nuevas características permiten utilizar una sintaxis más expresiva y sucinta

Como puede ver, los miembros con cuerpo de expresión tienen un puñado de atajos que hacen que los miembros de propiedad sean más compactos:

  • No es necesario usar una declaración return porque el compilador puede inferir que desea devolver el resultado de la expresión
  • No es necesario crear un bloque de declaración porque el cuerpo es solo una expresión
  • No es necesario usar la palabra clave get porque está implícita en el uso de la sintaxis de miembro con cuerpo de expresión.

He puesto el último punto en negrita porque es relevante para su pregunta real, que responderé ahora.

La diferencia entre...

// expression-bodied member property public int MaxHealth => x ? y:z;

Y...

// field with field initializer public int MaxHealth = x ? y:z;

Es lo mismo que la diferencia entre ...

public int MaxHealth { get { return x ? y:z; } }

Y...

public int MaxHealth = x ? y:z;

Lo cual, si comprende las propiedades, debería ser obvio.

Sin embargo, para ser claros: la primera lista es una propiedad con un captador debajo del capó que se llamará cada vez que acceda a ella. La segunda lista es un campo con un inicializador de campo, cuya expresión solo se evalúa una vez, cuando se instancia el tipo.

Esta diferencia en la sintaxis es bastante sutil y puede conducir a un "problema" que describe Bill Wagner en una publicación titulada "AC # 6 gotcha: Initialization vs. Expression Bodied Members" .

Si bien los miembros con cuerpo de expresión son como expresiones lambda, no son expresiones lambda. La diferencia fundamental es que una expresión lambda da como resultado una instancia delegada o un árbol de expresión. Los miembros con cuerpo de expresión son solo una directiva para que el compilador genere una propiedad detrás de escena. La similitud (más o menos) comienza y termina con la flecha ( => ).

También agregaré que los miembros con cuerpo de expresión no se limitan a los miembros de la propiedad. Trabajan en todos estos miembros:

  • Propiedades
  • Indexadores
  • Métodos
  • Operadores

Agregado en C # 7.0

Sin embargo, no funcionan en estos miembros:

  • Tipos anidados
  • Eventos
  • Campos

Ok ... Hice un comentario de que eran diferentes pero no podía explicar exactamente cómo, pero ahora lo sé.

String Property { get; } = "value";

no es lo mismo que

String Property => "value";

Aquí está la diferencia ...

Cuando usa el inicializador automático, la propiedad crea la instancia de valor y usa ese valor de forma persistente. En la publicación anterior hay un enlace roto a Bill Wagner, que explica esto bien, y busqué el enlace correcto para entenderlo yo mismo.

En mi situación, hice que mi propiedad inicializara automáticamente un comando en un ViewModel para una Vista. Cambié la propiedad para usar el inicializador con cuerpo de expresión y el comando CanExecute dejó de funcionar.

Esto es lo que parecía y esto es lo que estaba sucediendo.

Command MyCommand { get; } = new Command(); //works

esto es a lo que lo cambié.

Command MyCommand => new Command(); //doesn''t work properly

La diferencia aquí es cuando uso { get; } = { get; } = Creo y hago referencia al mismo comando en esa propiedad. Cuando uso => realidad creo un nuevo comando y lo devuelvo cada vez que se llama a la propiedad. Por lo tanto, nunca pude actualizar CanExecute en mi comando porque siempre le decía que actualizara una nueva referencia de ese comando.

{ get; } = // same reference => // new reference

Dicho todo esto, si solo está apuntando a un campo de respaldo, entonces funciona bien. Esto solo sucede cuando el cuerpo automático o de expresión crea el valor de retorno.


Otro punto importante si está usando C # 6:

''=>'' se puede usar en lugar de ''get'' y es solo para los métodos ''get only'' ; no se puede usar con un ''set''.

Para C # 7, vea el comentario de @avenmore a continuación: ahora se puede usar en más lugares. Aquí hay una buena referencia: https://csharp.christiannagel.com/2017/01/25/expressionbodiedmembers/


Se llama Expression Bodied Member y se introdujo en C # 6. Es simplemente azúcar sintáctico sobre una propiedad get only.

Es equivalente a:

public int MaxHealth { get { return Memory[Address].IsValid ? Memory[Address].Read<int>(Offs.Life.MaxHp) : 0; }

Un equivalente de una declaración de método está disponible:

public string HelloWorld() => "Hello World";

Principalmente le permite acortar la placa repetitiva.