Variables refer to the same instance - vb.net

Within my learning curve I play around with converting List and IEnumerable between each other.
What I am surprised with is that after executing EditMyList procedure MyIEnumerable contains the same data for each DBTable object as MyList. However I have modified MyList only, without assigning it to MyIEnumerable once List has been modified.
Can you explain what happened here and why MyList and MyEInumerable refer to the same instance?
Public Class DBTable
Public Property TableName As String
Public Property NumberOfRows As Integer
End Class
Public Sub EditMyList
Dim MyList As New List(Of DBTable)
MyList.Add(New DBTable With {.TableName = "A", .NumberOfRows = 1})
MyList.Add(New DBTable With {.TableName = "B", .NumberOfRows = 2})
MyList.Add(New DBTable With {.TableName = "C", .NumberOfRows = 3})
Dim MyIEnumerable As IEnumerable(Of DBTable) = MyList
For Each item In MyList
item.NumberOfRows += 10
Next
End Sub
UPDATE: string case where at the end b is not equal to a. String is also reference type, so assigning one variable to other one we shall copy just reference. However at the end there is different result than in the first example (explained by #Sefe)
Dim a As String
Dim b As String
a = "aaa"
b = "bbb"
a = b
' At this point a and b have the same value of "bbb"
a = "xxx"
' At this point I would expect a and b equal to "xxx", however a="xxx" but b="bbb"

A List is a reference type. That means it is created on the heap and your MyList variable contains just a reference (sometimes incorrectly called "pointer") to the list. When you assign MyList to MyEnumerable you don't copy the whole list, you just copy the reference. That means all changes you make to the (the one) list, is reflected by all the references.
If you want a new list you need to create it. You can use the list constructor:
Dim MyIEnumerable As IEnumerable(Of DBTable) = New List(Of DBTable)(MyList)
Since you don't need a list, but an IEnumerable you can also call the list's ToArray method:
Dim MyIEnumerable As IEnumerable(Of DBTable) = MyList.ToArray
You can also use LINQ:
Dim MyIEnumerable As IEnumerable(Of DBTable) = MyList.ToList
As far as the behavior of String is concerned, strings in .net are immutable. That means once created, they can not be changed. String operations (for example concatinations) will always create new strings. In other words: the copy operation you have to do manually for your lists is done automatically for strings. That's why you see similar behavior for strings as for value types.
Also, the assignment operation in your question would also still behave the same if strings were mutable. When you assign a = "xxx", you update the reference of afrom "bbb" to "xxx". That however does not affect b, which still keeps its old reference.

Use ToList() extension method for creating another List
Dim newCollection = MyList.ToList()
But notice that instances of DBTable still will reference to the same items
For creating "full" copy you need create new instances of DBTable for every item in the collection
Dim newCollection = MyList.Select(Function(item)
return new DBTable
{
.TableName = item.TableName,
.NumberOfRows = item.NumberOfRows
}
End Function).ToList()
For Each item in MyList
item.NumberOfrows += 10 ' will not affect on the newCollection items
Next

Related

Properties, Arrays and ArrayLists using vb.net

In my code behind in my Web project I have a Property
Public Shared UserAttributes(2) As String
Public Property _UserAttributes(ByVal Index As Integer) As String
Get
Return UserAttributes(Index)
End Get
Set(value As String)
UserAttributes(Index) = value
End Set
End Property
And I also have an ArrayList declared as Friend
Friend UserParameters As New ArrayList
I call my property like that:
_UserAttributes(0) = "parameter1"
_UserAttributes(1) = "parameter2"
_UserAttributes(2) = "parameter3"
UserParameters.Add(UserAttributes)
_UserAttributes(0) = "parameter1,1"
_UserAttributes(1) = "parameter2,1"
_UserAttributes(2) = "parameter3,1"
UserParameters.Add(UserAttributes)
From the above code we may see the two pairs of Attributes having one text each one.
What I need now is:
After I add the three Attributes from my Property to my ArrayList
The second three Attributes of my property NOT to spoil the first one.
Which by now that they are doing
And finally I have two(2) _items in my ArrayList which they have the same text on each _item (which is the last one).
What I need is to write the second (or more) set of Attributes without spoiling the previous _items from ArrayList.
Finally I've made it to solve this puzzle as follows
One Property as ArrayList
Public Property _UserParameters As ArrayList
Get
Return UserParameters
End Get
Set(value As ArrayList)
UserParameters = value
End Set
End Property
Second Property as Array
Public Property _UserAttributes(ByVal Index As Integer) As String
Get
Return UserAttributes(Index)
End Get
Set(value As String)
UserAttributes(Index) = value
End Set
End Property
And from code behind I use this code:
Dim UserAttributes As New Hashtable
Dim key As Object = Nothing
Dim Param As Object = Nothing
Dim myList As New ArrayList
Dim item As Object = UserAttributes
UserAttributes.Add("UserId", "Parametr1")
UserAttributes.Add("UserName", "Parametr2")
UserAttributes.Add("UserMail", "Parametr3")
For Each item In UserAttributes
key = item.Key
Param = item.value
logHandler._UserParameters.Add(key & "^" & Param)
Next
myList.Add(logHandler.UserParameters.ToArray)
UserAttributes.Clear()
logHandler.UserParameters.Clear()
UserAttributes.Add("UserId", "Parametr1-1")
UserAttributes.Add("UserName", "Parametr2-1")
UserAttributes.Add("UserMail", "Parametr3-1")
For Each item In UserAttributes
key = item.Key
Param = item.value
logHandler._UserParameters.Add(key & "^" & Param)
Next
myList.Add(logHandler.UserParameters.ToArray)
The use of HashTable solves my issue, Along with the conversion from HashTable parameters to String
Which add them first to the ArrayList Property
And after that add them to the second ArrayList
And the result of these ArrayList I add it to a Pull Down menu control.
And Why I'm doing all that?
That is because I have many users with the same attributes as keys but deferent values
Good day to all, with many thanks.

vb.net create objects in for each loop from partial info in Ienumerable

Summary:I want to create objects in a for each loop.
I have a class - Dashboard, which has some properties - length, height, etc.
This info is contained within an XML document, but my class' properties is only a small subset of the information in the XML.
I have created a Collection of XElement, and I can iterate over these, but how do I create my object on each iteration of the loop?
Dim Col_DashBoards As IEnumerable(Of XElement) = From XDashboard In XDocument.Load(filename).Descendants("dashboard")
For Each XDashboard In Col_DashBoards
'PseudoCode
Dim Xdashboard.Name as New DashboardClassObject
Xdashboard.Name.Height = XDashboard.Element("Height").value
...
Next
If I have understood your question correctly, you wish to create a new object based on a subset of data from within an XML document?
The below is a function that will generate a new DashboardClassObject for every matching node and populate that object. A list of type DashboardclassObject is returned.
Public Function GenerateDashBoardFromXML(filename as string) As List(Of DashboardClassObject)
Dim dashboardList As List(Of DashboardClassObject) = New List(Of DashboardClassObject)()
Dim Col_DashBoards As IEnumerable(Of XElement) = From XDashboard In XDocument.Load(filename)?.Descendants("dashboard")
For Each XDashboard In Col_DashBoards
Dim dashboard As DashboardClassObject = New DashboardClassObject () With {
.Name = XDashboard.Element("Height")?.value
}
dashboardList.Add(dashboard)
Next
return dashboardList
End Function
It should be noted that Null checking is used here. The below is only populated if the matching element is found. Null coalescing operator is also an option here.
.Name = XDashboard.Element("Height")?.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 . . . . .

Infinite amount of Collection VBA

Here is the screen of my problem which is infinite amount of collections.
I want the collection be added to object property just once. Not like this:
http://postimg.org/image/o6da95j0f/
(screen showing the problem with "watch" of collection in VBA
Public Sub testCollections()
Dim index As Long
index = 1
Dim OJsonElement As JsonElement
Dim newColl As New Collection
Dim str As String
Call addColl(OJsonElement, newColl)
For Each OJsonElement In newColl
Debug.Print "THE NAME IS:" & OJsonElement.name
Next OJsonElement
End Sub
Function addColl(obj1 As JsonElement, nextCollection As Collection)
Dim i As Long
Set nextCollection = New Collection
Set obj1 = New JsonElement
Set obj1.valueCollection = nextCollection
obj1.name = "CityName"
obj1.value = "type"
nextCollection.Add obj1
'obj1.ValueType = nextCollection
'nextCollection.Add nextCollection
End Function
Class:
Public name As String
Public nameCollection As Collection
Public value As Variant
Public ValueType As String
Public valueCollection As Collection
I don't really understand well your code, but I will limit to explain you why it happens what you see in your watcher. The line:
Set obj1.valueCollection = nextCollection
is adding the new collection into the obj1 property valueCollection. Then, two lines after, you say:
nextCollection.Add obj1
which means you're adding the obj1 into its own property, so creating an infinite nesting. I'd like to help you but for that I'd need to understand what you want to reach with your code. But sticking to your request I want the collection be added to object property just once, I would just suggest you to remove the line nextCollection.Add obj1, which (at least from the perspective of who doesn't know the project purpose) does not seem to do anything useful but an infinite nesting.

.NET: Quick way of using a List(Of String) as the Values for a new Key/Value collection?

I have a List(Of String) object and need to create a new collection based on the strings' values. The new collection will be of a custom class with two string fields - call them Key and Value (but it's not the built-in KeyValue class, it's a custom class).
All the values of Key will be the same, it's just Value that I want to source from the string list. Example:
Dim slValues = New List(Of String)({"Cod", "Halibut", "Herring"})
Dim myList = New List(Of myClass)( ... amazing initialisation line here? )
Class myClass
Public Key As String ' This will always be "Fish"
Public Value As String ' This will be the fish name.
End Class
(Note I don't actually have access to the class I'm using, so can't just change it to Public Key As String = "Fish" as a default. Key must be set at runtime.)
I can of course just do a loop and do it manually, but I'm wondering if there's a whiz-bang way to achieve this as part of the initialisation line?
How about this
Dim myList = slValues.ConvertAll(Of myClass)( _
Function(s) New YourClass With {.Key = "Fish", .Value = s})
Here's an alternative to Jodrell's answer using Linq's Select function:
Dim myList = slValues.Select(Function(fish) New CustomClass With {.Key = "Fish", .Value=fish}).ToList()
from x in Enumerable.Range(1, slValues.count-1).ToArray
select new KeyValuePair(of string,myClass) with {.Key=slValues(x),.Value=myList(x)}
hope it helps.