How to use reflection to get keys from Microsoft.VisualBasic.Collection - vb.net

I know that it is not possible to get keys (How to get the Key and Value from a Collection VB.Net) and it is better to use other classes. However I am debugging 10 years old code and I cannot change it.
In the Watch Window I see:
So it seems to be possible to deal the collection as "List(of KeyValuePair)". Can I do it in code or is it only an internal translation.
I basically need to list all KEYs in a Collection.

VisualBasic.Collection has an undocumented private method InternalItemsList which allows reading keys besides values. The type of InternalItemsList is Microsoft.VisualBasic.Collection.FastList which does not seem to be Enumerable but has a method Item which returns items with a zero based index. The type of those items is Microsoft.VisualBasic.Collection.Node. An item has two private properties m_Value and m_Key. And here we are.
Following code illustrates how to convert VisualBasic.Collection to List(Of KeyValuePair.
Dim flg As BindingFlags = BindingFlags.Instance Or BindingFlags.NonPublic
Dim InternalList As Object = col.GetType.GetMethod("InternalItemsList", flg).Invoke(col, Nothing)
Dim res As New List(Of KeyValuePair(Of String, Object))
For qq As Integer = 0 To col.Count - 1
Dim Item As Object = InternalList.GetType.GetProperty("Item", flg).GetValue(InternalList, {qq})
Dim Key As String = Item.GetType.GetField("m_Key", flg).GetValue(Item)
Dim Value As Object = Item.GetType.GetField("m_Value", flg).GetValue(Item)
res.Add(New KeyValuePair(Of String, Object)(Key, Value))
Next
I know that using undocumented functions is not safe but it is for internal use only and only solution I have managed to find.

Related

Which overload of todictionary that I use if I do this?

Dim dict1 = fileToDict(PriceList)
Dim dict2 = dict1.ToDictionary(Function(y) Val(y))
Basically I am turning dict(of string,string) to dict(of string, double).
My question is I do not see any overload of toDictionary that takes a single function of stuff.
Note: The correct format is actually
Dim dict2 = dict1.ToDictionary(Function(x) x.Key, Function(y) Val(y.Value))
All of the overloaded operator takes 1 function that takes a keyvaluepair as argument.
Somehow the one that works use 2 function. That's where I am confused.
You need to specify a selector for both the keys and the values. There's no overload that is going to simply take the keys from the original Dictionary. Remember that ToDictionary is actually called on an IEnumerable(Of T) so it has no idea that the source is a Dictionary. It only knows that it is an IEnumerable(Of KeyValuePair(Of String, String)). You have to tell it how to get the keys for the new Dictionary from the items in the IEnumerable(Of T) source.
E.g.
Dim dict2 = dict1.ToDictionary(Function(kvp) kvp.Key, Function(kvp) CDbl(kvp.Value))

Get data from a collection

I want to make a collection to have data available
Example:
Dim b As New Collection
colb = New Collection
b.Add("system", "1001", "SYSTEM")
b.Add("network", "1002", "NETWORKA")
b.Add("networksecond", "1010", "NETWORKB")
colb.Add(b, "list")
im looking for a function to get data from this collection:
I want to, based on the ID (Second number) get the first and third value
So if I search for 1010, I need to have the value Network and NETWORKA
VB6 called, they want their Collection back.
No, seriously, please consider using a Dictionary instead of the old, legacy Collection class. Behold the beauty of generics and strong typing:
Dim dic As New Dictionary(Of Integer, Tuple(Of String, String))
dic.Add(1001, Tuple.Create("system", "SYSTEM"))
dic.Add(1002, Tuple.Create("network", "NETWORKA"))
dic.Add(1010, Tuple.Create("networksecond", "NETWORKB"))
' Search
Dim t As Tuple(Of String, String) = Nothing
If dic.TryGetValue(1002, t) Then
Console.WriteLine(t.Item1) ' prints "network"
Console.WriteLine(t.Item2) ' prints "NETWORKA"
End If
As soon as you have more than two values, I suggest that you use a specialized class instead of a Tuple to increase readability.
Also, you can simply use List(Of T). In most cases this is enough. Dictionary is good for fast search out long list by a single key.
'declare model
Public Class NetworkModel
Public Property Id As Integer
Public Property Name1 As String
Public Property Name2 As String
End Class
' load list of models
Private _modelList As New List(Of NetworkModel)()
.......
' search using LINQ
Dim model As NetworkModel = _modelList.FirstOrDefault(Function(m) m.Id = 1001)
If model IsNot Nothing Then . . . . .

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

VB.Net - how can i get the type of the object contained in an List

If I have a list...
dim l as List(of MyClass) = new List(of MyClass)
and I want to get the type of the objects contained in the list, how do I do that?
The obvious answer, that doesn't seem to be possible from my actual implementation, would be to do something like this...
public function GetType(byval AList as IList(of GenericType)) as System.Type
dim lResult as system.type = nothing
if AList.Count > 0 then lResult = AList(0).GetType
return lResult
end function
But what if the list is empty and I still want to know the type it contains?
There's a good article on this at MSDN, here
Basically you can use GetGenericArguments() to get an array of the types provided as arguments to your generic type. In the case of a List, there's only one argument so you will get what you need using eg
dim l as List(of MyClass) = new List(of MyClass)
dim t as Type = (l.GetGenericArguments())(0)

Can't get a List(Of <my class>) from a Dictionary in .NET?

I have a Dictionary with key of type UInteger and the value is List(Of Session) where the (Public) class Session contains a couple of variables and a constructor (Public Sub New(...)).
Some of the variables in my Session class is:
Private count As Integer
Private StartDate As Date
Private Values As List(Of Integer)
and a couple of methods like:
Friend Sub Counter(ByVal c as Integer)
count += c
End Sub
There is no problem to add values to the Dictionary:
Dim Sessions As New List(Of Session)
Dim dict As New Dictionary(Of Integer, List(Of Sessions))
then some code to fill up a couple of Session objects in Sessions (not shown here) and then:
dict.Add(17, Sessions) ''#No problem
Sessions.Clear()
Sessions = dict(17) ''#This doesn't return anything!
The Sessions object is empty even if the code doesn't returned any error.
Is my class Session to compex to be stored in a Dictionary?
That's because the Sessions variable is a reference to the data, so when you add it to the dictionary, the one in the dictionary is pointing to the same thing. So when you do Sessions.Clear() you clear the actual data and since both the references point at the same place, neither of them now hold the data.
If you actually want to have two different copies of the data, this discussion might be helpful.
These lines seem fishy to me:
Dim Sessions As New List(Of Session)
' Your Sessions variable has the same name as a class? '
Dim dict As New Dictionary(Of Integer, Sessions)
' You are adding a List(Of Session) to a Dictionary(Of UInteger, Sessions)? '
' This could only be legal if List(Of Session) derived from your Sessions class '
' (which is obviously not true). '
dict.Add(17, Sessions)
It's also confusing what exactly you mean here:
Sessions.Clear()
Sessions = dict(17) 'This does not return anything!'
By "doesn't return anything," do you mean it returns Nothing or an empty List(Of String)? In the latter case, that's expected: you just cleared the very list you're talking about. In the former case, that is very strange, and we'll need more details.