sharp - ¿Falsa advertencia del compilador de C#?
compilador c# descargar (2)
En el primer ejemplo, se asigna foo
, pero nunca se lee. El 0
se asigna a foo
, luego se devuelve 0
, independientemente de cuál sea el valor de foo
(por ejemplo, si otro hilo lo mutó mientras tanto).
En el segundo ejemplo, se asigna foo
, luego se lee de. Si otro hilo modificó foo
mientras tanto, se devolverá el valor modificado, no 0
.
Puedes ver esto en acción comparando el IL compilado. Dado el siguiente código:
public class Foo
{
private int foo;
public int Reset() { return (foo = 0); }
public int Reset2() { foo = 0; return foo; }
}
La siguiente IL está compilada para Reset
y Reset2
.
.method public hidebysig
instance int32 Reset () cil managed
{
.maxstack 3
.locals init (
[0] int32
)
IL_0000: ldarg.0
IL_0001: ldc.i4.0
IL_0002: dup
IL_0003: stloc.0
IL_0004: stfld int32 Foo::foo
IL_0009: ldloc.0
IL_000a: ret
}
.method public hidebysig
instance int32 Reset2 () cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldc.i4.0
IL_0002: stfld int32 Foo::foo
IL_0007: ldarg.0
IL_0008: ldfld int32 Foo::foo
IL_000d: ret
}
En Reset
, solo almacenamos en Foo::foo
( stfld
).
En Reset2
, puede ver que tanto almacenamos como stfld
desde él ( stfld
+ ldfld
).
Ok, este es un caso de esquina muy extendido con el que tropezamos, pero me dio curiosidad.
Considere el siguiente código:
public class Foo
{
private int foo;
public int Reset() => foo = 0; //remember, assignment expressions
//return something!
}
¿Se compilará este código?
No, no lo hará si ha fallado en todas las advertencias; obtendrá un member foo is assigned but never used
advertencia.
Este código es, a todos los efectos, el mismo que:
public class Foo
{
private int foo;
public int Reset() { foo = 0; return foo; }
}
Que compila bien, ¿cuál es el problema aquí? Tenga en cuenta que la sintaxis =>
no es el problema, sino que devuelve la expresión de asignación que parece confundir al compilador.
Para responder esto en términos C # puros, el valor de una expresión de asignación es el valor asignado. Podemos demostrar esto así:
public class Foo
{
public int Bar
{
get { return 2; }
set { /* do nothing */ }
}
}
/* … */
Foo foo = new Foo();
Console.WriteLine(foo.Bar = 23);
Esto imprime 23
en la consola porque el valor de foo.Bar = 23
es 23
aunque el valor de foo.Bar
siempre es 2
.
La mayoría de las veces, el único efecto de esto es un beneficio de rendimiento menor, porque la mayoría de las veces el valor de la propiedad o el campo será lo que se le asignó, pero solo podemos reutilizar el valor local que ya tenemos en lugar de Accediendo a esa propiedad de campo.
La advertencia aquí no solo es técnicamente correcta, sino que también es útil: dado que foo
nunca se lee realmente, es solo una pérdida de memoria tenerlo y tiempo para asignárselo, y debe eliminarlo como cruft. (O, por supuesto, continúe desarrollándose para que aparezca un caso para el que no se ha codificado todavía).