Let's first consider that there are as you say two possible values -1
and 0
. There's a question of what should be done if 42
ends up in there; whether that is impossible (you are correct in your statement) or just about possible (the value acts like a variant_bool in which -1
is the normal true value, but all non-zero should be treated as true) it's worth considering either way. And it makes sense to treat 42
the same as we treat -1
; that is, it make sense to treat all non-zero as the same.
And even if there is absolutely no other possible non-zero value than -1
it still generalises to "test is non-zero" which is a very common case elsewhere, so it still makes sense to consider this a "test is non-zero" case. This is especially so if the compiler doesn't know -1
is the only possible non-zero value (very likely).
Now there is the question of whether to branch directly on the value (with brfalse
, brtrue
etc.) or to do a boolean operation and then branch on the result. Generally both the C# and VB.NET compilers will produce a boolean value and then branch on that in a debug builds:
Simple Code:
public void TestBool(bool x)
{
if(x)
throw new ArgumentOutOfRangeException();
}
Debug CIL:
nop
ldarg.1
ldc.i4.0
ceq
stloc.0
ldloc.0
brtrue.s NoError
newobj instance void [mscorlib]System.ArgumentOutOfRangeException::.ctor()
throw
NoError:
ret
Release CIL:
ldarg.1
brfalse.s NoError
newobj instance void [mscorlib]System.ArgumentOutOfRangeException::.ctor()
throw
NoError:
ret
The extra steps of essentially doing x == true
before doing the branching aids debugging. Similar effects are sometimes seen in release code, though less often.
So, for this reason we have a comparison being done before the branch in your code, rather than just a branch.
Now there is another question, of whether we should test that the value is zero or test that the value is not zero; either is equivalent much as:
if(x)
DoSomething();
And
if(!x)
{
}
else
DoSomething();
Are equivalent.
For this reason ceq
could have been used, with the branching subsequent being appropriate for the case where item.Found
as 0
. But it's if anything more sensible to use cne
with the branching subsequent being appropriate for the case where item.Found
is not 0
.
But there's no such CIL instruction as cne
, or anything which comparably tests if something is not equal. Generally to do "check not equal" we do a sequence ceq
, ldc.i4.0
, ceq
; check two values are equal and then check that the result of that check is false.
Luckily in the common case that what we are checking something is not equal to is 0
we don't need cne
because cgt.un
is logically equivalent to a hypothetical cne
in this case. This makes cgt.un
the obvious choice when we want to test that something isn't zero.
And hence while IYO "no-one with a sane mind makes the distinction between '-1' and '0' this way" it's a very sane way indeed to test for non-zero generally. And indeed, cgt.un
appears often as just such a non-zero test.
And related: what was the original source code most likely?
If item.Found Then
'More stuff
End If
Which is equivalent to the C#
if(item.Found != 0)
{
//More stuff
}