다음을 통해 공유


VB.NET Type conversions (Part 1)

Introduction

Developers work with various types of data ranging from displaying information collected from user inputting which is then stored in a container, reading information from various sources such as web services, native files to databases. One thing is common, at one point the data is represented as an object and usually a string. In this article developers will learn how to work with raw data to convert to a type suitable for tasks in Visual Studio solutions using defensive programming which means in most task data read or written may not be in the expected format to be stored in designated containers like databases, json, xml or another format.

Information and code samples are geared towards both novice and intermediate skill level which are demonstrated in unit test, Windows Forms and WPF projects. In some cases, there will be class projects which can be used in a Visual Studio solution by copying one of the class projects into an existing Visual Studio solution followed by (if needed) changing the .NET Framework version in the class project to match the .NET Framework used in an existing Visual Studio solution. The base .NET Framework used in code in this article is 4.7.2. If a Framework below 3.5 is used code presented may not work, for example String. IsNullOrWhiteSpace was first introduced in Framework 3.5.

Language extension methods are utilizing in many operations which in some cases utilize LINQ or Lambda, when reviewing these methods do not to hung up on the complexity, instead if the method(s) work simply use them.

The code presented should not be considered that they belong to one platform when presented in ASP.NET, WPF or Windows Forms. For example, exploring code in a WPF project may reveal a useful regular expression pattern or a unit test may lead to a method useful in a Windows Form project

Conventions 

All code presented uses the following directives, Option Strict On, Option Infer On. Using these directives assist with having quality code.

A common practice with novice developers to programming or have coded in perhaps JavaScript are not use to strong typing as shown in the code below.

A common practice with novice developers to programming or have coded in perhaps JavaScript are not use to strong typing as shown in the code below.

Option Strict Off
Option Infer Off
Public Class  Form1
    Private Sub  Button1_Click(sender As Object, e As  EventArgs) _
        Handles Button1.Click
 
        Dim someInteger = CInt("Does not represent an Integer")
 
    End Sub
End Class

In the code above someInteger variable is an object, not a String which compile but will throw a runtime exception because there was no type checking.

In the following code sample Option Strict On will not compile as Visual Studio wants a type.

Option Strict On
Option Infer Off
Public Class  Form1
    Private Sub  Button1_Click(sender As Object, e As  EventArgs) _
        Handles Button1.Click
 
        Dim someInteger = CInt("Does not represent an Integer")
 
    End Sub
End Class

Adding the type.

Option Strict On
Option Infer Off
Public Class  Form1
    Private Sub  Button1_Click(sender As Object, e As  EventArgs) _
        Handles Button1.Click
 
        Dim someInteger As Integer  = CInt("Does not represent an Integer")
 
    End Sub
End Class

Visual Studio will allow this code to compile even though the conversion will fail as "Does not represent an Integer" can not be represented as an Integer.

By setting Option Strict On provides a decent base for writing quality code. Later assertion will be taught to protect against runtime exceptions when a value can not be converted from one type to another type without any runtime exceptions.

The following code example shows the basics for testing if a string can be converted to an Integer.

Option Strict On
Option Infer On
Public Class  Form1
    Private Sub  Button1_Click(sender As Object, e As  EventArgs) _
        Handles Button1.Click
 
        Dim userInput As String  = "Does not represent an Integer"
 
        If Not  String.IsNullOrWhiteSpace(userInput) Then
            Dim someInteger As Integer  = 0
            If Integer.TryParse(userInput, someInteger) Then
                ' converted
            Else
                ' failed to convert
            End If
        Else
            ' variable has no content
        End If
    End Sub
End Class

Naming variables and controls 

Common practice for novice developers is to provide meaningless names for variable and controls which does not lend to easily reading code later on which leads to code which is difficult or impossible to maintain. Code presented in this article uses a simple convention, name variables and controls so that a non-developer can read your code and have an idea what variables and controls represent. For example for a input used to obtain a customer’s first name, firstNameTextBox rather than TextBox1.

Public Class  Form1
    Private Sub  Button1_Click(sender As Object, e As  EventArgs) _
        Handles Button1.Click
 
        Dim firstName As String  = firstNameTextBox.Text
 
    End Sub
End Class

Note in the last code example the button has a generic name, does not describe what the button is for. Naming controls and events are both important so here the button Click event has a meaningful name.

Public Class  Form1
    Private Sub  processCustomerInputButton_Click(sender As Object, e As  EventArgs) _
        Handles processCustomerInputButton.Click
 
        Dim firstName As String  = firstNameTextBox.Text
        Dim lastName As String  = lastNameTextBox.Text
 
    End Sub
End Class

When naming variables in general use names that make it apparent the data type. Some developers prefer a prefix e.g. for a string strFirstName where when defining a variable today in Visual Studio hovering over a variable will indicate it’s type so rather than strFirstName use firstName which 99 percent of the time is a string. In short lose the three-character prefix, give variables meaning full names.

Naming variables can change dependent on scope e.g. a private variable with a leading underscore quickly indicates you are working with a privately scoped variable e.g. _firstName while firstName would be in scope of the current method. When passing arguments/parameters to a method consider a consistent naming convention.

In the following code sample shows using an underscore for a Private class level variable which is set by a parameter passed in via the new constructor which is available to methods within this class.

Public Class  DataOperations
    Public Sub  New()
 
    End Sub
    Private _customerIdentifer As Integer
    ''' <summary>
    ''' Setup for working with a specific customer
    ''' </summary>
    ''' <param name="pCustomerIdentifier">Existing Customer primary key</param>
    Public Sub  New(pCustomerIdentifier As Integer)
        _customerIdentifer = pCustomerIdentifier
    End Sub
End Class

String assertion

Anytime required input is required for instance to create an object always assume one or more inputs do not have values. For instance, adding a new customer which requires first and last name along with an email address using TextBox controls for input and a button to create the new customer.

First attempt which asserts each TextBox, if even one is empty the requirement for all inputs to be entered has not been meant.

Public Class  Form1
    Private Sub  addCustomerButton_Click(sender As Object, e As  EventArgs) _
        Handles addCustomerButton.Click
 
        If String.IsNullOrWhiteSpace(firstNameTextBox.Text) OrElse
           String.IsNullOrWhiteSpace(lastNameTextBox.Text) OrElse
           String.IsNullOrWhiteSpace(personalEmailAddressTextBox.Text) Then
 
            MessageBox.Show("All fields are required")
 
        Else
 
            Dim customer As New  Customer With
                {
                    .FirstName = firstNameTextBox.Text,
                    .LastName = lastNameTextBox.Text,
                    .EmailAddress = personalEmailAddressTextBox.Text
                }
 
        End If
    End Sub
End Class

Second attempt validates all inputs (TextBox controls) are populated using Enumerable.Any method. Which works well when all inputs are required.

Public Class  Form1
    Private Sub  addCustomerButton_Click(sender As Object, e As  EventArgs) _
        Handles addCustomerButton.Click
 
        If Controls.OfType(Of TextBox).Any(Function(textBox) String.IsNullOrWhiteSpace(textBox.Text)) Then
 
            MessageBox.Show("All fields are required")
 
        Else
 
            Dim customer As New  Customer With
                {
                    .FirstName = firstNameTextBox.Text,
                    .LastName = lastNameTextBox.Text,
                    .EmailAddress = personalEmailAddressTextBox.Text
                }
 
        End If
    End Sub
End Class

Third attempt, using control validation. In the code sample below Control.Validated event is used along with ContainerControl.ValidateChildren Method to cause in this case the firstNameTextBox to perform validation.

Public Class  Form1
    Private Sub  addCustomerButton_Click(sender As Object, e As  EventArgs) _
        Handles addCustomerButton.Click
 
        If ValidateChildren() Then
            MessageBox.Show("Go to go")
        End If
 
    End Sub
 
    Private Sub  firstNameTextBox_Validating(sender As Object, e As  CancelEventArgs) _
        Handles firstNameTextBox.Validating
 
        If String.IsNullOrWhiteSpace(firstNameTextBox.Text) Then
            ErrorProvider1.SetError(firstNameTextBox, "first name is required")
            e.Cancel = True
        Else
            ErrorProvider1.SetError(firstNameTextBox, "")
            e.Cancel = False
        End If
 
    End Sub
 
End Class

String to numerics

Once a required string has been established to have content usually the value will be stored as a string or converted to another type. In the following WPF window there are three TextBox controls.

The first TextBox assertion is done by setting up an event for PreviewTextInput. In the code behind a Regular Expression is used to determine if the text entered can be represented as an Integer.

Private Shared  ReadOnly _regex As New  Regex("[^0-9.-]+")
Private Shared  Function IsTextAllowed(ByVal text As String) As  Boolean
    Return _regex.IsMatch(text)
End Function
''' <summary>
''' Used to prevent information entered to be non-integer values
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
Private Sub  AssertIntegerTextBox_OnPreviewTextInput(
    sender As  Object,
    e As  TextCompositionEventArgs)
 
    e.Handled = _regex.IsMatch(e.Text)
 
End Sub

There is still a need to determine if there has been a value entered which in this case is done using String.IsNullOrWhiteSpace as in examples done above.

Private Sub  IntegerOnlyTextBox_OnClick(
    sender As  Object,
    e As  RoutedEventArgs)
 
    If String.IsNullOrWhiteSpace(assertIntegerTextBox.Text) Then
        MessageBox.Show("No value")
    Else
        MessageBox.Show($"Value is {assertIntegerTextBox.Text}")
    End If
 
End Sub

In the middle TextBox control a conversion is performed without checking to see if the value can be represented as an Integer. Novice developers tend to do this at least once without a try/catch. Then not knowing any better will not only surround this code with a try/catch they will have an empty catch. About the only time it is okay to have an empty catch is when traversing a folder structure were the operator does not have permissions to one or more folders.

In the bottom TextBox a decimal has been entered were an Integer was expected. An option is to use Decimal.TryParse, use Decimal.Truncate to get the Integer value which may be okay in some task while others this may not be okay dependent on business requirements along with operator thinking what happen to the decimal entered?

Private Sub  tryParseAssertionButton_Click(
    sender As  Object,
    e As  RoutedEventArgs) Handles tryParseAssertionButton.Click
 
    If Not  String.IsNullOrWhiteSpace(tryparseIntegerTextBox.Text) Then
 
        Dim value As Integer  = 0
 
        If Integer.TryParse(tryparseIntegerTextBox.Text, value) Then
            MessageBox.Show($"You entered a valid integer: {value}")
        Else
            MessageBox.Show($"'{tryparseIntegerTextBox.Text}' could not be converted to an integer")
        End If
 
    Else
        MessageBox.Show("Please enter a value")
    End If
 
End Sub

In a Windows Form project one method to assert that a input in a TextBox is an Integer is using KeyPress event of the TextBox.

Private Sub  AssertionOnInteger_KeyPressForOnlyIntegers(
    sender As  Object,
    e As  KeyPressEventArgs) _
    Handles keyPressAssertionOnIntegerTextBox.KeyPress
 
    '97 - 122 = Ascii codes for simple letters
    '65 - 90  = Ascii codes for capital letters
    '48 - 57  = Ascii codes for numbers
 
    If Asc(e.KeyChar) <> 8 Then
        If Asc(e.KeyChar) < 48 Or Asc(e.KeyChar) > 57 Then
            e.Handled = True
        End If
    End If
 
End Sub

Note there are other ways to perform assertion on string and converting to numbers using custom validator components and by implementing a component with IExtenderProvider Interface.

Generics

The best method to perform conversions is to have same named methods (or extension methods )for each type for maintainability although it's possible to write one method for various types.

The following extension method can be used against Integer, Decimal, Double and Long.

''' <summary>
''' Converts a string to a Nullable type as per T
''' </summary>
''' <typeparam name="T">Type to convert too</typeparam>
''' <param name="sender">String to work from</param>
''' <returns>Nullable of T</returns>
''' <example>
''' <code>
''' Dim value = "12".ToNullable(Of Integer)
''' If value.HasValue Then
'''   - use value
''' Else
'''   - do not use value
''' </code>
''' </example>
<CompilerServices.Extension>
Public Function ToNullable(Of T As Structure)(sender As String) As T?
 
    Dim result As New T?()
 
    Try
        If Not String.IsNullOrWhiteSpace(sender) Then
            Dim converter As TypeConverter = TypeDescriptor.GetConverter(GetType(T))
            result = CType(converter.ConvertFrom(sender), T)
 
        End If
    Catch
        ' don't care, caller should use HasValue before accessing the value.
    End Try
 
    Return result
 
End Function

Example usage converting from string to integer. If the value of ToNullableFailGracefullyTextBox TextBox text at present is "Karen", HasValue will return False while if the value in the TextBox was 12 HasValue would be True.

Dim nullableIntegerValue As Integer? =
        ToNullableFailGracefullyTextBox.Text.ToNullable(Of Integer)
 
If nullableIntegerValue.HasValue Then
    Dim integerValue = nullableIntegerValue.Value
    MessageBox.Show($"Converted successfully")
Else
    MessageBox.Show($"{ToNullableFailGracefullyTextBox.Text} could not be converted")
End If

To use the extension method to test if the TextBox contains a decimal value.

ToNullableFailGracefullyTextBox.Text = "12.4"
Dim nullableDecValue = ToNullableFailGracefullyTextBox.Text.ToNullable(Of Decimal)

To try out the various generic methods, see the project ParsingAndConvertingWindowsForms.

String array to numeric array

In this section conversion from string array to Integer arrays are discussed, in the repository there are mirror method for Long, Decimal and Double in NumericHelpers project.

Working with arrays can prove challenging when first starting out having to convert a string array which should contain only Integer, Decimal or Double values in each element of an array. A common practice is to use code shown below.

Dim stringArray = {"123", "456",  "789"}
Dim intList = stringArray.
        ToList().
        ConvertAll(Function(stringValue) Integer.Parse(stringValue))

Which will convert each element in the string array by first accessing th e array using .ToList then using ConvertAll to parse using Integer.Parse each element into an Integer.

Since each element can represent Integers the conversion is successful. If an element can not be converted a runtime exception is thrown. In the following code sample one element can not be converted because there is a space in between two numbers in the middle element.

Dim stringArray = {"123", "45 6",  "789"}
Dim intList = stringArray.
        ToList().
        ConvertAll(Function(stringValue) Integer.Parse(stringValue))

Assertion

Anytime there is a possible chance something is not as expected, in this case if one or more elements can not be converted this is were assertion comes in.

In the following example rather than using Integer.Parse Integer.TryParse is used where if a element can not be converted Integer.TryParse returns False. Also note TryParse can be used in other numeric types along with DateTime.

Enumerable.All determines whether all elements of a sequence satisfy a condition. In this case all string values can be converted to Integer. If one or more can not be converted then All returns False.

Dim testValue As Integer
Dim stringArray = {"123", "45 6",  "789"}
 
If stringArray.All(Function(input) Integer.TryParse(input, testValue)) Then
    Console.WriteLine("Safe to perform conversion")
Else
    Console.WriteLine("Not safe to perform conversion")
End If

If this type of assertion is needed often a function can be created and placed in a common code module.
 

Public Module  ArrayHelpers
    Public Function  CanConvertToIntegerArray(sender() As String) As  Boolean
        Dim testValue As Integer
        Return sender.All(Function(input) Integer.TryParse(input, testValue))
    End Function
End Module

If this type of assertion is needed often a function can be created and placed in a common code module.

Public Module  ArrayHelpers
    Public Function  CanConvertToIntegerArray(sender() As String) As  Boolean
        Dim testValue As Integer
        Return sender.All(Function(input) Integer.TryParse(input, testValue))
    End Function
End Module

Working example

Dim stringArray = {"123", "45 6",  "789"}
 
If CanConvertToIntegerArray(stringArray) Then
    Console.WriteLine("Safe to perform conversion")
Else
    Console.WriteLine("Not safe to perform conversion")
End If

The function could also be written as a Language Extension

Public Module  ArrayHelpers
    <Runtime.CompilerServices.Extension>
    Public Function  CanConvertToIntArray(sender() As String) As  Boolean
        Dim testValue As Integer
        Return sender.All(Function(input) Integer.TryParse(input, testValue))
    End Function
End Module

Then used as shown here.

Dim stringArray = {"123", "45 6",  "789"}
 
If stringArray.CanConvertToIntArray() Then
    Console.WriteLine("Safe to perform conversion")
Else
    Console.WriteLine("Not safe to perform conversion")
End If

Since Integer is not the only type that may be in an array language extensions can be created for all other numeric types a developer may work with, example, Decimal.

Public Module  ArrayHelpers
    <Runtime.CompilerServices.Extension>
    Public Function  CanConvertToDecimalArray(sender() As String) As  Boolean
        Dim testValue As Decimal
        Return sender.All(Function(input) Decimal.TryParse(input, testValue))
    End Function
End Module

Safely converting from String array to Integer array

There are two paths to take when converting a String array to an Integer array, perserve all elements by setting elements which can not be converted to Integer to a default value such as 0 while the other path is to create an Integer array with only the values which can be converted to Integers.

Preserve all elements

The first path, preserve all elements where non-integer values will have a default value, in this case 0.

Rather than creating a function this will be done with a function setup as a language extension method. This is done by creating a code module as shown below.

ToIntegerPreserveArray()

Public Module  IntegerArrayExtensions
    <Runtime.CompilerServices.Extension>
    Public Function  ToIntegerPreserveArray(sender() As String) As  Integer()
        Return Array.ConvertAll(sender,
                Function(input)
                    Dim integerValue As Integer
                    Integer.TryParse(input, integerValue)
                    Return integerValue
                End Function)
    End Function
End Module

The magic happens using Array.ConvertAll, first parameter is the String array, second parameter is a function which uses Integer.TryParse where if an element can be converted the variable integerValue will contain the Integer value while on failure to convert a String element to Integer 0 is returned.

The follow code sample provides a string array with some elements which can not be converted to Integer values.

Public Class  Form1
    Private Sub  exampleButton_Click(sender As Object, e As  EventArgs) _
        Handles exampleButton.Click
 
        Dim stringArray As String() =
                {
                    "2",
                    "4B",
                    Nothing,
                    "6",
                    "8A",
                    "",
                    "1.3",
                    "Karen",
                    "-1"
                }
        Dim integerArray = stringArray.ToIntegerPreserveArray()
 
        For index As Integer  = 0 To  stringArray.Length - 1
            Console.WriteLine(
                $"Index: {index} String value: " &
                $"'{stringArray(index)}' Converted too: {integerArray(index)}")
        Next
 
    End Sub
End Class

The results show the index, original string value and the converted value.

Index: 0 String value: '2' Converted too: 2
Index: 1 String value: '4B' Converted too: 0
Index: 2 String value: '' Converted too: 0
Index: 3 String value: '6' Converted too: 6
Index: 4 String value: '8A' Converted too: 0
Index: 5 String value: '' Converted too: 0
Index: 6 String value: '1.3' Converted too: 0
Index: 7 String value: 'Karen' Converted too: 0
Index: 8 String value: '-1' Converted too: -1

Taking a slightly different path on obtaining output which has nothing to do with the conversion process but shows how developers can use chaining of statements together as done in ToIntegerPreserveArray.

Dim integerArray = stringArray.ToIntegerPreserveArray()
 
integerArray.
    ToList().
    Select(Function(number, index) New  With
        {
            .Index = index,
            .Value = number
        }).ToList().
    ForEach(Sub(data)
        Console.WriteLine(
            $"Index: {data.Index} " &
            $"Integer Value: {data.Value,2} " &
            $"Original Value: '{stringArray(data.Index)}'")
            End Sub)

Breaking down the code for displaying information on the converted array.

  1. Use ToList extension method to gain access to Select extension method.
  2. Using a function in the Select method use two parameters, the first parameter represents an Integer of a element in IntegerArray while the second parameter is the index of the element in the array.
  3. Within this function create an anonymous type, .Index represents the element index in the array while .Value represents the element which is an Integer.
  4. Using the language extension .ForEach iterate the anonymous type created in the function and display the same information shown in the prior code sample.

Shrink array to only true Integer values

In the prior example the resulting Integer array will be the exact size as the String array. In this section if a string element can not be converted to an Integer value those elements are discarded.

Consider reading a String array which has elements which are not Integer type and are consistant e.g. reading lines from a file.

1,Karen,Payne,1956
2,Bob,Jones,1977
3,Mary,Adams,1984

The task is to obtain the first element and the last element. In the code sample below the program reads a file named TextFile1.txt which resides in the same folder as the program's executable with the content shown above.

Using File.ReadAllLines which returns a string array of lines in the file where each item to read is delimited by a comma.

With a For-Each on each iteration a extension method, ToStringArray converts the string representing a line in the string array to a string array. This is followed by invoking ToIntegerArray which will return only integer values.

Public Class  Form2
    Private Sub  exampleButton_Click(sender As Object, e As  EventArgs) _
        Handles exampleButton.Click
 
        Dim fileName = Path.Combine(
            AppDomain.CurrentDomain.BaseDirectory, "TextFile1.txt")
 
        Dim linesFromFile = File.ReadAllLines(fileName)
 
        For Each  line As  String In  linesFromFile
            Dim items = line.ToStringArray().ToIntegerArray
            Console.WriteLine($"Id: {items(0)}, Birth Year {items(1)}")
        Next
    End Sub
End Class

Here is the definition for ToStringArray which is a simple function which by default accepts a string which is split into a string array delimited by a comma which is defined as Optional meaning if the string is delimited by a comma the parameter need not be passed while if the string is delimited by another delimiter other than a comma the delimiter needs to be passed.

<Runtime.CompilerServices.Extension>
Public Function  ToStringArray(
    sender As  String,
    Optional separator As Char  = ","c)  As  String()
 
    Return sender.Split(separator)
End Function

Moving on to the language extension ToIntegerArray, to determine if an element can represent an Integer Integer.TryParse checks if the string can be converted when creating an instance of an anonymous type, .IsInteger stored the result of Integer.TryParse while .Value contains the result, if "Amy" is parsed .Value will contain 0 and .IsInteger will be False while a value of 12 will set .Value to 12 and .IsInteger to True.

In turn .Where condition specifies .IsInteger = True which in turn using .Select returns only Integer values.

<Runtime.CompilerServices.Extension>
Public Function  ToIntegerArray(sender() As String) As  Integer()
    Return Array.ConvertAll(sender,
    Function(input)
        Dim value As Integer
        Return New  With
        {
            .IsInteger = Integer.TryParse(input, value),
            .Value = value
        }
    End Function).
    Where(Function(result) result.IsInteger).
    Select(Function(result) result.Value).
    ToArray()
End Function

Determining indexes which do not represent Integers

The following is an inversion of the above where the condition was .IsInteger = True this method does .IsInteger = False along with asking for the index rather than the actual value using GetNonIntegerIndexes.

<Runtime.CompilerServices.Extension>
Public Function  GetNonIntegerIndexes(sender() As String) As  Integer()
    Return sender.Select(
    Function(item, index)
 
        Dim integerValue As Integer
        Return If(Integer.TryParse(item, integerValue),
            New With
                {
                    .IsInteger = True,
                    .Index = index
                },
            New With
                {
                    .IsInteger = False,
                    .Index = index
                }
            )
    End Function).
    ToArray().
    Where(Function(item) item.IsInteger = False).
    Select(Function(item) item.Index).
    ToArray()
 
End Function

Code sample which rather than obtaining values for the first and last element will return the two middle elements which are first and last name indexes.

Public Class  Form2
    Private Sub  exampleButton_Click(sender As Object, e As  EventArgs) _
        Handles exampleButton.Click
 
        Dim fileName = Path.Combine(
            AppDomain.CurrentDomain.BaseDirectory, "TextFile1.txt")
 
        Dim linesFromFile = File.ReadAllLines(fileName)
 
        For Each  line As  String In  linesFromFile
            Dim items = line.ToStringArray().GetNonIntegerIndexes
            Console.WriteLine($"First name: {items(0)}, Last name {items(1)}")
        Next
    End Sub
End Class

String array to Double array

The same logic which works for string to integer array applies to string to double arrays. For instance, to test if a string array can be converted to a string array use CanConvertToIntArray extension method, for testing if a string array can be converted to a double array use CanConvertToDoubleArray. To convert only integer elements in a string array to an integer array discarding non-integer elements ToIntegerArray, and for the same operation with string to double array use ToDoubleArray.

Suppose there is a need to convert a string array of currency values to a pure double array, the following langage extension, FromCurrencyToDoubleArray can perform this operation. The main difference between converting a string array with possible double values to a double array is in the array with currency symbols there is a need to tell TryParse (overload constructor) to indicate the number style and which currency to use. In the following the culture is set to the current culture. If there is a need to perform this conversion on other cultures and overloaded version can be created passing in the culture code, create the culture to perform the operation on the desired culture.

<Runtime.CompilerServices.Extension>
Public Function  FromCurrencyToDoubleArray(sender() As String) As  Double()
Return Array.ConvertAll(sender,
                        Function(input)
                            Dim value As Double
                            Return New  With
                           {
                               .IsDDouble = Double.TryParse(
                                   input, NumberStyles.Number Or  NumberStyles.AllowCurrencySymbol,
                                   CultureInfo.CurrentCulture, value),
                               .Value = value
                           }
                        End Function).
    Where(Function(result) result.IsDDouble).
    Select(Function(result) result.Value).
    ToArray()
End Function

Miscellaneous extensions

The following are helper extensions which are wrappers for joining numeric arrays to a comma delimited string for assisting in either debugging purposes or when there is a need to create a text file were each line consist of a numeric array.

Namespace LanguageExtensions
    Public Module  GeneralHelperExtensions
        <Runtime.CompilerServices.Extension>
        Public Function  IntegerArrayToString(sender() As Integer) As  String
            Return String.Join(",", sender)
        End Function
        <Runtime.CompilerServices.Extension>
        Public Function  DecimalArrayToString(sender() As Decimal) As  String
            Return String.Join(",", sender)
        End Function
        <Runtime.CompilerServices.Extension>
        Public Function  DoubleArrayToString(sender() As Double) As  String
            Return String.Join(",", sender)
        End Function
        <Runtime.CompilerServices.Extension>
        Public Function  ToStringArray(sender As String,
            Optional separator As Char  = ","c)  As  String()
            Return sender.Split(separator)
        End Function
    End Module
 
End Namespace

Unit test

Unit test are critical when writing code especially when creating language extension methods as presented here.  If all unit test pass then in a production application there is a failure where the issue might have something to do with the extension method then rerun the unit test to ensure something was not missed. If something was missed then fix the extension method along with writing new unit test to cover the issue which was not covered originally. If the issue was not from a homegrown extension method this makes it easy to disregard the method(s) and focus on other parts of code logic. 

For instance,  for the following generic extension method test should be created for all types which it may be used for.

<CompilerServices.Extension>
Public Function  ToNullable(Of T As  Structure)(sender As String) As  T?
 
    Dim result As New  T?()
 
    Try
        If Not  String.IsNullOrWhiteSpace(sender) Then
            Dim converter As TypeConverter = TypeDescriptor.GetConverter(GetType(T))
            result = CType(converter.ConvertFrom(sender), T)
 
        End If
    Catch
        ' don't care, caller should use HasValue before accessing the value.
    End Try
 
    Return result
 
End Function

Integer test

''' <summary>
''' Given a string array where some elements can be converted to 
''' Nullable Integer and some elements which can not be converted
''' validate the extension method ToNullable
''' </summary>
<TestMethod>
Public Sub  StringToNullableInteger_Successful()
 
    Dim expected() As Integer? = {2, 6, -1}
 
    Dim nullableResults =
    (
        From item In  StringArrayMixedTypesIntegers
        Select value = item.ToNullable(Of Integer)
        Where value.HasValue
    ).ToList()
 
    Assert.IsTrue(nullableResults.SequenceEqual(expected))
 
End Sub

Double test

''' <summary>
''' Given a string array where some elements can be converted to 
''' Nullable Integer and some elements which can not be converted
''' validate the extension method ToNullable
''' </summary>
<TestMethod>
Public Sub  StringToNullableDouble_Successful()
 
    Dim expected() As Double? = {2.4, 6.7, -1}
 
    Dim nullableResults =
            (
            From item In  StringArrayMixedTypesDoubles
            Select value = item.ToNullable(Of Double)
            Where value.HasValue
            ).ToList()
 
    Assert.IsTrue(nullableResults.SequenceEqual(expected))
 
End Sub

In some cases a failed test is worth it's weight in gold to validate a good test (one that is under the best conditions) actually is a valid test. For example, the following test although simple validate a good test as in this case the test is to see if a string value will fail converting to an Integer.

The test passes (when the string can't be converted) when an a FormatException is thrown validated by ExpectedException attribute testing for FormatException.  The difficulty here is knowing what exception may be thrown as there may be more than one which means the developer must try and throw various exceptions against a method which the developer coded. Better to spend time while coding a solution then a embracing issue while the application is in production.

''' <summary>
''' Attempt to parse a string which can not be converted to an Integer.
''' No assertion requires pass ExpectedException attributive.
''' </summary>
<TestMethod, ExpectedException(GetType(FormatException),
      "Integer.Parse expects an Integer, failed as expected.")>
Public Sub  WhatHappensWhenValueIsNotIntegerNoFormatting_Integer()
    Dim nonInteger = "A"
    Dim result = Integer.Parse(nonInteger)
End Sub

See also

VB.NET: Decimal Places / Significant Figures

C#: Casting and Type Checking

C#: Generic Type Parameters And Dynamic Types

References

Conversions Between Strings and Other Types (Visual Basic)

Type Conversion Functions (Visual Basic)

How to: Convert an Object to Another Type in Visual Basic

Implicit and Explicit Conversions (Visual Basic)

Summary

This article has presented language extension methods to provide consistency for converting strings and string arrays to numeric types which were designed for common task when writing solutions using Visual Basic in Visual Studio in a class project which can be used in your solutions. Also, how to test these extension methods using unit test methods.

Requires

Microsoft Visual Studio

Framework 3.5 or higher

Using these extension methods in your solutions

To use these extension methods, add the project NumericHelpers to your Visual Studio solution which targets .NET Framework 3.5 or higher. Open project property page for your project and the class project property page, if the Framework version differs change one of them so both match. Add a reference to your project for the NumericHelper project followed by compiling the solution to ensure the solution compiles properly. From here add a Import statement which references the class project and begin using the extension methods.

Source code

All source code can be found in the following GitHub repository.