Vb.Net assignment to a Tuple - vb.net

Does VB support assignment to a tuple? If so what is the Syntax?
Private Function GetPeStream(metadataDiagnostics As DiagnosticBag, peStreamProvider As EmitStreamProvider, metadataOnly As Boolean) As (peStream As Stream, signingStream As Stream, selectedStream As Stream)
Return ...
End Function
Dim ret As Stream
Dim peStream as Stream
Dim signingInputStream as Stream
(peStream, signingInputStream, ret) = GetPeStream(metadataDiagnostics, peStreamProvider, metadataOnly)

You can use below syntax for tuples to initialize and assign in single line.
Dim x As Tuple(Of Stream, Stream, Stream)= New Tuple(of Stream,Stream,Stream)(str1,str2,str3)
Important: Make sure you have str1,str2 and str3 instantiated and assigned to values before direct assignment above

You can avoid using Item1, Item2, etc, using the example bellow.
Private Function GetPeStream(metadataDiagnostics As DiagnosticBag, peStreamProvider As EmitStreamProvider, metadataOnly As Boolean) As (peStream As Stream, signingStream As Stream, selectedStream As Stream)
.....
Return (processed_peStream, processed_signingStream,processed_selectedStream)
End Function
Private Function ConsumingGetPeStream()...
'Calling the function
Dim Your_Result_From_GetPeStream = GetPeStream(metadataDiagnostics_value, peStreamProvider_value, metadataOnly_Value)
'Using returned values from function
Your_Result_From_GetPeStream.peStream
Your_Result_From_GetPeStream.signingStream
Your_Result_From_GetPeStream.selectedStream
End Function
A more simple version for better undesrtanding
Public Sub Main()
Dim ReturnFunctionWithTuple = FunctionWithTuple()
Console.WriteLine($"Your long: {ReturnFunctionWithTuple.YourFieldLong}")
Console.WriteLine($"Your date: {ReturnFunctionWithTuple.YourFieldDate}")
Console.WriteLine($"Your bool: {ReturnFunctionWithTuple.YourFiedBoolean}")
End Sub
Public Function FunctionWithTuple() As (YourFieldLong As Long, YourFieldDate As Date, YourFiedBoolean As Boolean)
Return (55, #01/01/2021#, False)
End Function

Declare the tuple variable and the function return type with the generic syntax, so that the types line up. For example:
Public Sub GetTuple()
Dim x As Tuple(Of String, String, Integer)
x = GetData()
End Sub
Public Function GetData() As Tuple(Of String, String, Integer)
Dim y = New Tuple(Of String, String, Integer)("A", "B", 27)
Return y
End Function

In order to do it you have to create a new Tuple that accepts the assignment as shown by PKing and then manually copy each variable one at a time.
Dim x As Tuple(Of Stream, Stream, Stream)
x = GetData()
Dim eStream as Stream = x.Item1
Dim signingInputStream as Stream = x.Item2
Dim Ret as Stream = x.Item3
Of course the assignments would be in a loop and have code dealing with the types.

Related

checking datatype in an overload doesn't work for decimal

I'm using vb.net. I am doing some checking and scrubbing of my data when i get it from the database. I'm using an overload but it doesn't seem to be working for decimal numbers. Decimal numbers get treated as integers.
Public Class CheckData
Public Shared Function Check(row As DataRow, columnName As String, pDefaultValue As Decimal) As Decimal
Dim x As Object = ReplaceDBNullAndColumnExists(row, columnName)
Dim y As Decimal = CDec(Dempsey.fnIsNull.IsNull(x, pDefaultValue))
Return y
End Function
Public Shared Function Check(row As DataRow, columnName As String, pDefaultValue As DateTime) As DateTime
Dim x As Object = ReplaceDBNullAndColumnExists(row, columnName)
Dim y As DateTime = Dempsey.fnIsNull.IsNull(x, pDefaultValue)
Return y
End Function
Public Shared Function Check(row As DataRow, columnName As String, pDefaultValue As Integer) As Integer
Dim x As Object = ReplaceDBNullAndColumnExists(row, columnName)
Dim y As Integer = Dempsey.fnIsNull.IsNull(x, pDefaultValue)
Return y
End Function
Public Shared Function Check(row As DataRow, columnName As String, pDefaultValue As String) As String
Dim x As Object = ReplaceDBNullAndColumnExists(row, columnName)
Dim y As String = Dempsey.fnIsNull.IsNull(x, pDefaultValue)
Return y
End Function
Public Shared Function Check(row As DataRow, columnName As String, pDefaultValue As Boolean) As Boolean
Dim x As Object = ReplaceDBNullAndColumnExists(row, columnName)
Dim y As Boolean = Dempsey.fnIsNull.IsNull(x, pDefaultValue)
Return y
End Function
So if i pass in some data and the type is a string, integer, boolean or datetime it goes to the propert function. if i pass in a decimal it goes to integer. If i set a breakpoint on the public shared function check that is a integer function and do a
row.Table.Columns(columnName).DataType.Name
I get back - "Decimal" (doing this in the immediate window)
So my question is what have i done wrong that it doesn't take it to the decimal overload and return a decimal value. By the way, the actual data value is 37.50.
Thanks
shannon
I use this to populate list.
Public Function populate(mDs As DataSet) As List(Of SR_SalaryRange_Current)
Dim rows As DataRowCollection
Dim drow As DataRow
Dim oSR_SalaryRange_Current As SR_SalaryRange_Current
Dim oSR_SalaryRange_Currents As List(Of SR_SalaryRange_Current) = New List(Of SR_SalaryRange_Current)
Dim dt As New DataTable
Try
dt = mDs.Tables("SR_SalaryRange_Currents")
rows = dt.Rows
For Each drow In rows
oSR_SalaryRange_Current = New SR_SalaryRange_Current
With oSR_SalaryRange_Current
.tblSR_SalaryRange_CurrentID = SitePlumbing.CheckData.Check(drow, "intTblSR_SalaryRange_CurrentID", 0)
.EffectiveDate = SitePlumbing.CheckData.Check(drow, "dtmEffectiveDate", CDate("1/1/1900"))
.WorkWeekHours = SitePlumbing.CheckData.Check(drow, "decWorkWeekHours", 0)
End With
oSR_SalaryRange_Currents.Add(oSR_SalaryRange_Current)
Next
Catch ex As Exception
ErrorMsg = "Populate Error:" & ex.InnerException.ToString
Return oSR_SalaryRange_Currents
End Try
Return oSR_SalaryRange_Currents
End Function
from there it goes into the checkdata that i mentioned before. In the code above it correctly goes to an integer and a datetime when hitting the overload, just isn't doing it for the decimal.
A decimal is implicitly covertable into an integer so the compiler is confused if you have OPTION STRICT set to off. You have two options:
1) Set OPTION STRICT to ON. Then the compiler will not convert implicitly.
2) Pass in a decimal literal like this: 10D (i.e. there is a D on the end telling the compiler it is a decimal).
For example:
'Sub Routine 1
public sub Test(ByVal d as decimal)
end sub
'Sub Routine 2
public sub Test(ByVal i as integer)
end sub
//Client
dim d1 as decimal={Number}D 'where {number} is replaced with a number
dim d2 as decimal={Number} 'where {number} is replaced with a number
Test(d1) 'this will always go to Sub Routine 1 regardless of whether OPTION strict is ON or OFF
Test(d2) 'this will go to Sub Routine 1 if option strict is ON. If it is OFF, then it may go to Sub Routine 2 depending on the size of the number.
I was given a way to work with this by Viorel on another forum. He suggested I do
Check(drow, "decWorkWeekHours", 0#)
and that did indeed work. When i asked what it does, this was his response.
With ‘#’, the constant ‘0#’ becomes a decimal one. Then VB.NET chooses the definition of Check that takes a Decimal parameter, since it is more suitable comparing with other candidates.
You can also write ‘0d’ [https://learn.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/data-types/type-characters].
hope that will help someone else down the road.
Thanks
shannon

Split() doesn't work properly

well I'm doing a computing assessment and well I've ran into an issue with splitting a string. For some reason when the string splits the array stores the whole thing in Variable(0). The error that occurs is when it tries to assign TicketID(Index) a value, it says that the array is out of bound.
Here's the code:
Private Sub ReadInformation(ByRef TicketID() As String, CustomerID() As String, PurchaseMethod() As Char, NumberOfTickets() As Integer, FileName As String)
Dim Line, TextArray(3) As String
Dim Index As Integer
FileOpen(1, FileName, OpenMode.Input)
For Index = 0 To 499
Input(1, Line)
TextArray = Line.Split(",")
CustomerID(Index) = TextArray(0)
TicketID(Index) = TextArray(1)
NumberOfTickets(Index) = TextArray(2)
PurchaseMethod(Index) = TextArray(3)
MessageBox.Show(CustomerID(Index))
Next
FileClose()
End Sub
Here's the first 10 lines of the TextFile I'm trying to read:
C001,F3,10,S
C002,F3,2,O
C003,F3,3,S
C004,W2,9,S
C005,T3,10,S
C006,F3,2,S
C007,W1,3,O
C008,W3,1,O
C009,T2,2,S
C010,F2,9,O
Here's the Error Message I receive:
Error Message
I would use some Lists instead of arrays. In this way you don't have to worry about length of the arrays or if there are fewer lines than 500. Of course, using the more advanced NET Framework methods of the File.IO namespace is a must
Private Sub ReadInformation(TicketID As List(Of String), _
CustomerID As List(Of String), _
PurchaseMethod As List(Of Char), _
NumberOfTickets As List(Of Integer), _
FileName As String)
for each line in File.ReadLines(FileName)
Dim TextArray = Line.Split(","c)
if TextArray.Length > 3 Then
CustomerID.Add(TextArray(0))
TicketID.Add(TextArray(1))
' This line works just because you have Option Strict Off
' It should be changed as soon as possible
NumberOfTickets.Add(TextArray(2))
PurchaseMethod.Add(TextArray(3))
End If
Next
End Sub
You can call this version of your code declaring the 4 lists
Dim TicketID = New List(Of String)()
Dim CustomerID = New List(Of String)()
Dim PurchaseMethod = New List(Of Char)()
Dim NumberOfTickets = New List(Of Integer)()
ReadInformation(TicketID, CustomerID, PurchaseMethod, NumberOfTickets, FileName)
Another approach more Object Oriented is to create a class that represent a line of your data. Inside the loop you create instances of that class and add the instance to a single List
Public Class CustomerData
Public Property TicketID As String
Public Property CustomerID As String
Public Property NumberOfTickets As Integer
Public Property PurchaseMethod As Char
End Class
Now the loop becomes
Private Function ReadInformation(FileName As String) as List(Of CustomerData)
Dim custData = New List(Of CustomerData)()
For Each line in File.ReadLines(FileName)
Dim TextArray = Line.Split(","c)
if TextArray.Length > 3 Then
Dim data = new CustomerData()
data.CustomerID = TextArray(0)
data.TicketID = TextArray(1)
data.NumberOfTickets = TextArray(2)
data.PurchaseMethod = TextArray(3)
custData.Add(data)
End If
Next
return custData
End Function
This version requires the declaration of just one list
You can call this version of your code passing just the filename and receiving the result fo the function
Dim customers = ReadInformation(FileName)
For Each cust in customers
Console.WriteLine(cust.CustomerID)
...
Next
Or use it as an array
Dim theFirstCustomer = customers[0]
Console.WriteLine(theFirstCustomer.CustomerID)

VB Assignment of member field programmatically

This question is a follow-on to VB ReDim of member field programmatically. After the arrays are dimensioned appropriately, I try to set the values of the elements, but I get an exception at run time when I try to assign the first value (MySB.AssignValues(0, "B", 0, 7.6))
System.InvalidCastException was unhandled
HResult=-2147467262
Message=Object cannot be stored in an array of this type.
Source=mscorlib
Module TestSetArray
Public Class BS
Public A As String
Public B() As Double
Public C() As Double
End Class
Public Class SB
Public MyBS() As BS
'ReadFieldString is a function that returns a string of the field name of Class BS,
'i.e., A, B or C. For test purpose, retun a constant
Public Function ReadFieldString() As String
Return "B"
End Function
'GetArrayDim is a function that returns an integer, which is the size of the array
'of that field name. For test purpose, retun a constant
Public Function GetArrayDim() As Integer
Return 2
End Function
Public Sub DimArrays()
ReDim MyBS(3)
Dim i As Integer
For i = 0 To MyBS.Length - 1
MyBS(i) = New BS()
Dim f = GetType(BS).GetField(ReadFieldString())
f.SetValue(MyBS(i), Array.CreateInstance(f.FieldType.GetElementType(), GetArrayDim()))
Next
End Sub
Public Sub AssignValues(MainIndex As Integer, TheName As String, TheIndex As Integer, TheValue As Double)
Dim f = MyBS(MainIndex).GetType.GetMember(TheName)
f.SetValue(TheValue, TheIndex)
End Sub
End Class
Sub Main()
Dim MySB As SB = New SB
MySB.DimArrays()
MySB.AssignValues(0, "B", 0, 7.6)
MySB.AssignValues(0, "B", 1, 8.2)
End Sub
End Module
Thanks in advance.
The problem is that the GetMember method returns an array of type MemberInfo, not the double array of the class. You'd probably have an easier time if you used GetField instead. You have to call GetValue and cast its result to an Array in order to use SetValue to set the value.
Public Sub AssignValues(MainIndex As Integer, TheName As String, TheIndex As Integer, TheValue As Double)
Dim f = MyBS(MainIndex).GetType().GetField(TheName)
Dim doubleArray = DirectCast(f.GetValue(MyBS(MainIndex)), Array)
doubleArray.SetValue(TheValue, TheIndex)
End Sub
or if you know that the array will always be an array of Double, you can cast it directly to that:
Public Sub AssignValues(MainIndex As Integer, TheName As String, TheIndex As Integer, TheValue As Double)
Dim f = MyBS(MainIndex).GetType().GetField(TheName)
Dim doubleArray = DirectCast(f.GetValue(MyBS(MainIndex)), Double())
doubleArray(TheIndex) = TheValue
End Sub

Filter a Dictionary to return a list

I know I can do this with a for loop cause that's how i'm doing it now. I was hoping for a more efficient way to accomplish the task.
I have a dictionary(Of Integer, Boolean)
or Of String, Boolean.
i want to get a list(of integer) or Of String from the dictionary where all the values are true(or false depending on what i need at the time)
and to generalize it or "black box" it, it could be any dictionary(of whatever, whatever)
and return a list(of whatever) where the value = whatever i'm looking for at the time.
string, string where value = "Closed"
in short: i want all list of all the keys who's value = some criteria
my current code:
Public Function FindInDict(Of tx, ty)(thedict As Dictionary(Of tx, ty), criteria As ty) As List(Of tx)
Dim tmpList As New List(Of tx)
For xloop As Integer = 0 To thedict.Count - 1
If CType(thedict(thedict.Keys(xloop)), ty).Equals(criteria) Then
tmpList.Add(thedict.Keys(xloop))
End If
Next
Return tmpList
End Function
You can do this easily with Linq:
Public Function FindInDict(Of tx, ty)(thedict As Dictionary(Of tx, ty), criteria As ty) As List(Of tx)
Return (From kvp In thedict
Where kvp.Value.Equals(criteria)
Select kvp.key).ToList()
End Function
Use LINQ, like so:
Dim tStorage As Dictionary(Of String, String) = New Dictionary(Of String, String)
Dim tKeys As List(Of String) = New List(Of String)
Dim tCriteria As List(Of String) = New List(Of String)
tStorage.Add("One", "Uno")
tStorage.Add("Two", "Dos")
tStorage.Add("Three", "Tres")
tStorage.Add("Four", "Quatro")
tCriteria.Add("Dos")
tCriteria.Add("Quatro")
tKeys = (From k In tStorage.Keys Where tCriteria.Contains(tStorage(k)) Select k).ToList
For Each tKey As String In tKeys
Console.WriteLine(tKey)
Next
Console.ReadKey()

How to convert a string of key/value pairs to HashTable or Dictionary or?

In VB.NET, how can I convert the following string into some kind of key/value type such as a Hashtable, Dictionary, etc?
"Name=Fred;Birthday=19-June-1906;ID=12345"
I want to extract Birthday or ID without having to split the string into an array.
EDIT: I'd prefer not to split the string into an array in case the format of the string changes later. I don't have control over the string. What if someone switches the order around or adds another element?
I’m currently unable to test this, lacking a VB compiler, but the following solution should also work, and it has the advantage of not requiring an explicit loop. It uses the Linq method ToDictionary and two nested Split operations:
Dim s = "Name=Fred;Birthday=19-June-1906;ID=12345"
Dim d = s.Split(";"c).Select(Function (kvp) kvp.Split("="c)) _
.ToDictionary( _
Function (kvp) kvp(0), _
Function (kvp) kvp(1))
First, we split on the outer delimiter (i.e. the semi-colon). From the resulting array, we select by splitting again, this time on =. The resulting array of arrays is converted to a dictionary by specifying that the first item is to become the key and the second is to become the value (the identifier kvp stands for “key-value pair”).
Since I can’t check the exact VB syntax and the above may contain subtle errors, here is the equivalent C# code (tested for correctness):
var s = "Name=Fred;Birthday=19-June-1906;ID=12345";
var d = s.Split(';').Select(kvp => kvp.Split('='))
.ToDictionary(kvp => kvp[0], kvp => kvp[1]);
Not sure why you don't want to split it. If you're sure there won't be any extra = or ; then you could just do:
Dim s As String = "Name=Fred;Birthday=19-June-1906;ID=12345"
Dim d As New Dictionary(Of String, String)
For Each temp As String In s.Split(";"c)
Dim index As Int32 = temp.IndexOf("="c)
d.Add(temp.Substring(0, index), temp.Substring(index + 1))
Next
Which might not be beautiful, but is very easy to understand.
input.Split(";"c) returns an array of key/value:
{ "Name=Fred", "Birthday=19-June-1906" , "ID=12345" }
so pair.Split("="c) returns { "Name", "Fred" } etc
If you want an alternative to doing a String.Split; there is always Regular Expressions as an alternative:
Dim map As Dictionary(Of String, String) = New Dictionary(Of String, String)
Dim match As Match = Regex.Match("Name=Fred;Birthday=19-June-1906;ID=12345", "(?<Name>[^=]*)=(?<Value>[^;]*);?")
While (match.Success)
map.Add(match.Groups("Name").Value, match.Groups("Value").Value)
match = match.NextMatch()
End While
The regular expression itself could be beefed up to better handle whitespace between key/value's and pair's but you hopefully get the idea. This should only pass through the string once to build up a string dictionary of keys and values.
Dim persSeparator as string=";"
Dim keyValSeparator as string="=";
Dim allPersons As New Dictionary(Of String, Person)
Dim str As String = "Name=Fred;Birthday=19-June-1906;ID=12345"
Dim parts As New List(Of String)(str.Split(persSeparator.ToCharArray)) 'why dont want you to split this string??
Dim person As New Person
For Each part As String In parts
Dim keyValue() As String = part.Split(keyValSeparator.toCharArray())
Select Case keyValue(0).ToUpper
Case "ID"
person.ID = keyValue(1)
Case "NAME"
person.Name = keyValue(1)
Case "BIRTHDAY"
person.BirthDay= keyValue(1)
End Select
Next
If Not allPersons.ContainsKey(person.ID) Then
allPersons.Add(person.ID, person)
End If
Public Class Person
Private _name As String
Private _birthday As String
Private _id As String = String.Empty
Public Sub New()
End Sub
Public Sub New(ByVal id As String)
Me._id = id
End Sub
Public Sub New(ByVal id As String, ByVal name As String)
Me._id = id
Me._name = name
End Sub
Public Sub New(ByVal id As String, ByVal name As String, ByVal birthday As String)
Me._id = id
Me._name = name
Me._birthday = birthday
End Sub
Public Property ID() As String
Get
Return Me._id
End Get
Set(ByVal value As String)
Me._id = value
End Set
End Property
Public Property Name() As String
Get
Return Me._name
End Get
Set(ByVal value As String)
Me._name = value
End Set
End Property
Public Property BirthDay() As String
Get
Return Me._birthday
End Get
Set(ByVal value As String)
Me._birthday = value
End Set
End Property
Public Overrides Function Equals(ByVal obj As Object) As Boolean
If TypeOf obj Is Person AndAlso Not obj Is Nothing Then
Return String.Compare(Me._id, DirectCast(obj, Person).ID) = 0
Else : Return False
End If
End Function
End Class
If you were just wanting to extract the birthday and ID from the string and place as a value pair in some sort of dictionary, for simplicity I would use regular expressions and then a generic dictionary (of string, valuepair structure). Something like this:
Imports System.Text.RegularExpressions
Imports System.Collections.Generic
Sub Main()
Dim Person As New Dictionary(Of String, ValuePair)
Dim s As String = "Name=Fred;Birthday=19-June-1906;ID=12"
Dim r As Regex = New Regex("Name=(.*);Birthday=(.*);ID=(.*$)")
Dim m As Match = r.Match(s)
Person.Add(CStr(m.Groups(1).Value), _
New ValuePair(CDate(m.Groups(2).Value), CInt(m.Groups(3).Value)))
Console.WriteLine(Person("Fred").Birthday.ToString)
Console.WriteLine(Person("Fred").ID.ToString)
Console.Read()
End Sub
Friend Structure ValuePair
Private _birthday As Date
Private _ID As Int32
Public ReadOnly Property ID() As Int32
Get
Return _ID
End Get
End Property
Public ReadOnly Property Birthday() As Date
Get
Return _birthday
End Get
End Property
Sub New(ByVal Birthday As Date, ByVal ID As Int32)
_birthday = Birthday
_ID = ID
End Sub
End Structure