假C#编译器警告?

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编译为ResetReset2.

.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).

  • 啊!很好,我做了错误的假设,左侧是在赋值表达式中返回的,不,它是什么向右.有道理,现在一切都清楚了.谢谢. (3认同)

Jon*_*nna 5

要以纯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 = 2323,即使价值foo.Bar是始终2.

大多数情况下,这种影响的唯一影响是较小的性能优势,因为大多数情况下,属性或字段的值将是分配给它的值,但我们可以重用我们已有的本地值而不是访问该字段的属性.

这里的警告不仅在技术上是正确的,而且也是有用的:因为foo从来没有真正读过,只是浪费内存来分配和分配它,你应该把它当作残骸删除.(或者,当然,继续开发,以便使用它的尚未编码的情况出现).