VB.Net Initialising an array on the fly - vb.net

I wrote this - very simple - function, and then wondered does VB have some pre-built functionality to do this, but couldn't find anything specific.
Private Shared Function MakeArray(Of T)(ByVal ParamArray args() As T) As T()
Return args
End Function
Not so much to be used like
Dim someNames() as string = MakeArray("Hans", "Luke", "Lia")
Because this can be done with
Dim someNames() as string = {"Hans", "Luke", "Lia"}
But more like
public sub PrintNames(names() as string)
// print each name
End Sub
PrintNames(MakeArray("Hans", "Luke", "Lia"))
Any ideas?

Any reason not to do:
Dim someNames() as string = New String(){"Han", "Luke", "Leia"}
The only difference is type inference, as far as I can tell.
I've just checked, and VB 9 has implicitly typed arrays too:
Dim someNames() as string = { "Han", "Luke", "Leia" }
(This wouldn't work in VB 8 as far as I know, but the explicit version would. The implicit version is necessary for anonymous types, which are also new to VB 9.)

Dim somenames() As String = {"hello", "world"}

The following codes will work in VB 10:
Dim someNames = {"Hans", "Luke", "Lia"}
http://msdn.microsoft.com/en-us/library/ee336123.aspx

PrintNames(New String(){"Hans", "Luke", "Lia"})

Microsoft recommends the following format
Dim mixedTypes As Object() = New Object() {item1, item2, itemn}
per http://msdn.microsoft.com/en-US/library/8k8021te(v=VS.80).aspx
Note, you don't have to specify the size of new Array, as that is inferred from the initialized count of args. If you do want to specify the length, you specify not the "length" but index number of last space in array. ie. New Object(2) {0, 1, 2} ' note 3 args.

Related

Convert an unknown structure to an untyped Object in VB.NET

I'd like to convert an unknown basic structure to an Object (no type here).
I'm building a library that will be used by many users to extract data from my system but don't want to do a new function for everyone of them. They have to know what will be the result.
In vb, it is possible to create an Object with some properties and use it as it is a regular Class like so:
Dim myObj as New With { .name = "Matt", .age = "28" }
MsgBox( myObj.name & " is now " & myObj.age & " years old.")
So far, so good.
Next step : my user will give me some instructions that I need to extract data from various DBs, and I've no idea of what the result will be.
What I know after the execution is a list of String containing the columns of the result set and, of course a (set of) rows.
And here is the problem of course
My function (for a single row) so far:
Public Function GetData(ByVal instructions as String) as Object ' User is supposed to know what will be inside, instructions is as XML describing DB, table, query, ...
' Do what is needed to retrieve data
' Here I have a variable cols As List(Of String) ' e.g. ("BP", "NAME", "VAT")
Dim o As New With ???
Return o
End Function
What I've tried: build a fake JSon on the fly, and try to Deserialize to Object.
But even if it seems to work, I (and the user) can't access the property as in my top piece of code like:
MsgBox(o.BP)
I know that I could do
Public Function GetData(Of T As {New})(ByVal instructions as String) As T
Dim o As T
' Use some Reflexion to TryInvokeMember of T
Return o
End Function
But I wanted to remove the hassle to create a class to use my code.
Plus, My librairy will be use in a webservice and the class of the user is then unknown.
One approach could be - to use Dictionary(Of String, Object)
Public Function GetData(instructions as String) As Dictionary(Of String, Object)
Dim data = ' Load data
Dim columns As String() = { "BP", "NAME", "VAT" }
Return columns.ToDictionary(
Function(column) column,
Function(column) data.GetByColumnName(column)
)
End Function
` Usage
Dim result = GetDate("instructions: ['BP', 'NAME']")
' Because user knows it is Integer
Dim bpValue = DirectCast(result.Item("BP"), Integer)
Thanks to #GSerg, #Fabio and a few other searches about ExpandoObject, I did it !
Imports System.Dynamic
Dim o As Object = New ExpandoObject()
For Each col In cols
DirectCast(o, IDictionary(Of String, Object)).Add(col, row.GetString(col))
Next

Convert Database Linq Query Results to String List

I am trying to use a LINQ query to grab some data from a database, and since I am grabbing just one column of data I want to store it within a string list. This is the code I have.
Dim POList As New List(Of String)
Using dbContext As New DBLINQDataContext
Dim query = (From o In dbContext.Orders
Where o.Order_Number.StartsWith(JobNumber)
Select o.Order_Number)
POList = query.ToList()
End Using
MessageBox.Show(POList.ToString())
When I run this the data in the MessageBox is
System.Collections.Generic.List`1[System.String]
There is no table data, even though I know there are actual data points for me to be getting :\
POList.ToString() will display the name of the object type.
You can use string.Join(",", POList.ToString()).
You can also define an extension method StringJoin() to show your list as string if you are going to use it frequently.
Here is an extension method that you can use:
public static StringExtensions
{
public static string StringJoin(this IEnumerable<string> strings, string seperator)
{
if (strings == null) return null;
return string.Join(seperator, strings);
}
}
Since this was a vb.net question, I translated #vendettamit 's excellent answer for the benifit of future vb readers.
Public Module StringExtensions
<Extension()>
Public Function StringJoin(MyStrings As IEnumerable(Of String), separator As String) As String
If MyStrings Is Nothing Then
Return Nothing
Else
Return String.Join(separator, MyStrings)
End If
End Function
End Module
To use the extension...
Dim l As New List(Of String) From {"Mathew", "Mark", "Luke", "John"}
Dim s As String = l.StringJoin(", ")
Debug.Print(s)

Calling System.IO.ReadAllBytes by string name

This post is related to Visual Basic .NET 2010
So, I'm wondering if there's any way to call a function from a library such as System.ReadAllBytes by string name.
I've been trying Assembly.GetExecutingAssembly().CreateInstance and System.Activator.CreateInstance followed by CallByName(), but none of them seemed to work.
Example of how I tried it:
Dim Inst As Object = Activator.CreateInstance("System.IO", False, New Object() {})
Dim Obj As Byte() = DirectCast(CallByName(Inst, "ReadAllBytes", CallType.Method, new object() {"C:\file.exe"}), Byte())
Help is (as always) much appreciated
It is System.IO.File.ReadAllBytes(), you missed the "File" part. Which is a Shared method, the CallByName statement is not flexible enough to permit calling such methods. You will need to use the more universal Reflection that's available in .NET. Which looks like this for your specific example, spelled out for clarity:
Imports System.Reflection
Module Module1
Sub Main()
Dim type = GetType(System.IO.File)
Dim method = type.GetMethod("ReadAllBytes")
Dim result = method.Invoke(Nothing, New Object() {"c:\temp\test.bin"})
Dim bytes = DirectCast(result, Byte())
End Sub
End Module

How to get the Key and Value from a Collection VB.Net

How do you get the key value from a vb.net collection when iterating through it?
Dim sta As New Collection
sta.Add("New York", "NY")
sta.Add("Michigan", "MI")
sta.Add("New Jersey", "NJ")
sta.Add("Massachusetts", "MA")
For i As Integer = 1 To sta.Count
Debug.Print(sta(i)) 'Get value
Debug.Print(sta(i).key) 'Get key ?
Next
Pretty sure you can't from a straight Microsoft.VisualBasic.Collection.
For your example code above, consider using a System.Collections.Specialized.StringDictionary. If you do, be aware that the Add method has the parameters reversed from the VB collection - key first, then value.
Dim sta As New System.Collections.Specialized.StringDictionary
sta.Add("NY", "New York")
'...
For Each itemKey in sta.Keys
Debug.Print(sta.Item(itemKey)) 'value
Debug.Print(itemKey) 'key
Next
I don't recommend using the Collection class, as that is in the VB compatibility library to make migrating VB6 programs easier. Replace it with one of the many classes in the System.Collections or System.Collections.Generic namespace.
It is possible to get a key with using Reflection.
Private Function GetKey(Col As Collection, Index As Integer)
Dim flg As BindingFlags = BindingFlags.Instance Or BindingFlags.NonPublic
Dim InternalList As Object = Col.GetType.GetMethod("InternalItemsList", flg).Invoke(Col, Nothing)
Dim Item As Object = InternalList.GetType.GetProperty("Item", flg).GetValue(InternalList, {Index - 1})
Dim Key As String = Item.GetType.GetField("m_Key", flg).GetValue(Item)
Return Key
End Function
Not using VB.Collection is recommended but sometimes we are dealing with code when it was used in past. Be aware that using undocumented private methods is not safe but where is no other solution it is justifiable.
More deailed information can be found in SO: How to use reflection to get keys from Microsoft.VisualBasic.Collection
Yes, it may well, but I want recomend that you use another Collection.
How to do you do with Reflection, the type Microsoft.VisualBasic.Collection contains some private fields, the field one should use in this case is "m_KeyedNodesHash" the field, and the field type is System.Collections.Generic.Dictionary(Of String, Microsoft.VisualBasic.Collection.Node), and it contains a property called "Keys", where the return type is System.Collections.Generic.Dictionary(Of String, Microsoft.VisualBasic.Collection.Node).KeyCollection, and the only way to get a certain key is to convert it to type IEnumerable(Of String), and the call ElementAt the function.
Private Function GetKey(ByVal col As Collection, ByVal index As Integer)
Dim listfield As FieldInfo = GetType(Collection).GetField("m_KeyedNodesHash", BindingFlags.NonPublic Or BindingFlags.Instance)
Dim list As Object = listfield.GetValue(col)
Dim keylist As IEnumerable(Of String) = list.Keys
Dim key As String = keylist.ElementAt(index)
Return key
End Function

Short way to create arrays?

In VB.NET, I create my arrays like
Dim myArray = New ArrayList
But isn't there a way to create an array with elements without having to make a variable?
In Ruby, the long way was
array = Array.new
And the simple, non-variable way was just
[element,element,...]
Well, things you can do with primitive (and String) arrays:
Dim array As New String()
Dim array As New String() { "one", "two", "three" }
If (New String() { "one", "two", "three" }).Contains("one") Then
' Do something for "one"
End If
If you move to VB.NET 2010 you will get some extra array initialization features, but if you're using 2008 or below the shortest you can get your lists created might be something like this:
Dim list As New List(Of String)
list.AddRange(New String() { "one", "two", "three" })
And to touch on the point of declaring things without assigning them to a variable: .NET is strongly typed, so while you don't always have to declare a variable, your objects will always need to be of a single type (and one you need to specify through a New).
I'm not sure just how useful such a beast is since, without a name, you can't easily access the elements of it.
I know C has a feature that allows this with "one-shot" accesses like:
char hexchar = "0123456789abcdef"[nybble];
but, after that statement's finished the char array making up that string is no longer accessible.
If you want an array you can access continuously, I suspect it will need an identifying name. I might be wrong, I haven't used VB since VB6 but even if it's possible, it's a dubious language feature (IMO).
You can do a few things.
Public Sub Main()
Dim xs = New Integer() {1, 2, 3}
CType({1, 2, 3}, Integer()).CopyTo(...)
Dim s2 = Sum({1, 2, 3})
End Sub
Public Function Sum(ByVal array As Integer()) As Integer
Return array.Sum()
End Function
Is this the kind of thing you're after?
For Each foo As String In New String() {"one", "two", "three"} 'an array with no name - "On the first part of the journey..."
Debug.WriteLine(foo)
Next