Related
Edit for clarity: This is a pretty odd behaviour from the compiler, I'm asking for why it behaves this way in general, rather than how to work around it (there are several simple solutions already).
Recently, I came across a piece of code which throws contains a subtle mistake, and ends up throwing an exception. A shortened, contrived example:
Dim list As List(Of Integer) = Nothing
If list?.Any() AndAlso list.First() = 123 Then
MessageBox.Show("OK then!")
End If
In the real example, list was only occasionally Nothing, I'm just shortening the code for clarity. The intention with the first term in the If statement was to both check that list is not Nothing, and to also test for the existence of at least one element. Since in this case, list is Nothing, the list?.Any() actually / typically evaluates to Nothing. Somewhat counter-intuitively, the 2nd term, namely list.First() = 123 is also evaluated by the runtime, causing an obvious exception. This is somewhat counter-intuitive, since at first guess most people would imagine that Nothing is seem as False, and since we're using an AndAlso here, the short-circuit operator would prevent the 2nd half of the If statement from executing.
Additional investigation / "What have you tried:"
Quick check to confirm that a shortened If list?.Any() Then seems to treat list?.Any() as a False:
Dim list As List(Of Integer) = Nothing
If list?.Any() Then
MessageBox.Show("OK then!") 'This line doesn't get hit / run
End If
Also, we can work around the issue by in several ways: If list IsNot Nothing AndAlso list.Any() AndAlso list.First() = 123 Then would work just fine, as would If If(list?.Any(), False) AndAlso list.First() = 123 Then.
Since VB.Net is not my usual language, I thought I'd have a look at this in C#:
List<int> list = null;
if (list?.Any() && list.First() == 123)
{
MessageBox.Show("OK then!");
}
However, this gives a compilation error:
error CS0019: Operator '&&' cannot be applied to operands of type 'bool?' and 'bool'
Apart from the obvious fact that the stricter compiler check would prevent this mistake from being made in the C# scenario, this leads me to believe that type coercion is happening in the VB.Net scenario. One guess might be that the compiler is trying to cast the Boolean result of the 2nd term to a nullable Boolean, however this doesn't make a whole lot of sense to me. Specifically, why would it evaluate it prior to / same time as the left side, and abandoning the entire process early, like it should? Looking back at the VB.Net examples that do work correctly, all involve explicit checks which have a simple Boolean result, rather than a nullable Boolean.
My hope is that someone can give some good insight into this behaviour!
This appears to be an omission(bug) in the VB compiler's syntax evaluation. The documentation for the ?. and ?() null-conditional operators (Visual Basic) states:
Tests the value of the left-hand operand for null (Nothing) before
performing a member access (?.) or index (?()) operation; returns
Nothing if the left-hand operand evaluates to Nothing. Note that, in
the expressions that would ordinarily return value types, the
null-conditional operator returns a Nullable.
The expression list?.Any() (Enumerable.Any Method) would ordinarily return a Boolean (a ValueType), so we should expect list?.Any() to yield a Nullable(Of Boolean).
We should see a compiler error as a Nullable(Of Boolean) can not participate in an AndAlso Operator expression.
Interestingly, if we treat list?.Any() as a Nullable(Of Boolean), it is seen as documented.
If (list?.Any()).HasValue() AndAlso list.First = 123 Then
' something
End If
Edit: The above does not really address your why?.
If you de-compile the generated IL, you get something like this:
Dim source As List(Of Integer) = Nothing
Dim nullable As Boolean?
Dim nullable2 As Boolean? = nullable = If((Not source Is Nothing), New Boolean?(Enumerable.Any(Of Integer)(source)), Nothing)
nullable = If((nullable2.HasValue AndAlso Not nullable.GetValueOrDefault), False, If((Enumerable.First(Of Integer)(source) Is &H7B), nullable, False))
If nullable.GetValueOrDefault Then
MessageBox.Show("OK then!")
End If
This obviously will will not compile, but if we clean it up a bit, the source of the issue becomes apparent.
Dim list As List(Of Integer) = Nothing
Dim nullable As Boolean?
Dim nullable2 As Boolean? = If(list IsNot Nothing,
New Boolean?(Enumerable.Any(Of Integer)(list)),
Nothing)
' nullable2 is nothing, so the 3rd line below is executed and throws the NRE
nullable = If((nullable2.HasValue AndAlso Not nullable.GetValueOrDefault),
False,
If((Enumerable.First(Of Integer)(list) = 123), nullable, False))
If nullable.GetValueOrDefault Then
MessageBox.Show("OK then!")
End If
Edit2:
The OP has found the following statement from the documentation for Nullable Value Types (Visual Basic)
AndAlso and OrElse, which use short-circuit evaluation, must evaluate
their second operands when the first evaluates to Nothing.
This statement makes sense if Option Strict Off is in force and using the OrElse Operator as Nothing can implicitly be converted to False. For the OrElse operator, the second expression is not evaluated only if the first expression is True. In the case of the AndAlso operator, the second operator is not evaluated if the first expression is True.
Also, consider the following code snippet with Option Strict On.
Dim list As List(Of Integer) = Nothing
Dim booleanNullable As Nullable(Of Boolean) = list?.Any()
Dim b As Boolean = (booleanNullable AndAlso list.First() = 123)
If b Then
' do something
End If
This re-arrangement of the original logic does yield a compiler error.
With Option Strict Off, no compiler error is generated, yet the same run-time error occurs.
My Conclusion: As originally stated, this is a bug. When an AndAlso operator is included in a If-Then block the compiler treats the result of the null conditional operator using Option Strict Off type conversion relaxation regardless of the actual state of Option Strict.
Due in part to its heritage of strong support for database access, classic VB supported Null values as a first-class part of the type system. Null is included in the truth tables for all of the built-in logical operators (for example, see https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/imp-operator ).
For this reason, it is not surprising that (as you later noted) VB would choose to use similar semantics to a classic Null for a Boolean? which does not have a value.
As a practical matter, the best way to address this is probably the following:
If (list?.Any()).GetValueOrDefault() AndAlso list.First() = 123 Then
In the alternative, you could use FirstOrDefault to avoid needing the check Any first, although this does rely on the value you seek not being the default (or using a "magic" replacement value, remember that you can override the default in GetValueOrDefault), e.g.
If list?.FirstOrDefault().GetValueOrDefault() = 123 Then
Please try
if (list?.Any() IsNot Nothing AndAlso list.First() == 123)
This question already has answers here:
Single statement across multiple lines in VB.NET without the underscore character
(8 answers)
Closed 4 years ago.
I apologize if this is documented somewhere, I could not find the answer using an underscore in this specific manner (an If Not, AndAlso statement).
Here is what I don't understand
If Not variable Is Nothing _
AndAlso Not Results.Tables.Count = 0 _
AndAlso Not Results.Tables(0).Rows.Count = 0 Then
If I take away the Underscores in this code, VB2017 automatically puts a Then at the end of the first line of code and I get 2 Syntax Errors for AndAlso on both lines they are used. Can someone explain why this is? I read on StackOverflow that the Compiler doesn't care about the underscore, and I've also read MSDN's AndAlso documentation and I'm a little confused.
Thank you for your help.
I read on StackOverflow that the Compiler doesn't care about the underscore
That's not quite true. Implicit line continuations are a fairly recent addition to the Visual Basic language1. They cover many, but not all, scenarios where the _ line continuation character used to be needed.
Here's the complete list from linked documentation:
After a comma (,).
After an open parenthesis (() or before a closing parenthesis ()).
After an open curly brace ({) or before a closing curly brace (}).
After an open embedded expression (<%=) or before the close of an embedded expression (%>) within an XML literal.
After the concatenation operator (&). For example:
After assignment operators (=, &=, :=, +=, -=, *=, /=, \=, ^=, <<=, >>=).
After binary operators (+, -, /, *, Mod, <>, <, >, <=, >=, ^, >>, <<, And, AndAlso2, Or, OrElse, Like, Xor) within an expression.
After the Is and IsNot operators.
After a member qualifier character (.) and before the member name.
This shows we can safely add lines after AndAlso rather than before:
If Not variable Is Nothing AndAlso
Not Results.Tables.Count = 0 AndAlso
Not Results.Tables(0).Rows.Count = 0 Then
I also suggest rewriting as follows, because VB.Net does odd things with Not, using it as a bitwise rather than logical operator3:
If variable IsNot Nothing AndAlso
Results.Tables.Count > 0 AndAlso
Results.Tables(0).Rows.Count > 0 Then
1. Okay, 2010, but I still find a lot of VB developers who don't know about them, or know about them but never check the rules and either avoid them or let the compiler tell them when to add one.
2. Emphasis mine.
3. Plus, I just find it easier to read without the negations.
"if I make it all one line of code I still get a Syntax Error"
This compiles fine:
If Not variable Is Nothing AndAlso Not Results.Tables.Count = 0 AndAlso Not Results.Tables(0).Rows.Count = 0 Then Exit Sub
This also compiles fine:
If Not variable Is Nothing _
AndAlso Not Results.Tables.Count = 0 _
AndAlso Not Results.Tables(0).Rows.Count = 0 Then Exit Sub
What won't work is this:
If Not variable Is Nothing
AndAlso Not Results.Tables.Count = 0
AndAlso Not Results.Tables(0).Rows.Count = 0 Then Exit Sub
Without the _ underscore it expects the If statement to be fully formed on the same line.
As Joel points out, the implicit line continuations require the AndAlso before the line break. See his better answer.
What's good to know however is that the underscores are not necessary when using Linq in newer versions of the compiler:
Dim interesting = From r As DataRow In Results.Tables(0).AsEnumerable()
Let fieldId = r.Field(Of Integer)("id")
Where fieldId <> 0
Order By fieldId
Select r
It means "line continues on next line".
You can also write it as:
If variable IsNoT Nothing AndAlso
Results.Tables.Count > 0 AndAlso
Results.Tables(0).Rows.Count > 0 Then
'something
End If
The conditions in the If statement must be on one line as a VB standard. because some conditions might be too long to be readable on one line, the underscore is used as an escape character that indicates that the line is not finished.
If you write
If Not variable Is Nothing
AndAlso Not Results.Tables.Count = 0
AndAlso Not Results.Tables(0).Rows.Count = 0 Then
Visual stdio will only consider the first line as part of the If statement Making the result:
If Not variable Is Nothing Then
AndAlso Not Results.Tables.Count = 0
AndAlso Not Results.Tables(0).Rows.Count = 0 Then
You will need to put all conditions on one line or use the underscore.
This is the standard in VB.
Using VB.Net
I want to check whether the table row is null or 0
Code
sCmd = New SqlCommand("SELECT * from table1)", conObjects.myConnection)
dReader = sCmd.ExecuteReader
While dReader.Read()
If dReader.Item(11) <> "0" And dReader.Item(11) IsNot Nothing Then
msgbox ("Not NUll or 0")
End if
End while
Showing Error as "Operator '<>' is not defined for type 'DBNull' and string "0""
Why i am getting this error, How to solve this problem.
Need VB.Net Code Help
You should always use AndAlso and OrElse instead of And and Or.
With AndAlso the latter expression is only evaluted if the former returned true. On this way you can avoid exceptions and make your code more efficient.
But apart from that, you should use reader.IsDBNull(index):
If Not dReader.IsDBNull(11) AndAlso dReader.GetString(11) <> "0" Then
End If
There are two problems here: one is that you use And instead of AndAlso: the former will cause your whole line to be evaluated, where the latter will only evaluate the second argument if its first argument is already True.
Your second problem is that you check for "not zero" before you check for "Nothing", and so your first part can fail before the second part is evaluated.
Try this instead:
If dReader.Item(11) IsNot Nothing AndAlso dReader.Item(11) <> "0" Then
Also, it's been a while since I did ADO, but shouldn't you be checking IsDbNull instead of comparing to Nothing? Perhaps VB takes care of that for you, I'm not sure.
Try using AndAlso
If (dReader.Item(11) IsNot Nothing) AndAlso (cint(dReader.Item(11)) <> 0) Then
msgbox ("Not NUll or 0")
End if
basically what it does is, when the first expression is already true, the second expression is not evaluated.
If Convert.ToString(dReader.Item(11)) <> "0" AndAlso dReader.IsDBNull(11)=False
I'm new in VB.NET, but for C, C++, C# and other languages I had some years of expericences. This problem for me is very weird because I never met it before.
I have this line of code:
If obj is Nothing Or obj.IsDisposed Then
'do some stuffs
End If
This line of code will reveal an error when obj is Nothing because obj.IsDisposed doesn't exist (no handle for it). As what I know, the first statement of Or it returns True so the result of If statement in anycase would be True.
Can anyone give me an instruction how to get rid of this (or I have to write If..Then..Else If..End If)
try OrElse, the obj.Disposed will not be evaluated when "obj is Nothing" is true
If obj is Nothing OrElse obj.IsDisposed Then
'do some stuffs
End If
You can use the OrElse Operator it will bypass the second evaluation if the first is true.
From above Link:
A logical operation is said to be short-circuiting if the compiled code can bypass the evaluation of one expression depending on the result of another expression. If the result of the first expression evaluated determines the final result of the operation, there is no need to evaluate the second expression, because it cannot change the final result. Short-circuiting can improve performance if the bypassed expression is complex, or if it involves procedure calls.
OrElse is what you need. it will only evaluate on the first evaluation as long as it is already true
If obj is Nothing OrElse obj.IsDisposed Then
'do some stuffs
End If
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.