Related
Premise: Performing comparisons between two textboxes, one in a subform, one in a parent form, during the subform textbox's AfterUpdate event. When making this comparison, illogical results are returned.
Objective: Return legitimate comparisons between the values contained within these two controls.
Progress:
The following code shows an example of code and apparent, illegitimate results outputted to the Immediate window.
Code:
Debug.Print IsNumeric(textBurnup) & IsNumeric(Parent!textBurnupLimit), _
textBurnup & ">" & Parent!textBurnupLimit & " = " & _
(textBurnup > Parent!textBurnupLimit)
textBurnup = Parent!textBurnupLimit - 100
Debug.Print IsNumeric(textBurnup) & IsNumeric(Parent!textBurnupLimit), _
textBurnup & ">" & Parent!textBurnupLimit & " = " & _
(textBurnup > Parent!textBurnupLimit)
Results:
TrueTrue 100>21500 = True
TrueTrue 21400>21500 = False`
Things I have tried:
Setting textbox format to 'general number'. Fixes problem but I dont want to have to do this.
Performing an arithmetic operation on either control. Fixes problem but I dont want to have to do this.
Using either controls' .Value property. Doesn't work.
Substituting a number in for the Parent!textbox. Works. Defeats the purpose of looking at the Parent's textbox.
A few other things I can't think of now because I am just too surprised that I managed to break logic.
The textboxes are both numbers and should be comparable. Why is this happening? Why does setting one of their formats to 'general number' fix it?
Related articles:
String Compare Logic (based on answers below)
Source Info:
Coding Horror (based on answers below)
In your example 100 > 21500 is True because it's a string ("ascii-betical") comparison, not a numeric one.
One wrinkle in that though...
? "100" > "21500" --> False!
? "100" >" 21500" --> True!
I'm guessing your observed result was because you had a leading space on the second value: if that wasn't the case there's no way it would evaluate to True...
IsNumeric() doesn't check to see if a variable value is a number, just if it could safely be converted to one, so
IsNumeric("100") = True
even though "100" is a string.
If you want values pulled from to behave as numbers then the correct approach is
1) use IsNumeric() to check they can safely be converted to a numeric type then
2) use CDbl(), CLng() etc to convert them as #simoco suggests.
"CDbl(textBurnup) > CDbl(Parent!textBurnupLimit)?" - simoco
"In your example 100>21500 is True because it's a string ("ascii-betical") comparison, not a numeric one. IsNumeric() doesn't check to see if a variable value is a number, so IsNumeric("100") = True even though "100" is a string. If you want values pulled from to behave as numbers then the correct approach is 1)Use IsNumeric() to check they can safely be converted to a numeric type then 2) use CDbl(), CLng() etc to convert them as #simoco suggests" - Tim Williams
I have a statement in VB.net that I thought I wrote correctly to prevent the second half from being evaluated. It looks like this:
If ((myDataSet2 IsNot Nothing) Or myDataSet2.Tables("CurData").Rows.Count > 0)
However it does not skip the second expresion "myDataSet2.Tables("CurData").Rows.Count > 0" like I want it to.
What should I change?
Use the OrElse operator.
If myDataSet2 IsNot Nothing OrElse myDataSet2.Tables("CurData").Rows.Count > 0
EDIT: See my comment on your original question. You are PROBABLY looking for:
If myDataSet2 IsNot Nothing AndAlso myDataSet2.Tables("CurData").Rows.Count > 0
That will check if myDataSet2 isn't null. Assuming that it's not, it will then check that there is at least one row. If it is null, then the second condition won't be checked.
You need to put the second statement into the first if-clause.
Like this:
If(statement1) then
If(statemtent2) then
Else
End if
Else
End If
As it is now both are evaluated and if one of them is true the content in your if-clause will be executed.
I have a piece of vb.net code that I wrote. It's a for loop with two embedded if statements, and the compiler is telling me that each elseif and endif must be preceded by a matching if.
This is my second day of working with vb.net ever, and the only programming experience I have is writing .bat files, so this could be something really stupid. But I cannot figure out why I'm getting these errors, and if you all would be willing to help me out, I would greatly appreciate it!
For Each computer In compArray
If compArray(I) <> Computers.GetKey(I) Then notpresentList.Add(Computers.GetKey(I))
Else
If Computers.GetByIndex(I) = 0 Then disabledList.Add(Computers.GetKey(I))
Elseif Computers.GetByIndex(I)=1 Then enabledList.Add(Computers.GetKey(I))
Elseif Computers.GetByIndex(I)=2 Then unknownList.Add(Computers.GetKey(I))
End if
End if
I += 1
Next
The context for this: I am trying to write a piece of code that will confirm the presence of bitlocker. I wrote in VBScript something that will check whether bitlocker is enabled and then send an email. This piece of code is a part of a program which would retrieve those emails, compare them to a list of computers, and then produce a digest email which states which computers are absent, have bitlocker enabled, disabled, or in an unknown state.
I'm sure there's another and better way to do this, but as I said, I'm fairly new at this, and we need to have this done for legal reasons.
Thanks again!
EDIT: If you need more info, please ask me!
I would use the inline syntax in VB.NETonly with short and simple conditions. Otherwise it makes the code less readable and more error-prone.
Try this:
For Each computer In compArray
If compArray(i) <> Computers.GetKey(i) Then
notpresentList.Add(Computers.GetKey(i))
Else
Dim comp = Computers.GetByIndex(i)
If comp = 0 Then
disabledList.Add(Computers.GetKey(i))
ElseIf comp = 1 Then
enabledList.Add(Computers.GetKey(i))
ElseIf comp = 2 Then
unknownList.Add(Computers.GetKey(i))
Else ' added this to show you that this case is not covered yet
Throw New NotSupportedException
End If
End If
i += 1
Next
Your If…Then lines need to be broken up. Move everything after Then to the next lines and you should be good.
If compArray(I) <> Computers.GetKey(I) Then notpresentList.Add(Computers.GetKey(I))
If…Then statements on one line are self-contained, are not followed with a terminating End If, and may not use ElseIf.
Your confusion is in the VB.NET syntax for If statements. VB.NET allows two different formats which each have different syntax rules: Single-line If statements, and Multi-line If blocks.
A single-line If statement looks like this (notice that there is no End If):
If x Then y = 1
A multi-line If block looks like this:
If x Then
y = 1
End If
When you put code on the same line, after the Then, it assumes that you intend it to be a single-line If statement. Single-line If statements cannot include ElseIf, nor Else conditions. They can only be used for simple conditions. Therefore, to make your code work properly, you need to format it as a multi-line If block by putting the conditional code on the following line, like this:
For Each computer In compArray
If compArray(I) <> Computers.GetKey(I) Then
notpresentList.Add(Computers.GetKey(I))
Else
If Computers.GetByIndex(I) = 0 Then
disabledList.Add(Computers.GetKey(I))
Elseif Computers.GetByIndex(I)=1 Then
enabledList.Add(Computers.GetKey(I))
Elseif Computers.GetByIndex(I)=2 Then
unknownList.Add(Computers.GetKey(I))
End if
End if
I += 1
Next
For more information on the syntax, take a look at the MSDN page on the If statement.
Single line If's should all start with just If:
i.e.
If Computers.GetByIndex(I) = 0 Then disabledList.Add(Computers.GetKey(I))
If Computers.GetByIndex(I) = 1 Then enabledList.Add(Computers.GetKey(I))
If Computers.GetByIndex(I) = 2 Then unknownList.Add(Computers.GetKey(I))
You can also use Select Case to make it more readable, i.e.
Select Case Computers.GetByIndex(I)
Case 0
disabledList.Add(Computers.GetKey(I))
Case 1
enabledList.Add(Computers.GetKey(I))
Case 2
unknownList.Add(Computers.GetKey(I))
Case Else
' Some sort of default action can go here, useful for error catching/prevention
End Select
I have the following INSTR condition in Excel VBA which doesn't work (all the time)
STR_TEXT="SKU1234 $100/10' $200/20'" ' < example string
IF INSTR(STR_TEXT,"/10'") AND INSTR(STR_TEXT,"/20'") THEN
' code
ELSE
' code
END IF
For some obscure reason, it seems like it cannot check for both conditions so the first IF, even if both condition match, doesn't seem to work and goes to ELSE.
The following does work:
STR_TEXT="SKU1234 $100/10' $200/20'" ' < example string
IF INSTR(STR_TEXT,"/10'") THEN
IF INSTR(STR_TEXT,"/20'") THEN
' code
END IF
ELSE
' code
END IF
As you can see, if I separate the conditions on the first IF, it works.
But I would prefer to have both conditions in same IF, as code is 'cleaner'.
Anyone knows why and/or how to fix it without having to put an IF inside another IF ?
The other answers point out the most important thing, which is that InStr actually returns the numeric position of one string in another (or 0 if the desired string isn't found). As they say, you should be testing the condition <result> > 0 in your If statement. I'll just address what the reason is behind your observation that your test "doesn't work (all the time)". It's a nice chance to revel in some ancient I-<3-BASIC awesomeness.
What's going on is that, in this case (see edit at the bottom for more) VBA's And operator (and Or, etc.) is actually a bitwise operator, not a logical one. That is, if you pass it two integer operands, it will do a bit-by-bit And, and return back the resulting integer. For example 42 And 99 evaluates to 34, because (in binary) 0101010 And 1100011 is 0100010.
Now, normally, if you use VBA Boolean values, And works like a logical operator. This is because in VBA, the constant True is equal to the numeric -1, and False is equal to the numeric zero. Because VBA represents -1 as a binary number with all bits set, and zero as a binary number with all bits cleared, you can see that binary operations become equivalent to logical operations. -1 And <something> always equals the same <something>. But if you're just passing any old numbers to And, you'll be getting back a number, and it won't always be a numeric value that is equal to the constants True or False.
Consider a simple example (typed in the Immediate window):
x="abc"
?Instr(x,"a")
1
?Instr(x,"b")
2
?Instr(x,"c")
3
?(Instr(x,"a") and Instr(x, "b"))
0
?(Instr(x,"a") and Instr(x, "c"))
1
Now recall that VBA's If statement treats any non-zero numeric argument as being the same as True, and a zero numeric argument as being the same as False. When you put all this together, you'll find that a statement of your example form:
IF INSTR(STR_TEXT,"/10'") AND INSTR(STR_TEXT,"/20'") THEN
will sometimes pick the first condition and sometimes the second, depending on just what is in the searched string. That's because sometimes the bitwise And operation will return zero and sometimes it will return non-zero. The exact result will depend on the exact positions of the found strings, and this clearly isn't what you'd expect. So that's why the advice you've already gotten matters in the details.
EDIT: As pointed out by Hugh Allen in this comment:
Does the VBA "And" operator evaluate the second argument when the first is false?
VBA's And operator does actually return Boolean values of both of it's operands are Boolean. So saying that it's a bitwise operator is not strictly correct. It's correct for this problem though. Also, the fact that it can act as a bitwise operator does mean that it can't act like a "normal", purely logical, operator. For example, because it must evaluate both operands in order to determine if they are numbers or not, it can't short-circuit.
EXMAPLE:
if instr(str_Text,"/10'") > 0 AND instr(str_text,"/20'") > 0 then
What Tim is saying is that the instr function returns the position in the string of the first instance of the string being searched for..
so in your example: 13 is being returned for instr(str_Text,"/10').
When VBA reads your version instr(str_text,"/10;") (without the >0) then it sees that the result is not 1 (which means true) so it always hits the else)
Instr() return a numeric result: if it's >0 then the tested string contains the string being searched for.
Sub Test()
Dim str_text As String
str_text = "SKU1234 $100/10' $200/20'" ' < example string
If InStr(str_text, "/10'") > 0 And InStr(str_text, "/20'") > 0 Then
MsgBox "Both"
Else
MsgBox "Only one, or none"
End If
End Sub
The INSTR function will return the index of the sub-string you are trying to find in a string.
So the following will give a numeric value instead of a boolean value:
INSTR(STR_TEXT,"/10'")
To fix this, use the following which will give a boolean answer which is required by if condition:
INSTR(STR_TEXT,"/10'") > 0
In VB.NET, what is the difference between And and AndAlso? Which should I use?
The And operator evaluates both sides, where AndAlso evaluates the right side if and only if the left side is true.
An example:
If mystring IsNot Nothing And mystring.Contains("Foo") Then
' bla bla
End If
The above throws an exception if mystring = Nothing
If mystring IsNot Nothing AndAlso mystring.Contains("Foo") Then
' bla bla
End If
This one does not throw an exception.
So if you come from the C# world, you should use AndAlso like you would use &&.
More info here: http://www.panopticoncentral.net/2003/08/18/the-ballad-of-andalso-and-orelse/
The And operator will check all conditions in the statement before continuing, whereas the Andalso operator will stop if it knows the condition is false. For example:
if x = 5 And y = 7
Checks if x is equal to 5, and if y is equal to 7, then continues if both are true.
if x = 5 AndAlso y = 7
Checks if x is equal to 5. If it's not, it doesn't check if y is 7, because it knows that the condition is false already. (This is called short-circuiting.)
Generally people use the short-circuiting method if there's a reason to explicitly not check the second part if the first part is not true, such as if it would throw an exception if checked. For example:
If Not Object Is Nothing AndAlso Object.Load()
If that used And instead of AndAlso, it would still try to Object.Load() even if it were nothing, which would throw an exception.
Interestingly none of the answers mentioned that And and Or in VB.NET are bit operators whereas OrElse and AndAlso are strictly Boolean operators.
Dim a = 3 OR 5 ' Will set a to the value 7, 011 or 101 = 111
Dim a = 3 And 5 ' Will set a to the value 1, 011 and 101 = 001
Dim b = 3 OrElse 5 ' Will set b to the value true and not evaluate the 5
Dim b = 3 AndAlso 5 ' Will set b to the value true after evaluating the 5
Dim c = 0 AndAlso 5 ' Will set c to the value false and not evaluate the 5
Note: a non zero integer is considered true; Dim e = not 0 will set e to -1 demonstrating Not is also a bit operator.
|| and && (the C# versions of OrElse and AndAlso) return the last evaluated expression which would be 3 and 5 respectively. This lets you use the idiom v || 5 in C# to give 5 as the value of the expression when v is null or (0 and an integer) and the value of v otherwise. The difference in semantics can catch a C# programmer dabbling in VB.NET off guard as this "default value idiom" doesn't work in VB.NET.
So, to answer the question: Use Or and And for bit operations (integer or Boolean). Use OrElse and AndAlso to "short circuit" an operation to save time, or test the validity of an evaluation prior to evaluating it. If valid(evaluation) andalso evaluation then or if not (unsafe(evaluation) orelse (not evaluation)) then
Bonus: What is the value of the following?
Dim e = Not 0 And 3
If Bool1 And Bool2 Then
Evaluates both Bool1 and Bool2
If Bool1 AndAlso Bool2 Then
Evaluates Bool2 if and only if Bool1 is true.
Just for all those people who say side effects are evil: a place where having two side effects in one condition is good would be reading two file objects in tandem.
While File1.Seek_Next_Row() And File2.Seek_Next_Row()
Str1 = File1.GetRow()
Str2 = File2.GetRow()
End While
Using the And ensures that a row is consumed every time the condition is checked. Whereas AndAlso might read the last line of File1 and leave File2 without a consumed line.
Of course the code above wouldn't work, but I use side effects like this all the time and wouldn't consider it "bad" or "evil" code as some would lead you to believe. It's easy to read and efficient.
AndAlso is much like And, except it works like && in C#, C++, etc.
The difference is that if the first clause (the one before AndAlso) is true, the second clause is never evaluated - the compound logical expression is "short circuited".
This is sometimes very useful, e.g. in an expression such as:
If Not IsNull(myObj) AndAlso myObj.SomeProperty = 3 Then
...
End If
Using the old And in the above expression would throw a NullReferenceException if myObj were null.
A simple way to think about it is using even plainer English
If Bool1 And Bool2 Then
If [both are true] Then
If Bool1 AndAlso Bool2 Then
If [first is true then evaluate the second] Then
Also see Stack Overflow question: Should I always use the AndAlso and OrElse operators?.
Also: A comment for those who mentioned using And if the right side of the expression has a side-effect you need:
If the right side has a side effect you need, just move it to the left side rather than using "And". You only really need "And" if both sides have side effects. And if you have that many side effects going on you're probably doing something else wrong. In general, you really should prefer AndAlso.
In addition to the answers above, AndAlso provides a conditioning process known as short circuiting. Many programming languages have this functionality built in like vb.net does, and can provide substantial performance increases in long condition statements by cutting out evaluations that are unneccessary.
Another similar condition is the OrElse condition which would only check the right condition if the left condition is false, thus cutting out unneccessary condition checks after a true condition is found.
I would advise you to always use short circuiting processes and structure your conditional statements in ways that can benefit the most by this. For example, test your most efficient and fastest conditions first so that you only run your long conditions when you absolutely have to and short circuit the other times.
For majority of us OrElse and AndAlso will do the trick except for a few confusing exceptions (less than 1% where we may have to use Or and And).
Try not to get carried away by people showing off their boolean logics and making it look like a rocket science.
It's quite simple and straight forward and occasionally your system may not work as expected because it doesn't like your logic in the first place. And yet your brain keeps telling you that his logic is 100% tested and proven and it should work. At that very moment stop trusting your brain and ask him to think again or (not OrElse or maybe OrElse) you force yourself to look for another job that doesn't require much logic.
Use And and Or for logical bit operations, e.g. x% = y% Or 3
AndAlso and OrElse are for If statements:
If x > 3 AndAlso x <= 5 Then
is same as
If (x > 3) And (x <= 5) Then
The way I see it...
Also it's worth noting that it's recommended (by me) that you enclose equations with logical operators in If statements, so they don't get misinterpreted by the compiler, for example:
If x = (y And 3) AndAlso ...
To understand with words not cods:
Use Case:With “And” the compiler will check all conditions so if you are checking that an object could be “Nothing” and then you are checking one of it’s properties you will have a run time error.
But with AndAlso with the first “false” in the conditions it will checking the next one so you will not have an error.
The And / AndAlso Or / OrElse are actually quite useful. Consider this code, where the function DoSomethingWihtAandB may not set A and/or B if it returns False:
Dim A,B,C,D As Object
If DoSomethingWithAandB(A,B)=True And A=1 And B=2 Then C=3
It will crash if DoSomethingWithAandB returns False, because it will continue to evaluate after the And and A and B will equal Nothing
Dim A,B,C,D As Object
If DoSomethingWithAandB(A,B)=True AndAlso A=1 AndAlso B=2 Then C=3
This won't crash if DoSomethingWithAandB returns False because it will stop evaluating at DoSomethingWithAandB(A,B)=True, because False is returned. The AndAlso prevents any further evaluation of the conditions (as the first condition failed). OrElse works the same way. The first True evaluated in the logic chain will stop any further evaluation.