If Object.Value IsNot "Something" Then
Can you do this, or are there certain cases in which it wouldn't work? Wasn't sure if this should only be used for integers and booleans.
Thanks!
I'm not sure if this works or not but if it did it would be a very bad idea to use. The Is and IsNot operators in VB.Net do reference comparisons. When dealing with String values you almost always want to do a value comparison which is through = and <>.
Reference comparisons tell you if it's literally pointing to the same object. In .Net it's very possible for the same identical string to be captured in 2 objects allowing for confusing cases like the following
Function CreateFoo() As String
return "foo"
End Function
Dim str1 = "foo"
Dim str2 = CreateFoo()
if str1 Is str2 Then
' This is possible
Else
' This is also possible
End If
Value comparison provides much more sanity here
Dim str1 = "foo"
Dim str2 = CreateFoo()
if str1 = str2 Then
' This will run
Else
' This is simply not possible
End If
That is will tell you if Object.Value and "Something" are literally the same object.
99.999% of the time, you don't care about that. All you care about is if they are semantically equal, that is they both contain the word 'Something'.
From the documentation: "The IsNot operator determines if two object references refer to different objects."
Thus, you would not want to compare strings with it because it is unlikely that two identical strings would actually refer to the same object. This would only happen if they were compile-time constants, were interned, or both copies of the same variable.
Related
I was wondering if there is any way to access the expected data type within a function similar to an event arg. I am doubtful that this is possible, though it would be an excellent feature.
I frequently work with (old and disorganized)Mysql databases creating interfaces through VB.Net. Often I will have an optional field which contains a NULL value in the database. I am frequently dealing with errors due to NULL and dbnull values in passing data to and from the database.
To complicate things, I often am dealing with unexpected datatypes. I might have an integer zero, a double zero, an empty string, or a string zero.
So I spend a fair amount of code checking that each entry is of the expected type and or converting NULLs to zeros or empty strings depending on the case. I have written a function ncc(null catch convert) to speed up this process.
Public Function ncc(obj As Object, tp As Type) As Object 'Null Catch Convert Function...
My function works great, but I have to manually set the type every time I call the function. It would be so much easier if it were possible to access the expected type of the expression. Here is an example of what I mean.
Dim table as datatable
adapter.fill(table)
dim strinfo as string
dim intinfo as long
strinfo = ncc(table.Rows(0).Item(0),gettype(String)) 'here a string is expected
intinfo = ncc(table.Rows(0).Item(0),gettype(Long)) 'here a long is expected
It would be so much more efficient if it were possible to access the expected type directly from the function.
Something like this would be great:
Public Function ncc(obj As Object, optional tp As Type = nothing) As Object
If tp Is Nothing Then tp = gettype(ncc.expectedtype)
That way I do not have to hard code the type on each line.
strinfo = ncc(table.Rows(0).Item(0))
You can make the ncc function generic to simplify calling it:
Public Function ncc(Of T)(obj As T) As T
If DbNull.Value.Equals(obj) Then Return Nothing
Return Obj
End Function
This kind of function will be able to in some cases infer the type, but if there's any possibility of null you'll still want to include a type name (because DBNull will be the inferred type for those values). The advantage is not needing to call gettype() and so gaining a small degree of type safety:
strinfo = ncc(Of String)(table.Rows(0).Item(0))
But I think this has a small chance to blow up at run time if your argument is not implicitly convertible to the desired type. What you should be doing is adding functions to accept a full row and return a composed type. These functions can exist as static/shared members of the target type:
Shared Function FromDataRow(IDataRow row) As MyObject
And you call it for each row like this:
Dim record As MyObject = MyObject.FromDataRow(table.Rows(i))
But, you problem still exists.
What happens if the column in the database row is null?
then you DO NOT get a data type!
Worse yet? Assume the data column is null, do you want to return null into that variable anyway?
Why not specify a value FOR WHEN its null.
You can use "gettype" on the passed value, but if the data base column is null, then you can't determine the type, and you right back to having to type out the type you want as the 2nd parameter.
You could however, adopt a nz() function (like in VBA/Access).
So, this might be better:
Public Function ncc(obj As Object, Optional nullv As Object = Nothing) As Object
If obj Is Nothing OrElse IsDBNull(obj) Then
Return nullv
End If
Return obj
End Function
So, I don't care if the database column is null, or a number, for such numbers, I want 0.
So
dim MyInt as integer
Dim MyDouble As Double
MyInt = ncc(rstData.Rows(0).Item("ContactID"), 0)
MyDouble = ncc(rstData.Rows(0).Item("ContactID"), 0)
dim strAddress as string = ""
strAddress = ncc(rstData.Rows(0).Item("Address"), "")
Since in NEAR ALL cases, you need to deal with the null from the DB, then above not only works for all data types, but also gets you on the fly conversion.
I mean, you CAN declare variables such as integer to allow null values.
eg:
dim myIntValue as integer?
But, I not sure above would create more problems than it solves.
So,
You can't get exactly what you want, because a function never has knowledge of how it's going to be used. It's not guaranteed that it will be on the right-hand side of an assignment statement.
If you want to have knowledge of both sides, you either need to be assigning to a custom type (so that you can overload the assignment operator) or you need to use a Sub instead of an assignment.
You could do something like this (untested):
Public Sub Assign(Of T)(ByVal field As Object, ByRef destination As T,
Optional ByVal nullDefault As T = Nothing)
If TypeOf field Is DBNull Then
destination = nullDefault
Else
destination = CType(field, T)
End If
End Sub
I haven't tested this, so I'm not completely certain that the compiler would allow the conversion, but I think it would because field is type Object. Note that this would yield a runtime error if field is not convertible to T.
You could even consider putting on a constraint requiring T to be a value type, though I don't think that would be likely to work because you probably need to handling String which is a reference type (even though it basically acts like a value type).
Because the destination is an argument, you wouldn't ever need to specify the generic type argument, it would be inferred.
What's difference between
Len(Me.txtStartDate.Value & vbNullString) = 0
and
Len(Me.txtStartDate.Value) = 0
vbNullString is essentially a null string pointer.
Debug.Print StrPtr(vbNullString) 'prints 0
It looks equivalent to a literal "" empty string, but it's not:
Debug.Print StrPtr("") 'prints an address; 6 bytes are allocated for it
There is practically no difference between your two examples... but only because Me.txtStartDate.Value is already a String.
If you were doing this:
Debug.Print Sheet1.Range("A1").Value & vbNullString
Then you would be doing an implicit type conversion between whatever type is returned by Sheet1.Range("A1").Value (say, a Date, or a Double) by means of string concatenation, because the & operator is only used for string concatenations, and the result of that expression will be a String.
In other words, it's a rather convoluted way to do this:
Debug.Print CStr(Sheet1.Range("A1").Value)
Or, as litelite mentioned, a rather convoluted way to do this:
Len(CStr(Me.txtStartDate.Value)) = 0
You typically use vbNullString in place of an empty string "" literal, to spare uselessly allocating 6 bytes (4 for the string pointer, 2 for the null character), and to make your code unambiguously clear about your intent (e.g. "I mean an empty string, this wasn't a typo"), similar to how you would use string.Empty in C#.
What is vbNullString? It is one strange bird, which exists solely for calling functions in a DLL that were written in C (or C++).
Take a look at http://vb.mvps.org/tips/varptr/. It describes a set of (now hidden) functions in VBA (and VB6) that exist solely to allow the passing of pointers to the contents of variables, again mostly to functions in a DLL that were written in C (or any other language that does pointers).
(Technically, VBA does not do pointers, so any pointer that may be returned by a function is - technically speaking - casted to a long integer. ByRef and With do involve pointers, of course, but in hidden-behind-the-scene ways.)
In particular, try out StrPtr(vbNullString); it will return 0. If you tried instead StrPtr(""), you would get some non-zero result. The result is a pointer to where the characters of a string really are in memory. However, a pointer of 0 means something special in C (and C++) - it is often called the NULL pointer (or simply NULL, or maybe null), and it is meant to signify that a pointer points to nowhere. In C programming, sometimes a coder would like to allow a pointer parameter being NULL to mean something special for a function.
If you tried StrPtr("") a few times in a row, you'll likely get a few different non-zero results. That's because VBA is creating a brand new empty string for each try, and as weird as it seems that any memory should be used for a string that is empty, remember that a string in VBA includes a long integer that indicates the number of characters in the string.
I have some old code that I'm running through to make sure it's as stable as possible. Once in a blue moon, something breaks and leaves a bunch of garbage data in the database so I'm going through to see where a potential error could occur. I've found a bunch of variable assignments that look something like this:
myVariable = dr.Item("myItem") & ""
If dr.Item("myItem") resolves to Null, would the & "" protect myVariable from being Null and set it to "" or would the Null cause me problems?
You're guaranteed to get a string back, whether dr.Item("myItem") is a string, Nothing or DBNull.Value. As per the documentation for the & Operator:
The data type of result is String. If one or both expressions evaluate to Nothing or have a value of DBNull.Value, they are treated as a string with a value of "".
You don't need & operator and extra instance of type String, which concatenate operator will create.
Use .ToString() method
dr.Item("myItem").ToString()
DataRow.Item property return instance of type Object. Then calling ToString() method of that instance will return what you expected in your case (Item contain value of type String)
If item contains DbNull value, then empty string will be returned
DBNull.ToString Method
Or use extension method .Field(Of T)
dr.Field(Of String)("myItem")
DataRowExtensions.Field(Of T) Method (DataRow, String)
If column doesn't exist then Column not in the table Exception will be thrown
Please see the code below:
Dim str1 As String="Test"
Dim int1 As Integer = 1
Dim str2 = str1 & int1
Should int1 be casted into a string before it is concatenated or does it make no difference?
I have recently turned OPTION STRICT ON in a VB.NET app
See String manipulation with & or + in VB.NET.
Using the & operator indicates your intention to concatenate strings,
while the + operator indicates your intention to add numbers. Using
the & operator will convert both sides of the operation into strings.
& always returns String.
This is a very poor question but I'll answer it anyway. The result is: no, casting is not needed. In your case str2 will be Test1.
Internally the code will use String.Concat() method, which takes objects and calls ToString() on the object. And since everything in .NET derives from object, this will work.
The concatenation (&) operator can convert a number to a string implicitly.
In addition, If I had any doubts I would use TypeName to determine the type of the variable
In your case:
TypeName(str2)
Also, If Option Strict is on, the implicit narrowing conversion causes a compile-time error, In this case it is Widening conversion.
I looking at some code and came acorss this:
Dim TestString As String
...
If TestString <> Nothing And TestString <> "" Then
...
EndIf
Are both conditions checking the same thing?
Thanks
Nothing would be no string at all (null in other languages), which is different than an empty string (""), which is actually a string.
However, the check should be replaced with If Not String.IsNullOrEmpty(TestString) Then, which makes it clearer what exactly you are doing.
I just played around with this some in LINQPad, and found something mildly surprising. In VB.NET:
Dim s1 as string = Nothing
Dim s2 as string = ""
Console.WriteLine(s1 is Nothing) 'True
Console.WriteLine(s2 is Nothing) 'False
Console.WriteLine(s1 = "") 'True
Console.WriteLine(s2 = "") 'True
Console.WriteLine(string.IsNullOrEmpty(s1)) 'True
Console.WriteLine(string.IsNullOrEmpty(s2)) 'True
In C#:
string s1 = null;
string s2 = "";
Console.WriteLine(s1 == null); //True
Console.WriteLine(s2 == null); //False
Console.WriteLine(s1 == ""); //False
Console.WriteLine(s2 == ""); //True
Console.WriteLine(string.IsNullOrEmpty(s1)); //True
Console.WriteLine(string.IsNullOrEmpty(s2)); //True
I wasn't quite expecting that. It appears that VB.Net treats an Nothing as an empty string. My guess is for compatibility with older versions of VB.
This reinforces all the more that you should be using String.IsNullOrEmpty for these sorts of checks, as it is more explicit what you are checking for, and works as expected.
They are checking the samething, but they MIGHT be meant to check different things.
If IsNothing(TestString) Then
And
If TestString = Nothing Then
Are different test -- the first is seldom used, because typicaly you only really want to know if it has a non-empty value. But it can be used for treating an empty string anf a null value differently in a DB, or for detecting usage of an optional parameter (both cases requiring extra work to make sure that you don't inadvertantly slip in a wrong value, so somewhat fragile).
In the example given, the test is actually a bit wordy and confusing, if that's what you want to test then
If String.IsNullOrEmpty(TestString) Then
Is the way to go about it. If that "and" was supposed to be an "or" then it might make sense to have used IsNothing(TestString).
Yes, by definition in VB.NET "" is equivalent to Nothing including for =, <> and all VB functions; unless you explicitly care about the difference, such as by checking with Is.
Of course, you'll see differences when using general .NET functions and especially methods where str.Method will fail with a Null reference exception.
BTW I would guess the excerpt in the OP is C# code (badly) converted.