How to chain a vb.net null coalesce operator? - vb.net

I have a class Customer which contains the property Extensions which in turn contains the property Any.
I tried to do:
Dim room = If(customer.Extensions.Any.ElementAt(0).InnerText, Nothing)
but it threw an error when it did not find an Extension element in the incoming xml. I thought it would return nothing once it saw that the first expression was Nothing. Do I have to do a multiple if statement in this case?

Yes, you do have to check for at least one element. So what you could do would be:
Dim a = customer.Extensions.Any.FirstOrDefault()
Dim room = If(a Is Nothing, Nothing, a.InnerText)
Depending on the type of customer.Extensions.Any, you could do this:
Dim room = If(customer.Extensions.Any.Count > 0, customer.Extensions.Any(0), Nothing)

Related

VB.NET Nothing evaluating to False [duplicate]

Coming from Basic boolean logic in C#, I was wondering why:
Dim b As Boolean
Dim obj As Object = Nothing
'followig evaluates to False'
b = DirectCast(Nothing, Boolean)
'This throws an "Object reference not set to an instance of an object"-Exception'
b = DirectCast(obj, Boolean)
A CType(obj, Boolean) would evaluate to False(just as CBool(obj)). I think it is because the compiler uses a helper function, but that is not my theme.
Why does casting Nothing to Boolean evaluates to False, whereas casting an object that is Nothing to Boolean throws an Nullreference-Exception? Does that make sense?
[Option Strict ON]
Presumably, this is because Nothing in VB.NET is not exactly the same thing as null in C#.
In the case of value types, Nothing implies the default value of that type. In the case of a Boolean, the default value is False, so the cast succeeds.
One of the primary differences between value types such as Integer or structures and reference types such as Form or String is that reference types support a null value. That is to say, a reference type variable can contain the value Nothing, which means that the variable doesn't actually refer to a value. In contrast, a value type variable always contains a value. An Integer variable always contains a number, even if that number is zero. If you assign the value Nothing to a value type variable, the value type variable just gets assigned its default value (in the case of Integer, that default value is zero). There is no way in the current CLR to look at an Integer variable and determine whether it has never been assigned a value - the fact that it contains zero doesn't necessarily mean that it hasn't been assigned a value.
–The Truth about Nullable Types and VB...
EDIT: For further clarification, the reason the second example throws a NullReferenceException at run-time is because the CLR is attempting to unbox the Object (a reference type) to a Boolean. This fails, of course, because the object was initialized with a null reference (setting it equal to Nothing):
Dim obj As Object = Nothing
Remember that, as I explained above, the VB.NET keyword Nothing still works the same way as null in C# when it comes to reference types. That explains why you're getting a NullReferenceException because the object you're attempting to cast is literally a null reference. It does not contain a value at all, and therefore cannot be unboxed to a Boolean type.
You don't see the same behavior when you try to cast the keyword Nothing to a Boolean, i.e.:
Dim b As Boolean = DirectCast(Nothing, Boolean)
because the keyword Nothing (this time, in the case of value types) simply means "the default value of this type". In the case of a Boolean, that's False, so the cast is logical and straightforward.
There's a couple of things you have to realize here.
The first is what others have already pointed out: Nothing can be interpreted by the VB compiler as simply the Boolean value False given the proper context, such as Dim b As Boolean = Nothing.
This means that when the compiler sees this:
b = DirectCast(Nothing, Boolean)
It sees a literal (Nothing) and also sees that you want to use this literal as a Boolean. That makes it a no-brainer.
But now here's the second thing you have to realize. DirectCast on an Object is essentially an unboxing operation (for value types). So what needs to happen from the VB compiler's perspective is: there needs to be a Boolean in that box, or else the operation will fail. Since there is in fact nothing in the box—and this time we're really talking nothing, as in null—it throws an exception.
If I were to translate this code to C#, it would look like this:
bool b;
object obj = null;
b = (bool)default(bool);
b = (bool)obj;
Hopefully that makes things a bit clearer?
There's a difference between using the keyword (literal) Nothing, and using a reference variable whose value is Nothing.
In VB.NET, the literal (keyword) Nothing gets special treatment. The Nothing keyword can be automatically converted into a value type, using the default value of that type.
A reference variable whose value is Nothing is different. You don't get the special behaviour.
The documentation says DirectCast "requires an inheritance or implementation relationship between the data types of the two arguments".
Clearly Object does not inherit from or implement Boolean, unless you have put a boxed Boolean into an object variable.
So the code below fails at runtime with an exception.
Dim obj As Object = Nothing
b = DirectCast(obj, Boolean)
To get the expected behavior, you need this code:
Dim b As Boolean?
Dim obj As Object = Nothing
b = DirectCast(obj, Boolean?)
The character ? mean Nullable(of ).
I'm finding that comparing of the Boolean variable to a string of "True", "False" or Is nothing seems to ensure that I get the correct comparisons. I was using a function to return an html string of a div with an image of a checked or unchecked radio button and was having the issue of nothing coming back as false. Using the variable = "True" or "False" string and doing the last check with IS NOTHING helped to resolve that issue.
Dim b as boolean = nothing
response.write CheckValue(b = "True")
response.write (b = "False")
response.write (b is nothing)
Function CheckValue(inVal as boolean) as string
if inVal then
return ("<div><img src="checked.png" ></div>
else
return ("<div><img src="unchecked.png" ></div>
end if
end function
The system seems to do the conversion to string when implicitly compared to a string whereas using the .tostring method just creates an error while allowing the last comparison to actually compare to a value of nothing.
Hopefully that helps somewhat. It at least let me

Get Length of Control.Tag

I am trying to find the length of a control's tag to determine a Boolean's value. I've tried a couple methods to get the text length of a Tag in a control and determine if it has a length of 1 or higher, but none of them seem to be working. They all end up with a System.NullReferenceException error.
Boolean = Control.Tag.ToString.Length > 1
Boolean = Control.Tag.ToString.Count > 1
Boolean = Not Control.Tag.Equals("")
Boolean = Not Control.Tag.ToString.Equals("")
Thats because your Tag is Null (or as it's called in VB Nothing).
So before you check the length of the Tag, you need to make sure it's not Nothing. e.g with:
If Control.Tag Is Nothing Then ...
Before accessing a method or property of the tag, you must make sure that the tag is not Nothing. You can do this in a single expression by using shortcut evaluation:
Dim isDefined As Boolean = Control.Tag IsNot Nothing AndAlso Control.Tag.ToString.Length > 1
Since VB 14.0 / Visual Studio 2015 you can use a Null-conditional operator
Dim isDefined As Boolean = If(Control.Tag?.ToString.Length, 0) > 1
In VB.NET you can use VB.NET specific way as the VB Runtime evaluates Nothing as an empty string which is represented by String.Empty.
In VB.NET you can assign Nothing to any variable regardless it is a value type or a reference type.
Its C# equivalent is default(T) which for reference types returns null and for value types returns the value represented by a state where all bits are zero. E.g. default(bool) returns false
So these ways are also working:
' Let's assume you set the Control.Tag property value to this variable
Dim controlTag As Object = Nothing
' Len() method can accept any Object
Dim controlTagLength As Integer = Len(controlTag)
Dim hasValueByLength As Boolean = controlTagLength > 0
' Always call Equals() method on a constant
' or on a well defined non-null value e.g. String.Empty
' to avoid NullReferenceException
Dim hasValueByInstanceEquals As Boolean = String.Empty.Equals(controlTag)
' Or you can use the static Equals() method that accepts Object
Dim hasValueByStaticEquals As Boolean = String.Equals(controlTag, String.Empty)

Dealing with Nothing in the middle of a string of calls

I am a relative VB.Net noob, and I'm learning by doing. I'm sure what I'm about to ask has been asked 10^19 times before, but whatever code word it's under, I can't figure out how to Google it. Here goes...
We have an object model with one or more Project objects that consists of several Tables, which contain Rows which have Fields. This leads to code all over our apps that looks something like this...
Dim theColor As String = Projects(1).Tables(5).Rows(22).Fields(3)
In our application, if any of the objects in this "call chain" does not exist, the correct value for theColor should be Nothing*. This is just like Excel - the value of an empty cell in an empty row is vbnull, not "Excel has crashed".
This is not how VB.Net works, however. If, for instance, Rows(22) does not exist, the Fields(3) is called on Nothing and an exception is thrown. My question is how to best deal with this...
1) I could check each value to see it it's not Nothing, but that leads to horrible amounts of code...
If Projects(1) IsNot Nothing AndAlso Projects(1).Tables(5) AndAlso...
We have thousands of these, the amount of code this would require would be enormous.
2) I could wrap all accessors in try/catch, but that's really just a different sort of (1)
3) I could have a special instance of each object that has empty values. So, for instance, Tables(5) returns NullTable and Row(22) returns NullRow. But this means I have to always use accessor methods, I can't just look in the underlying arrays. You're probably saying good, but sadly a lot of our older code does just that (yes, duh).
4) Something else entirely? Am I missing some magic that everyone other than me knows?
You could have a function called GetField
Instead of
Dim theColor As String = Projects(1).Tables(5).Rows(22).Fields(3)
You would have
Dim theColor As String = GetField(1, 5, 22, 3)
Function GetField(ByVal projectIndex As Integer, ByVal tableIndex As Integer, ByVal rowIndex As Integer, byVal fieldIndex As Integer) As Object
If Projects(projectIndex) Is Nothing Then
Return Nothing
End If
If Projects(projectIndex).Tables(tableIndex) Is Nothing Then
Return Nothing
End If
If Projects(projectIndex).Tables(tableIndex).Rows(rowIndex) Is Nothing Then
Return Nothing
End If
If Projects(projectIndex).Tables(tableIndex).Rows(rowIndex).fields(fieldIndex) Is Nothing Then
Return Nothing
End If
Return Projects(projectIndex).Tables(tableIndex).Rows(rowIndex).fields(fieldIndex)
End Function
But I got to say... what you are doing looks sloppy. You should think of using classes with properties.
You could concoct a "project manager" of sorts. It is hard to know how viable this is from the post, but something like:
Class ProjectManager
Public Property CurrentProject As ProjectThing
Public Property CurrentTable As Integer
Get
Return tblIndex
End Get
Set
If CurrentProject IsNot Nothing Then
If CurrentProject.Tables(value) Is Nothing Then
Throw exception
Else
tblIndex = value
End If
End If
End Set
End Property
' etc
Then use it to store the current reference to the project and/or table. All the Is Not Nothings can be embedded there.
Private myPrj As New ProjectManager
...
myPrj.CurrentProject = Project(1)
myPrj.CurrentTable = 5
With the "manager" doing all the checking for everyone, you dont have to (much):
Dim theColor As String = myPrj.Rows(22).Fields(3)
or
Dim theColor As String = myPrj.GetRowValue(22, 3)
What it would really be doing is storing a validated object references for you, and testing those not worth storing. It could go as deep as you needed. Even if all it really did was encapsulate those Is Nothing/Is Not Nothing tests, it might add some value.
Public Function GetRowValue(r As Integer, f as Integer) As Something
If r < CurrentProject.Tables(tblIndex).Rows.Count AndAlso
f < CurrentProject.Tables(tblIndex).Rows(r).Fields.Count Then
Return ...
'or
Public Function GetRowValue(Of T)(r As Integer, f as Integer) As T
Once a project is specified, it could expose helpful properties like TableCount. It is possible that the data represented by some of the most used, most important Const definitions, could be exposed as properties:
' swap a property interface for "3"
Dim theColor As String = myPrj.FooColor(22)
You can handle the exception:
Dim theColor As String = Nothing
Try
theColor = Projects(1).Tables(5).Rows(22).Fields(3)
Catch
End Try
If you want to do it 'properly' you should specify the exceptions you are guarding against:
Dim theColor As String = Nothing
Try
theColor = Projects(1).Tables(5).Rows(22).Fields(3)
Catch ex As IndexOutOfRangeException
Catch ex As NullReferenceException
End Try

Why won't this list of struct allow me to assign values to the field?

Public Structure testStruct
Dim blah as integer
Dim foo as string
Dim bar as double
End Structure
'in another file ....
Public Function blahFooBar() as Boolean
Dim tStrList as List (Of testStruct) = new List (Of testStruct)
For i as integer = 0 To 10
tStrList.Add(new testStruct)
tStrList.Item(i).blah = 1
tStrList.Item(i).foo = "Why won't I work?"
tStrList.Item(i).bar = 100.100
'last 3 lines give me error below
Next
return True
End Function
The error I get is: Expression is a value and therefore cannot be the target of an assignment.
Why?
I second the opinion to use a class rather than a struct.
The reason you are having difficulty is that your struct is a value type. When you access the instance of the value type in the list, you get a copy of the value. You are then attempting to change the value of the copy, which results in the error. If you had used a class, then your code would have worked as written.
try the following in your For loop:
Dim tmp As New testStruct()
tmp.blah = 1
tmp.foo = "Why won't I work?"
tmp.bar = 100.100
tStrList.Add(tmp)
Looking into this I think it has something to do with the way .NET copies the struct when you access it via the List(of t).
More information is available here.
Try creating the object first as
Dim X = New testStruct
and setting the properties on THAT as in
testStruct.blah = "fiddlesticks"
BEFORE adding it to the list.

ignore null reference exception vb.net

I am coding in vb.net.
At times the data is empty/null this is due to user's input into the db.
i will like to bypass it, however i get no luck.
here is snippet of my code:
If hct.mydbvalue.name IsNot Nothing Then
dr("mydbvalue") = hct.mydbvalue.name
End If
I still get an error:
System.NullReferenceException: Object reference not set to an instance of an object.
is there a way if it is a null value to not do anything?
Both #FishBasketGordo and #Yuck are correct, you need to check the full object path for nullability:
If (hct IsNot Nothing) AndAlso (hct.mydbvalue IsNot Nothing) AndAlso (hct.mydbvalue.name IsNot Nothing) Then
dr("mydbvalue") = hct.mydbvalue.name
End If
You won't get a NullReferenceException from data in the database that's null when using ADO.NET; ADO.NET uses DBNull.Value to represent null, so your null reference is coming from somewhere else. In the code above, your exception could occur if any of the following were null:
hct
hct.mydbvalue
dr
Make sure that the whole chain is not null. If hct or mydbvalue is null, you'll get the exception.
To me this looks like hct.mydbvalue is null, and therefore you can't call "name" on it.
Private Function NullCheck(ByVal inObject As Object, Optional ByVal defaultValue As Object = "") As Object
Dim out As Object
If Not IsDBNull(inObject) Then
out = inObject ' This returns the value that was passed in when it is not null
Else
out = defaultValue ' This ensures that out is something and defaults to ""
End If
Return out
End Function
You should be checking whether hct is Nothing, as well as mydbvalue. If you look at the exception message property, it will tell you which is causing the error.
I'm also solving this problem, but in C#.
On my project we've complex object paths like "RootObject.childObject.LitleObject.TinyObject.StringName"
when any of these objects in the path is null, you'll get a null reference when you try something easy like
if(RootObject.childObject.LitleObject.TinyObject.StringName == "a")
I would be okay if it just works as whole rest of the path will be null.
eg. when childObject = null, then I want also RootObject.childObject.LitleObject.TinyObject.StringName to be null, not null reference exception.
However I've found no solution yet, but there is one new operator which can slightly help you in some null tasks.
a = object.object ?? defaultValue;
operator ?? is something like ISNULL in SQL server. If object on left is null, it returns the object from right.
It also replaces whole function NullCheck posted by Michael above.
Hope this will help a bit.
more info on operators
http://msdn.microsoft.com/en-us/library/ms173224(v=vs.80).aspx
http://msdn.microsoft.com/en-us/library/6a71f45d(v=vs.80).aspx
you're talking about diferent things.
It doesn't matter if you use ISDBNull(x.y), String.IsNullOrEmpty(x.y) or (x.y=null)
The problem is far sooner than your selected function is called.
when X is null, it cannot have a property Y. so when you call
AnyOfYourPrefferedFunctions(x.y);
the error raises during evaluation of x.y (null. doesn't exist), so it stops before the machine actually knows what is the function you want to call.
Probably only way to check this, would be using reflection. But you would need to send string with path and reference to root. so something like:
var v = GetValueThroughReflection(rootObject, "rootObject.path.to.the.last.object");
Then you'll be able to write a function which will go through the object path and find which one is null and handle it accordingly. e.g. returns null.
But when you'll heavy use that function, it can make your application run slower. as you'll use reflection for such simple task as is getting value out of variable.
from VS14+ you can use
If hct?.mydbvalue?.name IsNot Nothing Then
dr("mydbvalue") = hct.mydbvalue.name
End If
Try inserting a IF NOT isdbnull(hct.mydbvalue.name)
The following code checks all values.
If hct IsNot Nothing AndAlso
hct.mydbvalue IsNot Nothing AndAlso
Not String.IsNullOrWhitespace(hct.mydbvalue.name) Then
dr("mydbvalue") = hct.mydbvalue.name
End If
Note that the last test used String.IsNullOrWhitespace(). I'm assuming name is a string and you don't want to save empty strings.
Update 1
The following code is a simple console application to prove that using IsDbNull() or Micheal's NullCheck() will throw NullReferenceException when hct.mydbvalue is Nothing.
Module Module1
Sub Main()
Dim hct = New hct
Dim dr = New Dictionary(Of String, String)
Dim errorCount = 0
Try
Dim thisCallWillFail = IsDBNull(hct.mydbvalue.name)
Catch ex As NullReferenceException
Console.WriteLine(
"Using IsDBNull() threw NullReferenceException as expected."
)
errorCount += 1
End Try
Try
Dim thisCallWillFail = NullCheck(hct.mydbvalue.name)
Catch ex As NullReferenceException
Console.WriteLine(
"Using NullCheck() threw NullReferenceException as expected."
)
errorCount += 1
End Try
Console.WriteLine("errorCount = 2? {0}", errorCount = 2)
End Sub
Private Function NullCheck(ByVal inObject As Object,
Optional ByVal defaultValue As Object = "") As Object
Dim out As Object
If Not IsDBNull(inObject) Then
' This returns the value that was passed in when it is not null
out = inObject
Else
' This ensures that out is something and defaults to ""
out = defaultValue
End If
Return out
End Function
End Module
Public Class hct
Property mydbvalue As mydbvalue
End Class
Public Class mydbvalue
Property name As String
End Class