VB.NET and Short-Circuiting

Started by Thorin, September 23, 2008, 04:05:39 PM

Previous topic - Next topic

Thorin

VB6 programmers know full-well that VB6 did not have short-circuiting.  That is, in an If statement all conditions were examined.  This meant that the following would cause an error:

Set x = Nothing
If Not x Is Nothing And x.y = 10 Then
End If

The error being that after determining that x is Nothing, VB6 would still try to compare x's y property to 10.  This, of course, is not possible, as x is Nothing and therefore has no properties.

Along comes VB.NET, where they proudly proclaim to have added short-circuiting!

So I write that same code

If Not x Is Nothing And x.y = 10 Then
End If

Sure enough, an exception is thrown!  Why?  Because in their infinite wisdom, the developers of VB.NET added new keywords to use for short-circuiting.  You see, old VB6 programmers might get confused by the new behaviour of the keyword "And", as that keyword used to do both bitwise and logical operations (how @%&#ed is that?!).  So here's the code you're supposed to use in VB.NET if you want to short-circuit:

If Not x Is Nothing AndAlso x.y = 10 Then
End If

Here's the blog where I found out the truth: http://www.panopticoncentral.net/articles/919.aspx.
Prayin' for a 20!

gcc thorin.c -pedantic -o Thorin
compile successful

Mr. Analog

#1
This is one of the many reasons why I prefer C# and took my learning in that direction way back when. There are kludgy things like this in VB.NET to bridge the differences between VB6 and MSIL concepts.

Heck even in Java doing something like this:

if (blah != null && blah.equalsIgnoreCase("Hello World")){
       doSomething();
}


Took a little bit of getting used to (well, after working heavily with scripting languages which don't support this kind of statement gracefully). So I can see how it would be frustrating.

(modified by, you know, finishing my half-thought...)
By Grabthar's Hammer

Darren Dirt

Quote from: Thorin on September 23, 2008, 04:05:39 PM
Along comes VB.NET, where they proudly proclaim to have added short-circuiting!

So I write that same code

If Not x Is Nothing And x.y = 10 Then
End If

Sure enough, an exception is thrown!  Why?  Because in their infinite wisdom, the developers of VB.NET added new keywords to use for short-circuiting.  You see, old VB6 programmers might get confused by the new behaviour of the keyword "And", as that keyword used to do both bitwise and logical operations (how @%&#ed is that?!).  So here's the code you're supposed to use in VB.NET if you want to short-circuit:

If Not x Is Nothing AndAlso x.y = 10 Then
End If

Here's the blog where I found out the truth: http://www.panopticoncentral.net/articles/919.aspx.


http://en.wikipedia.org/wiki/Short-circuit_evaluation

http://en.wikipedia.org/wiki/Comparison_of_C_Sharp_and_Visual_Basic_.NET#Equality_and_other_comparison_operators

WOW though -- I rely on that kinda shorthand ("short-circuiting") ALL the time in Javascript, since it makes the code way ... um... shorter.
_____________________

Strive for progress. Not perfection.
_____________________

Thorin

Yeah, a lot of non-VB programmers rely on short-circuiting.  What's weird is a lot of old VB programmers used to rely on non-short-circuiting to call methods from their if statements even when the first test in the if statement was already false.  For instance:

Dim x As Integer
Dim y As Integer
x = -1
If x = 1 And y = GetValue() Then
    MsgBox(x)
Else
    MsgBox(y)
End If

Yet, a C#, C++, C, Java, or JavaScript programmer would shake their head at this, thinking that y is never set to a value so it should bork.
Prayin' for a 20!

gcc thorin.c -pedantic -o Thorin
compile successful

Mr. Analog

Actually I seem to recall a lot of short circuiting assertions in C++, it makes debugging hell if it doesn't work properly but I know I've seen lots of

if (x = this.getThatThing() || x = this.getThatOtherThing())

If I ever caught a programmer writing this I'd use the Board of "Education" on 'em
By Grabthar's Hammer

Thorin

Quote from: Mr. Analog on September 25, 2008, 01:01:41 PM
Actually I seem to recall a lot of short circuiting assertions in C++, it makes debugging hell if it doesn't work properly but I know I've seen lots of

if (x = this.getThatThing() || x = this.getThatOtherThing())

If I ever caught a programmer writing this I'd use the Board of "Education" on 'em

I concur, that's an excellent example of what not to do.  Thanks to short-circuiting, x would hold the return from getThatThing() unless getThatThing() didn't return anything, in which case x would hold the return from getThatOtherThing().

But hey, as soon as I see a single equals sign in an if statement, I'm suspicious - if statements are not the right place to make assignments!  (unfortunately I work with VB now, where the single equals sign is both assignment and comparison operator)
Prayin' for a 20!

gcc thorin.c -pedantic -o Thorin
compile successful

Mr. Analog

Anytime I see "clever" code I take a second look ;)
By Grabthar's Hammer

Tom

Quote from: Mr. Analog on September 25, 2008, 02:54:18 PM
Anytime I see "clever" code I take a second look ;)
Yeah, theres not often a good reason for clever code. sometimes, but not often.

I tend to code things in small simple layers that build on one another, where each layer is simple enough not to need anything too clever.
<Zapata Prime> I smell Stanley... And he smells good!!!

Darren Dirt

Quote from: Thorin on September 25, 2008, 02:01:06 PM
Quote from: Mr. Analog on September 25, 2008, 01:01:41 PM
Actually I seem to recall a lot of short circuiting assertions in C++, it makes debugging hell if it doesn't work properly but I know I've seen lots of

if (x = this.getThatThing() || x = this.getThatOtherThing())

If I ever caught a programmer writing this I'd use the Board of "Education" on 'em

I concur, that's an excellent example of what not to do.  Thanks to short-circuiting, x would hold the return from getThatThing() unless getThatThing() didn't return anything, in which case x would hold the return from getThatOtherThing().

Actually... wouldn't x hold the return from getThatThing() but then THAT would be "OR"ed with the return from getThatOtherThing() -- whether or not the first call worked as expected or not?

Ugh.

Maybe the intention was more like:
if (x = this.getThatThing()){do_this()}
else if(x = this.getThatOtherThing()){do_that()}


(in other words, forgetting that Call1 || Call2 means "Call1, or Call2, OR call BOTH") ? ...okay now my brain hurts...



_____________________

Strive for progress. Not perfection.
_____________________

Tom

Quote from: Darren Dirt on September 25, 2008, 04:54:15 PM
Quote from: Thorin on September 25, 2008, 02:01:06 PM
Quote from: Mr. Analog on September 25, 2008, 01:01:41 PM
Actually I seem to recall a lot of short circuiting assertions in C++, it makes debugging hell if it doesn't work properly but I know I've seen lots of

if (x = this.getThatThing() || x = this.getThatOtherThing())

If I ever caught a programmer writing this I'd use the Board of "Education" on 'em

I concur, that's an excellent example of what not to do.  Thanks to short-circuiting, x would hold the return from getThatThing() unless getThatThing() didn't return anything, in which case x would hold the return from getThatOtherThing().

Actually... wouldn't x hold the return from getThatThing() but then THAT would be "OR"ed with the return from getThatOtherThing() -- whether or not the first call worked as expected or not?

Ugh.

Maybe the intention was more like:
if (x = this.getThatThing()){do_this()}
else if(x = this.getThatOtherThing()){do_that()}


(in other words, forgetting that Call1 || Call2 means "Call1, or Call2, OR call BOTH") ? ...okay now my brain hurts...

No. = binds tighter than ||. = always evaluates first, on both sides.

Here's the C++ operator precedence table: http://www.cppreference.com/wiki/operator_precedence
<Zapata Prime> I smell Stanley... And he smells good!!!

Lazybones

DEPENDING on tricks and precedence to push out one or two lines less code is evil. I think code should be written in such a way that someone that does not know that language could figure out basically what it does just by looking at it (with in reason).

Thorin

Quote from: Darren Dirt on September 25, 2008, 04:54:15 PM
Quote from: Thorin on September 25, 2008, 02:01:06 PM
Quote from: Mr. Analog on September 25, 2008, 01:01:41 PM
Actually I seem to recall a lot of short circuiting assertions in C++, it makes debugging hell if it doesn't work properly but I know I've seen lots of

if (x = this.getThatThing() || x = this.getThatOtherThing())

If I ever caught a programmer writing this I'd use the Board of "Education" on 'em

I concur, that's an excellent example of what not to do.  Thanks to short-circuiting, x would hold the return from getThatThing() unless getThatThing() didn't return anything, in which case x would hold the return from getThatOtherThing().

Actually... wouldn't x hold the return from getThatThing() but then THAT would be "OR"ed with the return from getThatOtherThing() -- whether or not the first call worked as expected or not?

Ugh.

Maybe the intention was more like:
if (x = this.getThatThing()){do_this()}
else if(x = this.getThatOtherThing()){do_that()}


(in other words, forgetting that Call1 || Call2 means "Call1, or Call2, OR call BOTH") ? ...okay now my brain hurts...

In the example that Mr. Analog gave, the return value from this.getThatThing() is being assigned into the variable x.  Because this is happening as part of the test portion of an if statement, the test becomes whether the assignment worked correctly or not independent of the data or lack of data in x.  In an ORed statement like that, with short-circuiting, as soon as the first part of the test returns true the rest gets skipped.  Thus, x will hold the return value from this.getThatThing(), unless the assignment of the return value into x didn't succeed.  And then if that assignment didn't work, then the second part of the if statement is run so x will hold the return value from this.getThatOtherThing(), and the if statement will still enter the True branch as long as the second assignment works.

The absolute @%&#ed part about all that is that you enter the true branch so long as the assignment to x worked.

So, to recap what happens in the example:

1. this.getThatThing() gets run, and it's return value gets assigned to x
2. if x was assigned to properly(*), this.getThatOtherThing() is skipped and the true branch of the if statement is run
3. if x was not assigned to properly, this.getThatOtherThing() gets run, and it's return value gets assigned to x
4. if x was assigned to properly(*), the true branch of the if statement is run
5. if x was not assigned to properly, the false branch of the if statement is run

(*) Pretty much the only ways the assignment won't work is if there's a type conversion error, like if getThatThing() returns a string and x is an integer variable, or if your machine just ran out of memory (in which case everything's borked anyway)
Prayin' for a 20!

gcc thorin.c -pedantic -o Thorin
compile successful

Tom

QuoteBecause this is happening as part of the test portion of an if statement, the test becomes whether the assignment worked correctly or not independent of the data or lack of data in x.
In C/C++ and Perl, the foo=bar statement resolves to the final value of foo, since = doesn't return a boolean if it succeeded or failed. So in those languages you can indeed test for the final value.
<Zapata Prime> I smell Stanley... And he smells good!!!

Thorin

Quote from: Tom on September 25, 2008, 07:36:11 PM
QuoteBecause this is happening as part of the test portion of an if statement, the test becomes whether the assignment worked correctly or not independent of the data or lack of data in x.
In C/C++ and Perl, the foo=bar statement resolves to the final value of foo, since = doesn't return a boolean if it succeeded or failed. So in those languages you can indeed test for the final value.

Well, based on the coding convention used I'd say the example was intended to be seen as Java code.

However, I remember writing C++ code way back in CST, and if you only put one equals sign the return value from the function you called was assigned to the variable and you *always* hit the true branch.  The only way to get the false branch to run was to cause the called function to return something that the variable couldn't hold without an explicit cast.  In fact, I remember spending several hours banging my head against the wall (so to speak) because I'd missed one equals sign and was too tired to notice it and couldn't figure out why the false branch of my if construct was never reached!
Prayin' for a 20!

gcc thorin.c -pedantic -o Thorin
compile successful

Darren Dirt

Quote from: Thorin on September 26, 2008, 01:06:36 AM
Quote from: Tom on September 25, 2008, 07:36:11 PM
QuoteBecause this is happening as part of the test portion of an if statement, the test becomes whether the assignment worked correctly or not independent of the data or lack of data in x.
In C/C++ and Perl, the foo=bar statement resolves to the final value of foo, since = doesn't return a boolean if it succeeded or failed. So in those languages you can indeed test for the final value.

Well, based on the coding convention used I'd say the example was intended to be seen as Java code.

However, I remember writing C++ code way back in CST, and if you only put one equals sign the return value from the function you called was assigned to the variable and you *always* hit the true branch.  The only way to get the false branch to run was to cause the called function to return something that the variable couldn't hold without an explicit cast.

^ Winner of the "Clear Post Of The Day" award.

I think now we all agree on what all of us were trying to say ;D  Thanks!
_____________________

Strive for progress. Not perfection.
_____________________