What is the difference between (OrElse and Or) and (AndAlso and And)?
Is there any difference in their performances, let say the correctness benefit?? Is there any situation that I shoudn't use OrElse and AndAlso?
Or/And will always evaluate both1 the expressions and then return a result. They are not short-circuiting.
OrElse/AndAlso are short-circuiting. The right expression is only evaluated if the outcome cannot be determined from the evaluation of the left expression alone. (That means: OrElse will only evaluate the right expression if the left expression is false, and AndAlso will only evaluate the right expression if the left expression is true.)
Assuming that no side effects occur in the expressions and the expressions are not dependent (and any execution overhead is ignored), then they are the same.
However, in many cases it is that the expressions are dependent. For instance, we want to do something when a List is not-Nothing and has more than one element:
If list IsNot Nothing AndAlso list.Length > 0 Then .. 'list has stuff
This can also be used to avoid an "expensive" computation (or side-effects, ick!):
If Not Validate(x) OrElse Not ExpensiveValidate(x) Then .. 'not valid
Personally, I find that AndAlso and OrElse are the correct operators to use in all but the 1% - or less, hopefully! - of the cases where a side-effect is desired.
Happy coding.
1 An Exception thrown in the first expression will prevent the second expression from being evaluated, but this should hardly be surprising ..
Besides the short-circuiting mentioned in the other answers, Or/And are usable as bitwise operators where OrElse/AndAlso are not. Bitwise operations include combining values of Flags enums, such as the FileAttributes enumeration where you might indicate a file is both read only and hidden by FileAttributes.ReadOnly Or FileAttributes.Hidden
The difference is that OrElse and AndAlso will short-circuit based on the first condition, meaning that if the first condition doesn't pass, the second (or more) conditions will not be evaluated. This is particularly useful when one of the conditions might be more intensive than the other.
Example where Or is fine (both conditions evaluated):
If Name = "Fred" Or Name = "Sam" Then
It really doesn't matter which way around they are evaluated
The following AndAlso is useful because the second condition might fail
If Not SomeObject Is Nothing AndAlso CheckObjectExistsInDatabase(SomeObject) Then
This allows for the first condition to check whether the object has been set and only if it has been set will go and check the database (or some other task). If this had been a plain And keyword, both would be evaluated.
#Gideon - glad someone pointed that out. Here is a simple test that shows the dramatic impact of AndAlso:
Dim tm As New Stopwatch
Const tries As Integer = 123456
Dim z As Integer = 0
Dim s() As String = New String() {"0", "one"}
Debug.WriteLine("AndAlso")
For x As Integer = 0 To s.Length - 1
z = 0
tm.Restart() 'restart the stopwatch
For y As Integer = 0 To tries
If s(x) = x.ToString AndAlso s(x) = y.ToString Then '<<<<<<<<<<
z += 1
End If
Next
tm.Stop()
Debug.WriteLine(x.ToString.PadRight(3, " "c) & z.ToString.PadRight(10, " "c) & tm.Elapsed.ToString)
Next
Debug.WriteLine("And")
For x As Integer = 0 To s.Length - 1
z = 0
tm.Restart() 'restart the stopwatch
For y As Integer = 0 To tries
If s(x) = x.ToString And s(x) = y.ToString Then '<<<<<<<<<<
z += 1
End If
Next
tm.Stop()
Debug.WriteLine(x.ToString.PadRight(3, " "c) & z.ToString.PadRight(10, " "c) & tm.Elapsed.ToString)
Next
Related
This question already has answers here:
AndAlso/OrElse in VBA
(10 answers)
Closed 8 years ago.
VBA doesn't short-circuit
VBA does not support short-circuiting - apparently because it only has bitwise And/Or/Not etc operations. From the VBA language specification: "Logical operators are simple data operators that perform bitwise computations on their operands." In this light, it makes sense that VBA was designed with true = &H1111 and false = &H0000: this way logical statements can be evaluated as bitwise operations.
The lack of short-circuiting can cause problems
Performance: the ReallyExpensiveFunction() will always be run when this statement is evaluated, even if it is not necessary by the result of the left hand side of the condition
If IsNecessary() And ReallyExpensiveFunction() Then
'...
End If
Errors: if MyObj is Nothing, this conditional statment will result in a runtime error because VBA will still try to check the value of Property
If Not MyObj Is Nothing And MyObj.Property = 5 Then
'...
End If
The solution I've used to implement short-cirtcuiting behavior is nested Ifs
If cond1 And cond2 Then
'...
End If
Becomes
If cond1 Then
If cond2 Then
'...
End If
End If
This way the If statements give the short-circuit-like behavior of not bothering to evaluate cond2 if cond1 is False.
If there is an Else clause, this creates duplicate code blocks
If Not MyObj Is Nothing And MyObj.Property = 5 Then
MsgBox "YAY"
Else
MsgBox "BOO"
End If
Becomes
If Not MyObj Is Nothing Then
If MyObj.Property = 5 Then
MsgBox "YAY"
Else
MsgBox "BOO" 'Duplicate
End If
Else
MsgBox "BOO" 'Duplicate
End If
Is there a way to rewrite If statements to preserve the short-circuit behavior, but avoid duplication of code?
Perhaps with another branching statement like Select Case?
To add context to the question, here is the specific case I'm looking at. I'm implementing a hash table that handles collisions by chaining them in a linked list. The underlying array size is enforced to be a power of two and the hashes are distributed into the current array size by truncating them to the appropriate length.
For example, suppose the array length is 16 (binary 10000). If I have a key that hashes to 27 (binary 11011), I can store it in my 16 slot array by keeping only the bits within the limit of that array size. The index where this item would be stored is (hash value) And (length of array - 1) which in this case is (binary 11011) And (1111) which is 1011 which is 11. The actual hash code is stored along with the key in the slot.
When looking up an item in the hash table in a chain, both the hash and the key must be checked to determine that the correct item has been found. However, if the hash doesn't match, then there is no reason to check the key. I was hoping to gain some tiny intangible amount of performance by nesting the Ifs to get the short-circuit behavior:
While Not e Is Nothing
If keyhash = e.hash Then
If Key = e.Key Then
e.Value = Value
Exit Property
Else
Set e = e.nextEntry
End If
Else
Set e = e.nextEntry
End If
Wend
You can see the Set... is duplicated, and thus this question.
As a more general apprach, I suggest to introduce condition flags and make usage of assigning comparison results to booleans:
dim cond1 as boolean
dim cond2 as boolean
cond1 = false
cond2 = false
' Step 1
cond1 = MyObj Is Nothing
' Step 2: do it only if step 1 was sucessful
if cond1 then
cond2 = MyObj.Property = 5
end if
' Final result:
if cond2 then
msgbox "Yay"
else
msgbox "Boo"
end if
By "chaining" those condition flags, every step is safe, you see the final result in the last condition flag and you don't do unnecessary comparisons. And, to me, it keeps readable.
EDIT 2014-07-09
I usually never omit block delimiters and I consequently set every statement of control structures on a new line. But in this case, you can carefully get a very dense notation that reminds on short-circuit notation, also because the VBA compiler initiates the variables:
dim cond1 as boolean
dim cond2 as boolean
dim cond3 as boolean
dim cond4 as boolean
cond1 = MyObj Is Nothing
if cond1 then cond2 = MyObj.Property = 5
if cond2 then cond3 = MyObj.Property2 = constSomething
if cond3 then cond4 = not isNull(MyObj.Property77)
if cond4 then
msgbox "Hyper-Yay"
else
msgbox "Boo"
end if
I could agree to this. It's a clear flow to read.
EDIT 2021-03-21
Thanks to #Tom's comment, one can write it simpler:
dim cond as boolean
cond = MyObj Is Nothing
if cond then cond = MyObj.Property = 5
if cond then cond = MyObj.Property2 = constSomething
if cond then cond = not isNull(MyObj.Property77)
if cond then
msgbox "Hyper-Yay"
else
msgbox "Boo"
end if
#Tom explains the advantages in his comment below. I fully agree with this. I can only imagine some situations while debugging, when I would like to have separated results of the conditions, and therefore explicitely with four different variables.
There is a way. You're not guaranteed to like it. But this is one of those carefully constructed cases where Goto comes in handy
If Not MyObj Is Nothing Then
If MyObj.Property = 5 Then
MsgBox "YAY"
Else
Goto JUMPHERE
End If
Else
JUMPHERE:
MsgBox "BOO" 'Duplicate
End If
A short-circuited code to implement a short-circuited condition!
Alternately, if instead of MsgBox "BOO" is some long and convoluted code, it can be wrapped in a function and that can be written twice with minimal impact/overhead.
Regarding the specific use case, the multiple Set operations will have a minimal performance impact and hence, if one wants to avoid using Goto (still the most globally efficient approach, codesize + performance wise, avoiding creation of dummy variables, etc. - won't matter, though for such a small piece of code) there is negligible downside in simply repeating the command.
Just to analyze (your sample code) how much can be gained by different methods...
If both conditions are true:, there are 2 comparisons, 1 assignment, 0 jumps
If only first condition is true: there are 2 comparisons, 1 pointer-assignment, 1 jump
If only second condition is true: there is 1 comparison, 1 pointer-assignment, 1 jump
If both conditions are false: there is 1 comparison, 1 pointer-assignment, 1 jump (same as above)
In terms of performance, a jump is usually more expensive than comparison (which happens very quickly in ALU vs. the jump which could lead to a disruption in the code cache, maybe not at these sizes, but still jumps are expensive).
And normal assignment by value would be at best as fast as a pointer-assignment or sometimes worse (this is VBA, can't be 100% sure of the p-code implementation)
So, depending on your use case / expected data, you can try to minimize average number of jumps per iteration in your loop and reorder the code.
How about:
s = "BOO"
If Not MyObj Is Nothing Then
If MyObj.Property = 5 Then s = "YAY"
End If
MsgBox s
This question already has answers here:
AndAlso/OrElse in VBA
(10 answers)
Closed 8 years ago.
VBA doesn't short-circuit
VBA does not support short-circuiting - apparently because it only has bitwise And/Or/Not etc operations. From the VBA language specification: "Logical operators are simple data operators that perform bitwise computations on their operands." In this light, it makes sense that VBA was designed with true = &H1111 and false = &H0000: this way logical statements can be evaluated as bitwise operations.
The lack of short-circuiting can cause problems
Performance: the ReallyExpensiveFunction() will always be run when this statement is evaluated, even if it is not necessary by the result of the left hand side of the condition
If IsNecessary() And ReallyExpensiveFunction() Then
'...
End If
Errors: if MyObj is Nothing, this conditional statment will result in a runtime error because VBA will still try to check the value of Property
If Not MyObj Is Nothing And MyObj.Property = 5 Then
'...
End If
The solution I've used to implement short-cirtcuiting behavior is nested Ifs
If cond1 And cond2 Then
'...
End If
Becomes
If cond1 Then
If cond2 Then
'...
End If
End If
This way the If statements give the short-circuit-like behavior of not bothering to evaluate cond2 if cond1 is False.
If there is an Else clause, this creates duplicate code blocks
If Not MyObj Is Nothing And MyObj.Property = 5 Then
MsgBox "YAY"
Else
MsgBox "BOO"
End If
Becomes
If Not MyObj Is Nothing Then
If MyObj.Property = 5 Then
MsgBox "YAY"
Else
MsgBox "BOO" 'Duplicate
End If
Else
MsgBox "BOO" 'Duplicate
End If
Is there a way to rewrite If statements to preserve the short-circuit behavior, but avoid duplication of code?
Perhaps with another branching statement like Select Case?
To add context to the question, here is the specific case I'm looking at. I'm implementing a hash table that handles collisions by chaining them in a linked list. The underlying array size is enforced to be a power of two and the hashes are distributed into the current array size by truncating them to the appropriate length.
For example, suppose the array length is 16 (binary 10000). If I have a key that hashes to 27 (binary 11011), I can store it in my 16 slot array by keeping only the bits within the limit of that array size. The index where this item would be stored is (hash value) And (length of array - 1) which in this case is (binary 11011) And (1111) which is 1011 which is 11. The actual hash code is stored along with the key in the slot.
When looking up an item in the hash table in a chain, both the hash and the key must be checked to determine that the correct item has been found. However, if the hash doesn't match, then there is no reason to check the key. I was hoping to gain some tiny intangible amount of performance by nesting the Ifs to get the short-circuit behavior:
While Not e Is Nothing
If keyhash = e.hash Then
If Key = e.Key Then
e.Value = Value
Exit Property
Else
Set e = e.nextEntry
End If
Else
Set e = e.nextEntry
End If
Wend
You can see the Set... is duplicated, and thus this question.
As a more general apprach, I suggest to introduce condition flags and make usage of assigning comparison results to booleans:
dim cond1 as boolean
dim cond2 as boolean
cond1 = false
cond2 = false
' Step 1
cond1 = MyObj Is Nothing
' Step 2: do it only if step 1 was sucessful
if cond1 then
cond2 = MyObj.Property = 5
end if
' Final result:
if cond2 then
msgbox "Yay"
else
msgbox "Boo"
end if
By "chaining" those condition flags, every step is safe, you see the final result in the last condition flag and you don't do unnecessary comparisons. And, to me, it keeps readable.
EDIT 2014-07-09
I usually never omit block delimiters and I consequently set every statement of control structures on a new line. But in this case, you can carefully get a very dense notation that reminds on short-circuit notation, also because the VBA compiler initiates the variables:
dim cond1 as boolean
dim cond2 as boolean
dim cond3 as boolean
dim cond4 as boolean
cond1 = MyObj Is Nothing
if cond1 then cond2 = MyObj.Property = 5
if cond2 then cond3 = MyObj.Property2 = constSomething
if cond3 then cond4 = not isNull(MyObj.Property77)
if cond4 then
msgbox "Hyper-Yay"
else
msgbox "Boo"
end if
I could agree to this. It's a clear flow to read.
EDIT 2021-03-21
Thanks to #Tom's comment, one can write it simpler:
dim cond as boolean
cond = MyObj Is Nothing
if cond then cond = MyObj.Property = 5
if cond then cond = MyObj.Property2 = constSomething
if cond then cond = not isNull(MyObj.Property77)
if cond then
msgbox "Hyper-Yay"
else
msgbox "Boo"
end if
#Tom explains the advantages in his comment below. I fully agree with this. I can only imagine some situations while debugging, when I would like to have separated results of the conditions, and therefore explicitely with four different variables.
There is a way. You're not guaranteed to like it. But this is one of those carefully constructed cases where Goto comes in handy
If Not MyObj Is Nothing Then
If MyObj.Property = 5 Then
MsgBox "YAY"
Else
Goto JUMPHERE
End If
Else
JUMPHERE:
MsgBox "BOO" 'Duplicate
End If
A short-circuited code to implement a short-circuited condition!
Alternately, if instead of MsgBox "BOO" is some long and convoluted code, it can be wrapped in a function and that can be written twice with minimal impact/overhead.
Regarding the specific use case, the multiple Set operations will have a minimal performance impact and hence, if one wants to avoid using Goto (still the most globally efficient approach, codesize + performance wise, avoiding creation of dummy variables, etc. - won't matter, though for such a small piece of code) there is negligible downside in simply repeating the command.
Just to analyze (your sample code) how much can be gained by different methods...
If both conditions are true:, there are 2 comparisons, 1 assignment, 0 jumps
If only first condition is true: there are 2 comparisons, 1 pointer-assignment, 1 jump
If only second condition is true: there is 1 comparison, 1 pointer-assignment, 1 jump
If both conditions are false: there is 1 comparison, 1 pointer-assignment, 1 jump (same as above)
In terms of performance, a jump is usually more expensive than comparison (which happens very quickly in ALU vs. the jump which could lead to a disruption in the code cache, maybe not at these sizes, but still jumps are expensive).
And normal assignment by value would be at best as fast as a pointer-assignment or sometimes worse (this is VBA, can't be 100% sure of the p-code implementation)
So, depending on your use case / expected data, you can try to minimize average number of jumps per iteration in your loop and reorder the code.
How about:
s = "BOO"
If Not MyObj Is Nothing Then
If MyObj.Property = 5 Then s = "YAY"
End If
MsgBox s
How Can I use "AND" in if else condition.
1st Condition: (Val(TextBox9.Text) > Val(time.Text))
2nd Condition:
((Format(CDate(Strings.Right(TextBox9.Text.Trim, 11)))) >
(Format(CDate(Strings.Right(time.Text.Trim, 11)))))
I have a problem in my statement condition, it is always execute when the first condition becomes true. I want my condition execute when my two condition becomes true, My big problem is when my 1st condition becomes true and my second condition is false it still executing which is wrong, the output should be "Second Condition is false".
If (Val(TextBox9.Text) > Val(time.Text)) AndAlso
((Format(CDate(Strings.Right(TextBox9.Text.Trim, 11)))) >
(Format(CDate(Strings.Right(time.Text.Trim, 11))))) Then
Console.writeline("Execute both condition is true")
else
Console.writeline("Second Condition is false")
end if
If you are having trouble diagnosing the issue with your conditionals, break them up into several lines, to help you better understand what you are comparing. For example you could do this:
Dim firstCompare = Val(TextBox9.Text) > Val(Time.Text)
Dim formattedTB9 = Format(CDate(Strings.Right(TextBox9.Text.Trim, 11)))
Dim formattedTime = Format(CDate(Strings.Right(Time.Text.Trim, 11)))
Console.WriteLine(String.Format("{0} > {1}", formattedTB9, formattedTime))
Dim secondCompare = formattedTB9 > formattedTime
If (firstCompare) AndAlso (secondCompare) Then
Console.WriteLine("Execute both condition is true")
Else
Console.WriteLine("One of the conditions is false")
End If
Now looking at your code, are you sure you want to be comparing formatted strings of dates and times you just converted from a textbox? Now that things are broken up, leverage the debugger to determine what values you are comparing and focus on tweaking it to be exactly what you want. Use the correct datatypes when comparing objects and ensure you understand how functions like Val and CDate work.
This is not a good way to compare two dates and times. You should first compare dates (not dates converted to strings) and only if they are same compare times. That's because if one date is larger or smaller than other, time doesn't matter. But when they are same, time is important. And you shouldn't compare strings, because when it's formated mm/dd/yyyy, months and days are more important for comparing than years.
If CDate(Strings.Right(TextBox9.Text.Trim, 11))>CDate(Strings.Right(time.Text.Trim, 11)) Then
Console.writeline("Date1 is bigger")
elseif CDate(Strings.Right(TextBox9.Text.Trim, 11))=CDate(Strings.Right(time.Text.Trim, 11))
If Val(TextBox9.Text) > Val(Time.Text) Then
Console.writeline("Date1 is bigger")
elseif Val(TextBox9.Text) = Val(Time.Text)
Console.writeline("Dates are equal")
else
Console.writeline("Date1 is smaller")
end if
else
Console.writeline("Date1 is smaller")
end if
If txtNum1.Text <= 0 Or txtNum2.Text <= 0 Then
lblResult.Text = "Result Error: Enter in a number graeter than zero"
End If
I am new to programming. I am trying to create an if/else statement so that if the number in either text box is less than or equal to 0 it will display an error message and not crash.
You have to Parse the number in the .Text property as an integer.
so your If statement would be something like
If Int32.Parse(txtNum1.Text) <= 0 ....
if you plan on reusing that value multiple times in your code, you can extract it in a variable.
Also, as pointed out in the comments, you should check for invalid numbers, you can do so, with Int32.TryParse(value, number). So then, if the TryParse(..) method returns false, you can handle the case.
To know exactly how this method works, you can read this
But to make it quick value is the string you want to parse and number is the integer value that is parsed out of the string. The method itself returns a boolean (true if it was successfully parse, and false otherwise)
Use proper conversion from string to numbers
Dim res1 As Integer
Dim res2 as Integer
if Not Int.TryParse(txtNum1.Text, res1) then
lblResult.Text = "Enter a valid first number "
return
End If
if Not Int.TryParse(txtNum2.Text, res2) then
lblResult.Text = "Enter a valid second number "
return
End If
If res1 <= 0 OrElse res2 <= 0 Then
lblResult.Text = "Result Error: Enter numbers greater than zero"
End If
You need to convert the user input to a numeric value. The Text property of a textbox is a string not a number. And if you want to convert it you should be prepared to receive bad inputs (like a non numeric value).
The best approach is to use Int.TryParse that try to convert the value typed by your user in a number and if it fails returns false. If successful the converted number will be found in the second argument.
Notice also that you should use OrElse instead of Or because the former use short-circuit evaluation
I wish to warn you about another pitfall that seems to be evident from the error message. The VB compiler tried to help you converting the two strings in numbers. This is very bad from my point of view. You should take the responsability to handle this kind of conversions disabling the automatic conversion of the compiler. Go to the properties of your project, page Compile and set the Option Strict to ON. In this way the compiler would stop this automatic conversion and signal as error the textBox1.Text <= 0
something like this would be better,
you check if it is a int then check if it is zero or under
Dim value1, value2 As Integer
If not Integer.TryParse(txtNum1.text, value1) orelse value1 <= 0 OrElse not Integer.TryParse(txtNum2.text, value2) orelse value2 <= 0 Then
lblResult.Text = "Result Error: Enter in a number graeter than zero"
End If
your comparison won't work normally, you are not using the same types (string vs integer)
i'd rather use integer.tryParse
so the code becomes something like :
dim n1 as integer
dim n2 as integer
if integer.tryparse(txtNum1.Text,n1) and integer.tryparse(txtnum2.text,n2) then
If n1 <= 0 Or n2 <= 0 Then
lblResult.Text = "Result Error: Enter in a number graeter than zero"
End If
else
lblResult.Text = "please input numbers"
end if
What is the difference between doing this:
Dim strTest As String
If strTest > " " Then
End If
and this:
Dim strTest As String
If strTest <> "" Then
End If
I think that code sample 1 is comparing ASCII values (the ASCII code of a SPACE is 32). I have looked through the String section on MSDN but I am unable to find an answer.
Update
I am also confused about what happens here:
Dim strTest As String = "Test"
If strTest > " " Then
End If
The > (greater than) operator will test by alphabetical order or character code value order (depending on the Option Compare setting), whereas the <> (not equal) operator tests for equality. As long as the two strings are different at all, then <> will always evaluate to True. > will evaluate to true as long as the string on the right side of the operator comes after the first string alphabetically, or by character code value. Therefore:
Option Compare Text ' Compare strings alphabetically
...
Dim x As String = "Hello"
Dim y As String = "World"
If x <> y Then
' This block is executed because the strings are different
Else
' This block is skipped
End If
If x > y Then
' This block is skipped
Else
' This block is executed because "Hello" is less than "World" alphabetically
End If
In your question, however, you are comparing a null string variable (set to Nothing) with an empty string. In that case, the comparison operators treat a null variable as an empty string. Therefore, Nothing <> "" should evaluate to False because both sides of the operator are considered empty strings. An empty or null string should always be considered the first in the sort order, so Nothing > "Hello" should evaluate to False because an empty string comes before everything else. But, Nothing > "" should evaluate to False because they are both equal and therefore neither comes before or after the other.
To answer you final question, "Test" > " " will test if the letter T comes before or after a space. If Option Compare is set to Text, it will compare them alphabetically and should return True (this ultimately depends on the alphabetic sorting for your locale). If Option Compare is set to Binary, it will compare them based on their character code values. If they are ASCII strings, a space character has a lower value than a letter, like T, so it, also, should return True.