TryCast for string to integer in VB.NET - vb.net

I do not understand at all how to use TryCast in my code, but it is something I need to use for validating user input. I have done various searches and looked at various questions on here, but no one seems to actually say how to use it, and the MSDN website doesn't seem to help at all.
Function ValidateInput(Var_In As String) As Integer
If TryCast(Var_In, Integer) = Nothing Then
Return vbNull
Else
Return Var_In
End If
End Function
The error says that
The operand must be of reference type but Integer is of value type
What is the explanation of what I have done wrong?
TryParse doesn't accept more than 10 digits so for example, an input of "12345678901" won't be accepted. How do I fix this?

Let's try to understand the differences between TryCast, Convert and TryParse.
TryCast
This function will attempt to convert one object into another type, as long as it is a reference type.
Dim MyNewObject = TryCast(MyObject, MyReferenceClass)
If IsNothing(MyNewObject) Then
MessageBox.Show("Impossible to cast")
End If
Since Integer is a value type, it will not work, so we have to figure something out...
Convert
Convert Class on MSDN
From MSDN:
Converts a base data type to another base data type.
So we can try:
Dim myInt = Convert.ToInt32(MyObject)
The problem is that it will generate an exception InvalidCastException if it's impossible to do the conversion.
TryParse
This function is trying to convert a String into something you want. And it will not generate an exception:
Dim myInt As Integer = 0
If Not Integer.TryParse(MyString, myInt) Then
MessageBox.show("This is not an integer")
End If
Limitation
Converting a String into a Integer can sometimes be tricky... If the String represents a number that is greater or lesser than Integer.MaxValue and Integer.MinValue, you will end up with no conversion...
So you can go with a Double:
Double.TryParse(MyString, MyDouble)
Or personally, if you know that it will be a number, use Decimal:
Decimal.TryParse(MyString, MyDecimal)
See Decimals on MSDN
Decimal still has a Max and Min value, according to MSDN:
The Decimal value type represents decimal numbers ranging from positive 79,228,162,514,264,337,593,543,950,335 to negative 79,228,162,514,264,337,593,543,950,335. The Decimal value type is appropriate for financial calculations that require large numbers of significant integral and fractional digits and no round-off errors.
Convert.ChangeType
This one is also interesting, but is a bit weird...

You are attempting to perform TryCast against an Integer, which is a value type. TryCast works only on reference types, such as (but not limited to) a Class, Object, or String type.
If you are trying to convert the input parameter to an Integer, you might try one of the methods in the Convert class, such as Convert.ToInt32() or Integer.TryParse.

Instead of TryCast, use TryParse:
Function ValidateInput(Var_In As String) As Integer
Dim iNum As Integer
If (Integer.TryParse(Var_In, iNum)) Then
Return iNum
Else
Return vbNull
End If
End Function

Much better is to use TryParse:
Function ValidateInput(Var_In As String) As Integer
Dim num as Integer
If Not Integer.TryParse(Var_In, num) Then
Return vbNull
Else
Return num
End If
End Function

I'm late to the discussion, but if anyone lands here (like I did) looking for a quick & dirty solution, here a function I'm using for simple cell validation in a DataGridView control.
Function TryTypeFit(theString As String, theType As Type, ShowError As Boolean) As Boolean
Dim TempReturn As Boolean = False
Dim TempObject As Object
Try
TempObject = Convert.ChangeType(theString, theType)
TempReturn = True
Catch ex As Exception
' didn't work
TempReturn = False
If ShowError Then
Dim eMessage As String = "Error: Value must be castable to a " & CStr(theType.Name)
MsgBox(eMessage)
End If
End Try
100:
Return TempReturn
End Function

Related

VB Nullables and Nothings

I researched C#'s default keyword equivalence in VB.NET and came across this question.
Then I got curious. Some background - I'm working with parsing an excel spreadsheet, where many columns can be null, and there is certainly a difference for me between an integer column being 0 and being null.
I wrote a little parse method:
Function Test(ByVal i As String) As Nullable(Of Integer)
Dim j As Integer
If Integer.TryParse(i, j) Then
Return j
Else
Return Nothing
End If
End Function
this seems to work correctly. But here, I can return an Integer if i want:
Function Test(ByVal i As String) As Nullable(Of Integer)
Return 2 'or Return Nothing
End Function
which I can in C# as well:
static void Main(string[] args)
{
int? j = Test("1");
}
public static int? Test(string i)
{
return 2; //or return null
}
In the code above, if I change j to an int, I'll get a compile-time conversion error, which makes total sense.
Now my question - in VB, if i attempt a similar approach:
Sub Main()
Dim j As Integer = Test("hello")
End Sub
Function Test(ByVal i As String) As Nullable(Of Integer)
Dim j As Integer
Return If(Integer.TryParse(i, j), j, Nothing)
End Function
or, in my test case where i is not an Integer it can be rewritten as:
Function Test(ByVal i As String) As Nullable(Of Integer)
Return DirectCast(Nothing, Integer)
End Function
because of the way Nothing works in VB.NET, this code compiles and runs without error -- j is set to Integer's default 0.
This feels so dirty to me. In this scenario you can somewhat alter the method's intentions without any warnings or errors. I'm just curious I suppose, is this an unintentional by-product of the way Nothing works in VB, or is this the intended purpose?
Your VB.Net code compiles because you're using late binding, which allows changing the type of a variable at runtime.
If you compile your code with OPTION STRICT ON, you'll get a compiler error like:
Option Strict On disallows implicit conversions from 'Integer?' to 'Integer'.
You can't assign NULL to a value type in VB.Net in which it instantiates that type with its default value. In your case you are not creating a NULL Integer, but an integer that holds the default value of 0.
Another good note: turn Option Strict On

Assign Nothing to a Short variable in VB.NET

I have a below line of code like this.
Private Sub SomeFunction(ByRef SomeShortVariable As Nullable(Of Short))
Dim SomeStringVariable As String = "" 'Let's assume it is "", that's how I am getting it in real time code
SomeShortVariable = IIf(SomeStringVariable = "", Nothing, SomeStringVariable) 'I want to set SomeShortVariable to Nothing but I am getting 0
End Sub
The variable SomeShortVariable is always sets to 0 even though I want it to be Nothing.
I know Short by default will set the variable to 0.
But how can I make it Nothing. I am using .NET 2.0.
Make SomeShortVariable a Nullable(Of Short) variable.
EDIT:
Also your statement should look like this:
SomeShortVariable = If(String.IsNullOrEmpty(SomeStringVariable), Nothing, New Nullable(Of Short)(Short.Parse(SomeStringVariable)))
SECOND EDIT:
If you're using Visual Studio 2005 the above won't work, because the If operator was only introduced in VS2008. So what you'll have to do is this:
If String.IsNullOrEmpty(SomeStringVariable) Then
SomeShortVariable = Nothing
Else
SomeShortVariable = Short.Parse(SomeStringVariable)
End If
Of course you will want to validate that SomeStringVariable is a numeric string first. :)
Regarding your update, well, that's cause it's Short, and not Nullable(Of Short) in the parameter. Make it nullable of Short and you are done. Although I would refactor to avoid ByRef, have a string parameter SomeStringVariable and return a Nullable(Of Short). Then things would start to make more sense.
Private Shared Function SomeFunction(SomeStringVariable As String) _
As Nullable(Of Short)
If String.IsNullOrEmpty(SomeStringVariable) Then
Return Nothing
Else
Return Convert.ToInt16(SomeStringVariable)
End If
End Function
EDIT: Actually shorthand syntax won't work in this case, for the reasons I outlined in my comment regarding change to If. Just don't use shorthand.
Watch out for the recommendations to use Convert or Parse on cases where the input string could be something other than an empty string but not a number (any user supplied input). It is typically better to use TryParse unless you are absolutely sure someone hasn't passed something in that you aren't expecting. Consider the following:
Dim someString = "a"
Dim someShort as new Nullable(Of Short)
Dim tempShort as Short
Console.WriteLine(someShort)
If Integer.TryParse(someString, tempShort) then
someShort = tempShort
end if
console.WriteLine(someShort)
if Not String.IsNullOrEmpty(someString) then
someShort = Short.Parse(someString) ' Throws FormatException
end if
Console.WriteLine(someShort)

Reflection - creating datatype from a string literal value

Please see the code below:
Public Function Test()
Dim o As Object = getVariable("Integer")
If TypeOf o Is Integer Then
'Do some processing on the integer
ElseIf TypeOf o Is Decimal Then
'Do some processing on the integer
End If
End Function
Public Function getVariable(ByVal strDataType As String)
If strDataType = "Integer" Then
Return New Integer
ElseIf strDataType = "Decimal" Then
Return New Decimal
ElseIf strDataType = "Double" Then
Return New Double
End If
End Function
I suspect there is an easier way (fewer lines of code) of doing this with Reflection?
You can use Type.GetType together with Activator.CreateInstance:
Public Function getVariable(ByVal strDataType As String)
Return Activator.CreateInstance(Type.GetType(strDataType))
End Function
For strDataType you need to be using System.Int32, System.Decimal and System.Double respectively. If you want to keep it as Integer etc., you need to incorporate string translation, for example, have a Dictionary(Of String, String), with entries like ("Integer", "System.Int32").
The real question is what ars you trying to solve? Activator will work. Are trying to create a factory or an IOC container. Could you provide more detail? You could also create a dictionary were the key is of type string and then item stores delegates used to actual create the type.

ByRef in VB.NET

I have written the following code in VB.NET:
Dim obj As Object
obj = "00"
Test(obj)
MsgBox(obj)
Private Sub Test(ByRef num As Integer)
End Sub
Private Sub Test(ByVal num As Integer)
End Sub
When the value "00" is passed "ByRef" in the method "Test" it converts to 0. But if the value "00" is passed "ByVal" it keeps the same value as "00". How the passed value is being converted only depending of the signature?
In VB6 although the default passing type is "ByRef", still the same code keeps the same value("00")
Could anybody explain the reason behind this contradictory behaviour in VB6 and VB.NET?
The way you are doing it, the ByRef changes the type of the object from string to integer. By default, integer do not have trailling "0" when covnerted to strings.
This example below might help you understand what is hapenning.
Sub Main()
Dim o1 As Object = "00"
Dim o2 As Object = "00"
Console.WriteLine(o1.GetType().ToString())
Test1(o1)
Console.WriteLine(o1.GetType().ToString())
Console.WriteLine(o2.GetType().ToString())
Test2(o2)
Console.WriteLine(o2.GetType().ToString())
Console.ReadLine()
End Sub
Sub Test1(ByVal num As Integer)
End Sub
Sub Test2(ByRef num As Integer)
End Sub
Output
System.String
System.String
System.String
System.Int32
I suggest you always turn Option Strict On, this will remove a lot of confusion.
The object is of type System.String. It cannot be passed ByRef to a method, it is of the wrong type. So the compiler has to work around it and rewrites the code:
Dim obj As Object
obj = "00"
Dim $temp As Integer
$temp = CInt(obj)
Test($temp)
obj = $temp '' <=== Here
MsgBox(obj)
The indicated statement is the one that changes the object from a string to an integer. Which, converted again to a string by the MsgBox() call, produces "0" instead of "00".
Notable is that C# does not permit this and generate a compile error. This rewriting trick is rather nasty, if the method itself changes the original object then you'll have a very hard time guessing what is going on since that doesn't change the passed argument value.
ByRef means that value passes by reference and in function will be used the same value what has been sent.
ByVal means that value passes by value (function creates a copy of passed value) and you use only copy of value.

In vb.net, isnumeric cannot recognize percentage string as number

I have some customized string formats of double values. I need to convert these strings to double values at some point (Note that these strings may contain error messages other than numbers). So I first check if these strings are numbers, and if yes, then convert them to double. Isnumeric works for my first customized string format. But I found that is numeric cannot recognize percentage strings. I wonder why isnumeric understands () but cannot understand %. And what is the better way to convert "(100.00)" and "50%" to doubles other than Cdbl?
Private Format As String = "###,###,###,##0.00;(###,###,###,##0.00);0" 'negative values are in the format (number)
Private rateFormat as string="p"
Dim str1 as string=-100.0.Tostring(Format) 'str1=(100.0)
Dim str2 as string=0.5.Tostring(rateFormat) 'str2=50%
Dim value1,value2 as double
If isnumeric(str1)
value1=Cdbl(str1) 'True. value1=-100.0
else
value1=Double.MinValue
End If
If isnumeric(str2)
value2=cdbl(str2) 'False.value2=Double.Minvalue
else
value2=Double.MinValue
End If
First, IsNumeric and C<whatever> generally aren't used because they can provide unexpected outputs. Typically you would use Double.TryParse or Convert.ToDouble (or whatever type) because they reliably parses numbers as you would expect; you just have to make sure it's in the right format.
Also, those methods are more efficient because you can determine if a string is a number and parse it all in one go. Just note that Parse and Convert throw exceptions when the format is wrong.
Now, as far as I know, there's know built-in way convert from percent notation. I went ahead and wrote three methods (current culture, any culture, invariant culture) that tested fine with 0.5 and -0.5 on the four possible locations of the percent symbol.
'these will return Double.NaN if they fails
'this parses based on the current culture
Public Function ParseDoublePercent(ByVal input As String) As Double
Return ParseDoublePercent(input, System.Globalization.NumberFormatInfo.CurrentInfo)
End Function
'invariant culture
Public Function ParseDoublePercentInvariant(ByVal input As String) As Double
Return ParseDoublePercent(input, System.Globalization.NumberFormatInfo.InvariantInfo)
End Function
'this parses based on a specified culture
Public Function ParseDoublePercent(ByVal input As String, ByVal provider As IFormatProvider) As Double
If String.IsNullOrEmpty(input) OrElse input.Length < 3 Then Return Double.NaN 'minimum length of string is 2 (0%)
'get some information about how the percent should be formatted
Dim format As System.Globalization.NumberFormatInfo = System.Globalization.NumberFormatInfo.GetInstance(provider)
'how this will be parsed will first depend on if it's positive or negative
Dim isNegative As Boolean = input.Substring(0, 1) = "-"
Dim percentPattern As Integer = If(isNegative, format.PercentNegativePattern, format.PercentPositivePattern)
'i'm using convert.todouble because I don't want to use NumberStyles in Double.TryParse
Try
Select Case percentPattern
Case 0 'n %, such as -50.00 %, 50.00 %
Return Convert.ToDouble(
input.Replace(" " & format.PercentSymbol, "e-" & format.PercentDecimalDigits),
provider)
Case 1 'n%, such as -50.00%, 50.00%
Return Convert.ToDouble(
input.Replace(format.PercentSymbol, "e-" & format.PercentDecimalDigits),
provider)
Case 2 '%n, such as -%50.00, %50.00
'need to do some special parsing just in case there are incorrect percent signs in the middle of the number
'dont want to just replace all of them
Return Convert.ToDouble(
input.Remove(If(isNegative, 1, 0), 1) & "e-" & format.PercentDecimalDigits, provider)
Case 3 '% n, such as '%-50.00, % 50.00
Return Convert.ToDouble(input.Remove(0, 1) & "e-" & format.PercentDecimalDigits, provider)
Case Else
Return Double.NaN 'should not happen
End Select
Catch ex As FormatException 'an exception is thrown when the string fails to parse
Return Double.NaN
End Try
End Function
isnumeric returns a Boolean value indicating whether an expression can be evaluated as a number. In your case % is not a number, so the error. Try the following code.
If isnumeric(str2.Substring(0,str2.length-2))
IsNumeric returns True if the data type of Expression is Boolean, Byte, Decimal, Double, Integer, Long, SByte, Short, Single, UInteger, ULong, or UShort, or an Object that contains one of those numeric types. It also returns True if Expression is a Char or String that can be successfully converted to a number.
IsNumeric returns False if Expression is of data type Date or of data type Object and it does not contain a numeric type. IsNumeric returns False if Expression is a Char or String that cannot be converted to a number.
() is, in fact, a numeric format. It's used in bookkeeping to represent negative values, instead of "-".
See http://blog.accountingcoach.com/use-of-parentheses/, or
in fact, a numeric format.In your case % is not a number, so the error.