C# WTF



  • A nice little pitfall for the unaware: I'm not certain if this a compiler bug or a WTF in the C# specs. Assume the following class:

    class Class1
        {
        private int i = 0;

        public int pi
            {
            set    {
                i = value;
                }

            get    {
                return(i);
                }
            }
        }

    Now do this:
     

     class = new Class1();
    ++class.pi;

     
    After the increment, class.pi will equal.... zero. Stepping through in the VS debugger you'll see it enter the get block, return it, and then drop the value on the floor and ignore the implicit assignment. However:

    class.pi += 1;

    And class.pi will equal one.



  • @sparked said:

    After the increment, class.pi will equal.... zero. Stepping through in the VS debugger you'll see it enter the get block, return it, and then drop the value on the floor and ignore the implicit assignment. However:

    class.pi += 1;

    And class.pi will equal one.

    A variable named "class"? Hard to believe...

    Anyway, I've tried your example ("class" renamed to "c") with both Mono and DotGNU portable.net; both return 1.



  • Works fine in VS2k5

    BTW you can name a variable with the same name as a keyword if you prepend it with a @ e.g. @class



  • I thought one second it was the same problem as with ruby. The increment (++) operator does not exist in ruby, hence ++1 just returns 1 ( same as +1 , +++1 , +++++++1 , --++1, ... ). I can't tell why no parser error occurs, though. Has someone a clue why one would want to put two operators aside without any operand between them ?



  • @aikii said:

    I thought one second it was the same problem as with ruby. The increment (++) operator does not exist in ruby, hence ++1 just returns 1 ( same as +1 , +++1 , +++++++1 , --++1, ... ). I can't tell why no parser error occurs, though. Has someone a clue why one would want to put two operators aside without any operand between them ?

    I imagine that + is a unary operator, so multiple +'s get evaluated in order, like:

    ++++1 = +(+(+(+1)))) = 1

     Just a guess, though.   
     



  • @rbowes said:

    @aikii said:

    I thought one second it was the same problem as with ruby. The increment (++) operator does not exist in ruby, hence ++1 just returns 1 ( same as +1 , +++1 , +++++++1 , --++1, ... ). I can't tell why no parser error occurs, though. Has someone a clue why one would want to put two operators aside without any operand between them ?

    I imagine that + is a unary operator, so multiple +'s get evaluated in order, like:

    ++++1 = +(+(+(+1)))) = 1

     Just a guess, though.   

    Oh, right. There aren't many unary operators, though, apart from + and -, I only found !. So you can write wonderful things like :

    !!!!!!!!!!!!!!!!!!!!!!!!true

     No WTF here, this works in any language. At best, we could hope a warning is produced for the special "++" case, not critical since it's a one-time pitfall ( unlike calling valid methods on nil object, i.e. rails produces a warning when you try nil.id, which is valid ).



  • I tested this, and it works just as expected:

    private class Test {
        private int _pi = 0;
        public int Pi { get { return _pi; } set { _pi = value; } }
    }
     

    Test test = new Test();
    int a = test.Pi;
    int b = ++test.Pi;
    int c = test.Pi;

    a contains 0, b contains 1, c ontains 1.

    There is no unary plus operator in C#, so ++i can not be interpreted as +(+i).

    I have actually used a unary operator more than once in a (more or less) valid implementation in Javascript:

    var IE = !!document.all;

    instead of:

    var IE;
    if (document.all) {
        IE = true;
    } else {
        IE = false;
    }



  • @Guffa said:

    var IE = !!document.all;

     ahah, nice typecast. Syntactic shortcuts easily lead to WTFisms, but I can't say this one would harm, especially when using explicit variable names.

    The unary + operator is useless, I guess ( but perhaps one can think of a similar typecast trick ), so forbidding it is probably an helpful language feature. Anyway, both unary "-" and "--" are widely used, so I'd say it doesn't help much.



  • @Guffa said:

    var IE = !!document.all;

    This was an old trick from C to convert any variable to boolean. It was important because long, long ago it was actually unspecified what the value of "true" was. Every compiler vendor had a different idea. Most of them used 1, but some of them used -1 so !0 and ~0 were the same value. (Compiler portability is nice, but it's much more important to account for a bad programmer's inability to grasp the difference between unary and boolean not.) Occasionally, some wiseass would hack his compiler so !0 == 42. Since we never knew for sure what !0 was on a given compiler, we used !!x because if x was nonzero and y was nonzero, !!x == !!y on every compiler every time.

    And if you look at very very old stdlib.h files, you'll find this:

    #define FALSE 0 

    #define TRUE (!FALSE)

    This likewise guaranteed that !!x == TRUE for any nonzero x of any type on any compiler.



  • @aikii said:

    The unary + operator is useless, I guess

    Unary + has historically corrected "negative zero" on floating point numbers. I don't use floating point enough to hit this, but if x and y are both zero and the compiler thinks x != y, I have a little exception handler in my head that says to correct it to (+x) and (+y) and the problem will go away. Not that this happens often... I think I hit that once in some old code I had to maintain in the early 1990s, and now I just have this little mental note in a catch{} block somewhere.

     



  • !! is used a lot when it comes to object detection in JavaScript, because you [b]do[/b] want a boolean as to the support of an object: try alert(document.getElementById); in a JS console and you'll most likely get something like:

    [code]function getElementById{[native function]}[/code]

    Whilst JavaScript is nice in that exists(something) can be evaluated as a boolean implicitly [like if(document.all)] using the double negative means that you can use the value as is rather than having to check if it isn't null later...



    • is rarely useless in C, but in C++ it can be overloaded, so that +x means i.e. "absolute value of x". Not that I would recommend this.


  • @sparked said:

    A nice little pitfall for the unaware: I'm not certain if this a compiler bug or a WTF in the C# specs. Assume the following class:

    class Class1

    {
        private int i = 0;
        public int pi
        {
            set
            {
                i = value;
            }
            get
            {
                return(i);
            }
        }

    }

    Now do this:

    Class1 c = new Class1();

    ++c.pi;

    After the increment, class.pi will equal.... zero. Stepping through in the VS debugger you'll see it enter the get block, return it, and then drop the value on the floor and ignore the implicit assignment. However:

    c.pi += 1;

    And class.pi will equal one.

    To understand why this happens, you need to understand how properties in .NET really work.

    In many ways, they appear to be public fields (member variables) on a class. However, because they can do all sorts of extra work behind they scenes, they are really functions that look like fields.

    Another way of looking at the first case is:

    Class1 c = new Class1();
    ++c.get_pi();

    Here, you can see that by accessing a property, you're really calling a method. The ++ then increments the value returned by the method. However, as you're not using that value, the compiler can optimize away the increment operation.

    Now for the second example. It's common knowledge that this:

    c.pi += 1;

    expands to this: 

    c.pi = c.pi + 1;

    Looking at properties as functions again, it becomes this:

    c.set_pi( c.get_pi() + 1 );

    which is why it works.

     

    Some of the other posters have mentioned that this works fine in VS.NET 2005 (.NET 2.0). My guess is that people complained to Microsoft about properties not working the same as fields, even though they appear the same when looking at the code.



  • @eimaj2nz said:

    Another way of looking at the first case is:

    Class1 c = new Class1();
    ++c.get_pi();

    Here, you can see that by accessing a property, you're really calling a method. The ++ then increments the value returned by the method. However, as you're not using that value, the compiler can optimize away the increment operation.

    Unfortunately for your theory, ++c.get_pi() doesn't compile. ++ must be applied to a lvalue (variable, indexer or property access).
     



  • @ammoQ said:

    @eimaj2nz said:

    Another way of looking at the first case is:

    Class1 c = new Class1();
    ++c.get_pi();

    Here, you can see that by accessing a property, you're really calling a method. The ++ then increments the value returned by the method. However, as you're not using that value, the compiler can optimize away the increment operation.

    Unfortunately for your theory, ++c.get_pi() doesn't compile. ++ must be applied to a lvalue (variable, indexer or property access).
     

    Oops... I guess I should check my examples before I post them. Still, the theory holds.

    I would assume that the property version compiles correctly because the property, while being a pair of functions, is "assignable" and therefore seen as an lvalue. Perhaps the OP found a bug in older versions of the .NET compilers where the result of the ++ operation wasn't being stored to the property.

    Don't quote me on that, however... :o)



  • I don't know what I'm missing here, but I normally do an increment with:

    class.pi++;


    I just tested it in C# Express 2005 (it's all I have here at work) both ways and it works:

    ++clsTest.pi;
    textBox1.Text = clsTest.pi.ToString();


    ...increments it just like...

    clsTest.pi++;
    textBox1.Text = clsTest.pi.ToString();


    shrug .. maybe it's because I'm still fairly new to C# with no prior Java, etc, experience (unless you count JavaScript)


Log in to reply