InB*_*een 18 c# compiler-warnings visual-studio-mac
好吧,这是我们偶然发现的一个非常紧张的角落,但它让我很好奇.
请考虑以下代码:
public class Foo
{
private int foo;
public int Reset() => foo = 0; //remember, assignment expressions
//return something!
}
Run Code Online (Sandbox Code Playgroud)
这段代码会编译吗?
不,如果你在所有警告上失败,它就不会; 你会收到member foo is assigned but never used警告.
出于所有目的,此代码与以下内容相同:
public class Foo
{
private int foo;
public int Reset() { foo = 0; return foo; }
}
Run Code Online (Sandbox Code Playgroud)
哪个编译得很好,所以这里有什么问题?请注意,=>语法不是问题,它返回的赋值表达式似乎是混淆了编译器.
yaa*_*kov 35
在第一个示例中,foo已分配,但从未读取.将0被分配给foo,然后0返回,无论什么样的价值foo是(例如,如果另一个线程突变是在此期间).
在第二个示例中,foo已分配,然后从中读取.如果foo在此期间修改了另一个线程,则将返回修改后的值,而不是0.
您可以通过比较编译的IL来查看此操作.给出以下代码:
public class Foo
{
private int foo;
public int Reset() { return (foo = 0); }
public int Reset2() { foo = 0; return foo; }
}
Run Code Online (Sandbox Code Playgroud)
以下IL编译为Reset和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
}
Run Code Online (Sandbox Code Playgroud)
在Reset,我们只存储到Foo::foo(stfld).
在Reset2,您可以看到我们都存储它并从中加载(stfld+ ldfld).
要以纯C#术语回答这个问题,赋值表达式的值是指定的值.我们可以证明这一点:
public class Foo
{
public int Bar
{
get { return 2; }
set { /* do nothing */ }
}
}
/* … */
Foo foo = new Foo();
Console.WriteLine(foo.Bar = 23);
Run Code Online (Sandbox Code Playgroud)
这将打印23到控制台,因为价值foo.Bar = 23是23,即使价值foo.Bar是始终2.
大多数情况下,这种影响的唯一影响是较小的性能优势,因为大多数情况下,属性或字段的值将是分配给它的值,但我们可以重用我们已有的本地值而不是访问该字段的属性.
这里的警告不仅在技术上是正确的,而且也是有用的:因为foo从来没有真正读过,只是浪费内存来分配和分配它,你应该把它当作残骸删除.(或者,当然,继续开发,以便使用它的尚未编码的情况出现).
| 归档时间: |
|
| 查看次数: |
957 次 |
| 最近记录: |