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.