Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

False C# compiler warning?

Ok, this is a far stretched corner case we stumbled upon, but it made me curious.

Consider the following code:

public class Foo
{
    private int foo;
    public int Reset() => foo = 0; //remember, assignment expressions
                                   //return something!
}

Will this code compile?

No, it won't if you have fail on all warnings; you'll get a member foo is assigned but never used warning.

This code is, to all purposes, the same as:

public class Foo
{
    private int foo;
    public int Reset() { foo = 0; return foo; }
}

Which compiles just fine, so what is the problem here? Note that the => syntax is not the issue, its returning the assignment expression which seems to befuddle the compiler.

like image 891
InBetween Avatar asked Nov 01 '17 00:11

InBetween


People also ask

What is false in C?

Boolean Variables and Data Type ( or lack thereof in C )Zero is used to represent false, and One is used to represent true. For interpretation, Zero is interpreted as false and anything non-zero is interpreted as true.

Is 1 True or false C?

Initial implementations of the language C (1972) provided no Boolean type, and to this day Boolean values are commonly represented by integers ( int s) in C programs. The comparison operators ( > , == , etc.) are defined to return a signed integer ( int ) result, either 0 (for false) or 1 (for true).

Is 2 considered true in C?

The expressions true == 1 and false == 0 are both true. (And true == 2 is not true).


1 Answers

In the first example, foo is assigned, but never read from. The 0 is assigned to foo, then 0 is returned, regardless of what the value of foo is (e.g. if another thread mutated it in the meantime).

In the second example, foo is assigned, then read from. If another thread modified foo in the meantime, the modified value will be returned, not 0.

You can see this in action by comparing the compiled IL. Given the following code:

public class Foo
{
    private int foo;
    public int Reset() { return (foo = 0); }
    public int Reset2() { foo = 0; return foo; }
}

The following IL is compiled for Reset and 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
}

In Reset, we only store to Foo::foo (stfld).

In Reset2, you can see that we both store to it and load from it (stfld + ldfld).

like image 142
yaakov Avatar answered Oct 13 '22 19:10

yaakov