How do I construct an "Add" routine for a custom object list? - vb.net

I feel really stupid asking this question, but here goes...
I'm trying to create a custom object in VB, that is itself a list (or collection, or "tuple" - I'm not sure what the difference between these is) of custom objects, and I need to create routines to add and remove these secondary objects to/from the larger custom object. So far, my code goes something like this:
Public Class parameterSet
Friend _xParameter As String
Public Property xParameter() As String
Get
Return _xParameter
End Get
Set(value As String)
_xParameter = value
End Set
End Property
Friend _yParameter As String
Public Property yParameter() As String
Get
Return _yParameter
End Get
Set(value As String)
_yParameter = value
End Set
End Property
Friend _zParameter As String
Public Property zParameter() As String
Get
Return _zParameter
End Get
Set(value As String)
_zParameter = value
End Set
End Property
Public Sub New(ByVal xParameter As String, ByVal yParameter As String, ByVal zParameter As String)
_xParameter = xParameter
_yParameter = yParameter
_zParameter = zParameter
End Sub
End Class
Public Class parameterCollection
Friend _parameterCollection As New List(Of parameterSet)
Friend Sub Add(xParameter As String, yParameter As String, zParameter As String)
Throw New NotImplementedException()
End Sub
End Class
What do I have to put in the Add routine to make this work?

Your first class ought to look like this:
Public Class ParameterSet
Public Property X As String
Public Property Y As String
Public Property Z As String
Public Sub New(x As String, y As String, z As String)
Me.X = x
Me.Y = y
Me.Z = z
End Sub
End Class
Your second class ought to look like this:
Imports System.Collections.ObjectModel
Public Class ParameterSetCollection
Inherits Collection(Of ParameterSet)
Public Overloads Sub Add(x As String, y As String, z As String)
Add(New ParameterSet(x, y, z))
End Sub
End Class
You might even want to do this:
Imports System.Collections.ObjectModel
Public Class ParameterSetCollection
Inherits Collection(Of ParameterSet)
Public Overloads Function Add(x As String, y As String, z As String) As ParameterSet
Dim item = New ParameterSet(x, y, z)
Add(item)
Return item
End Function
End Class
The Collection(Of T) class already provides all the standard collection functionality and you can extend it as required.

Related

Collection class of specific type containing basic features

Every time i use some class e.g Artikel as follows:
Public Class Artikel
Property ID As Integer
Property Nummer As String
Property Name As String
Property Position As Integer
End Class
For such classes i would like to have collection class. The features i would like to have is like:
--> Add (passing Artikel object)
--> Remove (passing Artikel object)
--> Sort entire collection (based on Position property desc/asc)
--> Compare two Artikels (pass by Artikels and tell by which property has to be compared)
--> Check whether two artikels equals
--> Every added artikel has to be marked by Key (so maybe dictionary)? <key><Artikel>
--> Remove Artikel (passing by Key index)
Could somone from you there tell me or even better provide example of collection class pass those requirments?
EDIT: Startup:
Artikel's collection:
Option Strict On
Public Class Articles
Public Property collection As Dictionary(Of Integer, Artikel)
Sub New()
'Initiate new collection
collection = New Dictionary(Of Integer, Artikel)
End Sub
'Add new Artikel to collection
Public Function AddToCollection(ByVal artikel As Artikel) As Boolean
collection.Add(artikel)
Return True
End Function
'Remove specific Artikel
Public Sub RemoveFromCollectionByArtikel(artikel As Artikel)
If Not IsNothing(collection) Then
collection.Remove(artikel)
End If
End Sub
'Get collection
Public Function GetCollection() As Dictionary(Of Integer, Artikel)
Return collection
End Function
'Sort collection by property position
Public Sub SortByPosition()
collection.Sort()
End Sub
'Remove specific sending keys and then reorder them
Public Sub RemoveAllMarkedAsDeleted(keys As List(Of Integer))
'-- Check whther anything has been marked as deleted
If keys.Count > 0 Then
For Each row In keys
collection.Remove(row)
Next
ReorderKeys()
End If
'Reorder all Artikels in collection
Private Sub ReorderKeys()
Dim newCollection As New Dictionary(Of Integer, Artikel)
Dim index As Integer = 0
For Each collitem In collection
newCollection.Add(index, collitem.Value)
index += 1
Next
collection.Clear()
collection = newCollection
End Sub
End Class
Artikel class (additionally i implemented IComparable to be able to sort)
Option Strict On
Public Class Artikel
Implements IComparable(Of Artikel)
Property ID As Integer
Property Nummer As String
Property Name As String
Property Position As Integer
Public Function CompareTo(pother As Artikel) As Integer Implements IComparable(Of Artikel).CompareTo 'we can sort because of this
Return String.Compare(Me.Position, pother.Position)
End Function
Public Shared Function FindPredicate(ByVal partikel As Artikel) As Predicate(Of Artikel)
Return Function(partikel2 As Artikel) partikel.ID = partikel2.ID
End Function
Public Shared Function FindPredicateByUserId(ByVal partikel As String) As Predicate(Of Artikel)
Return Function(partikel2 As Artikel) partikel = partikel2.ID
End Function
End Class
Parts of it look good, but I would ultimately do it a bit differently. First, consider overloads on the item class to make them easier to create and default initialization:
Public Class Article
Property ID As Integer = -1
Property Key As String = ""
Property Name As String = ""
Property Position As Integer = -1
Property PubDate As DateTime = DateTime.Minimum
Public Sub New()
End Sub
' whatever minimum data a new item requires
Public Sub New(k As String, n As String)
Key = k
Name = n
End Sub
' full initialization:
Public Sub New(k As String, n As String, pos As Int32,
pubDt As DateTime)
...
End Sub
End Class
I added some properties for variety, and I suspect "Nummer" might be the "Key" mentioned in the OP, but whatever it is, I would add it to the Article class as that name, if it has some importance.
You might need a simple ctor for serialization (???). Some of these will find and use a Private parameterless constructor, but your code will be forced to use one of the overloads in order to provide some minimum level of data when a new one is created.
You probably do not need IComparable. That is typically for more complex comparisons, such as multiple or complex properties. An example is a carton or box:
If (width = Other.Width) AndAlso (height = Other.Height) Then
Return 0
ElseIf (width = Other.Height) AndAlso (height = Other.Width) Then
Return 0
End If
Plus more gyrations to work out which is "less" than the other. One reason you dont need it, is because If Art1.Postion > Art2.Postion is trivial. The other reason in your case, is because a Dictionary cannot be sorted.
Rather than a Dictionary, an internal List would work better for some of the things you describe but still allow you to have it act like a Dictionary to the extent you need it to. For this, I might build it using ICollection<T>:
Public Class ArticleCollection
Implements ICollection(Of Article)
Pressing Enter after that line will add all the required methods including:
Public Sub Add(item As Article) Implements ICollection(Of Article).Add
Public Sub Clear() Implements ICollection(Of Article).Clear
Public Function Contains(item As Article) As Boolean Implements ICollection(Of Article).Contains
Public ReadOnly Property Count As Integer Implements ICollection(Of Article).Count
Public Function Remove(item As Article) As Boolean Implements ICollection(Of Article).Remove
It remains completely up to you how these are implemented. It also doesn't rule out adding methods such as RemoveAt(int32) or RemoveByKey(string) depending on what you need/how it will be used. One of the benefits to ICollection(Of T) is that it includes IEnumerable which will allow use for each loops (once you write the Enumerator): For Each art In Articles
To emulate a dictionary to allow only one item with a specific property value:
Public Class ArticleCollection
Implements ICollection(Of Article)
Private mcol As List(Of Article)
...
Public Sub Add(item As Article) Implements ICollection(Of Article).Add
' check for existing key
If KeyExists(item.Key) = False Then
mcol.Add(item)
End If
End Sub
You can also overload them:
' overload to match Article ctor overload
Public Sub Add(key As String, name As String)
If KeyExists(key) = False Then
' let collection create the new item
' with the minimum required info
mcol.Add(New Article(key, name))
End If
End Sub
If you add an Item Property, you can index the collection ( Articles(3) ):
Property Item(ndx As Int32) As Article
Get
If ndx > 0 AndAlso ndx < mcol.Count Then
Return mcol(ndx)
Else
Return Nothing
End If
End Get
Set(value As Article)
If ndx > 0 AndAlso ndx < mcol.Count Then
mcol(ndx) = value
End If
End Set
End Property
' overload for item by key:
Public Property Item(key As String) As Article
An Add method and an Item Property will be important if the collection will display in the standard NET CollectionEditor.
There are several ways to implement sorting. The easiest is to use linq in the code which uses your collection:
Articles = New ArticleCollection
' add Article items
Dim ArticlesByDate = Articles.OrderBy(Function(s) s.PubDate).ToList()
Where PubDate is one of the Article properties I added. The other way to handle sorting is by the collection class returning a new collection (but it is so simple to do, there is little need for it):
Friend Function GetSortedList(bSortAsc As Boolean) As List(Of Article)
If bSortAsc Then
Return mcol.OrderBy(Function(q) q.PubDate).
ThenBy(Function(j) j.Position).ToList()
Else
Return mcol.OrderByDescending(Function(q) q.PubDate).
ThenByDescending(Function(j) j.Position).ToList()
End If
End Function
Whether it implements ICollection(Of T), inherits from ICollection(Of T) or does work off a Dictionary depends entirely on what this is, how it is used and whatever rules and restrictions there are (including if it will be serialized and how). These are not things we know.
MSDN has an article on Guidelines for Collections which is excellent.
Create your class
Public Class Artikel
Property ID As Integer
Property Nummer As String
Property Name As String
Property Position As Integer
sub new (_ID as integer, _Nummer as string, _Name as string, _Position as integer)
ID = _ID
Nummer = _Nummer
Name = _Name
Position = _Position
End Sub
End Class
Create another class which holds a private list and add sub routines to it
Public Class ArtikelList
Private _List as new list (of Artikel)
Public sub remove(Key as integer)
Dim obj as Artikel = nothing
for each x as Artikel in _List
if x.ID = Key then
obj = x
exit for
end if
Next
if not isnothing(obj) then
_List.remove(obj)
end if
End sub
Sub Add(obj as Artikel)
Dim alreadyDeclared as boolean = falsse
for each x as Artikel in _List
if x.ID = obj.id then
alreadyDeclared = true
exit for
end if
Next
if not AlreadyDeclared then
_List.add(obj)
Else
'Somehow inform the user of the duplication if need be.
end if
End sub
End Class
Then use your list class.
dim L as new ArtikelList
L.add(new Artikel(1280, "AFKforever!", "Prof.FluffyButton", 96))
L.remove(1280)
I only added one sub routine as an example. I hope it helps but feel free to ask for more example routines.
This can also be done by creating a class which inherits from the list class, exposing all list class functionality but by using this method you are forced to create every subroutine that will be used. This way you only use routines that you created exclusively for the purpose Artikel objects handling.
Check if two Artikels are equal
Public Class Artikel
Property ID As Integer
Property Nummer As String
Property Name As String
Property Position As Integer
sub new (_ID as integer, _Nummer as string, _Name as string, _Position as integer)
ID = _ID
Nummer = _Nummer
Name = _Name
Position = _Position
End Sub
End Class
Public Overrides Overloads Function Equals(obj As Object) As Boolean
If obj Is Nothing OrElse Not Me.GetType() Is obj.GetType() Then
Return False
else
dim _obj as artikel = obj
if Me.ID = _obj.ID then
Return true
else Return False
End If
End Function
End Class
Use it like:
If x.equals(y) then
'they have the same ID
end if

Class with a list of class in VB

I am trying to write a DLL that will accept an custom input with a nested list(of T)
I created 2 classes, a Main Class ClassPolyPoints and the nested Class classEF
I am getting an error when trying to pass the list(of classEF) object into the ClassPolyPoints object.
Dim TMP_effPoints As New List(Of classEF)
For i = 0 to 10
TMP_effPoints.Add(New classEff(
i,
i*0.125
))
Next
Dim tmpClass As New ClassPolyPoints(9.8765, TMP_effPoints)
Class that contains the nested list(of classEF)
Public Class ClassPolyPoints
Sub New(ByVal x_P0 As Double,
ByVal x_EffPoints As List(Of classEF))
_P0 = x_P0
With _effPoints
For Each a In x_EffPoints
.Add(New classEFF(
a.ID,
a.Eff
))
Next
End With
End Sub
Private _effPoints As List(Of classEff)
Public Property effPoints() As List(Of classEff)
Get
Return _effPoints
End Get
Set(ByVal value As List(Of classEff))
_effPoints = value
End Set
End Property
Private _P0 As Double
Public Property P0() As Double
Get
Return _P0
End Get
Set(ByVal value As Double)
_P0 = value
End Set
End Property
End Class
The nested class
Public Class classEF
Sub New(X_ID As Integer, X_Eff As Double)
_ID = X_ID
_Eff = X_Eff
End Sub
Private _ID As Integer
Public Property ID() As Integer
Get
Return _ID
End Get
Set(ByVal value As Integer)
_ID = value
End Set
End Property
Private _Eff As Double
Public Property Eff() As Double
Get
Return _Eff
End Get
Set(ByVal value As Double)
_Eff = value
End Set
End Property
End Class
You don't need to loop here just assign the list.
Public Class ClassPolyPoints
Sub New(ByVal x_P0 As Double,
ByVal x_EffPoints As List(Of classEF))
Me._P0 = x_P0
Me.effPoints = x_EffPoints
End Sub
'''
The issue is that you forgotten to create an istance of _effPoints before trying to add to the list new elements of class classEF:
That is in class ClassPolyPoints, you have to change the following declaration:
Private _effPoints As List(Of classEff)
into this:
Private _effPoints As New List(Of classEF)

How should I store Child Classes in an enumerable and how to access them correctly?

Public Class UniqueList(Of T As BaseClass)
Inherits List(Of T)
Public Overridable Overloads Sub Add(value As T)
If Not Me.Contains(value) Then MyBase.Add(value)
End Sub
Public Function [Get](val As integer) As T
Return Me.Where(Function(cb) cb.Id = val)(0)
End Function
End Class
I get en error if I try to use it without casting it first, so I I cast it before trying to get my object :
dim mylist as new UniqueList(of Baseclass)
mylist.add(new ChildClass(1))
dim x as ChildClass= (mylist.get(1))
x.RandomMethod() ...
so I tried to create a new function in my UniqueList class that would cast it for me :
Public Function [GetChildClass](val As integer) As ChildClass
Return DirectCast(Me.Where(Function(cb) cb.Id = val)(0), ChildClass)
End Function
but I always get errors saying value T cannot be converted to ChildClass... is there any way to have this function returns me the correct object ?
edit: I cant change it to a list of ChildClass
...
edit : declare them however you want ...
Class BaseClass
public id as integer
public sub new(id as integer)
me.id = id
end sub
end class
Class ChildClass
inherits BaseClass
Public sub New(id as integer)
mybase.new(id)
end sub
public sub randomMethod()
'do nothing
end sub
end class
You may be looking for something like this:
Public Function [GetParent](val As integer) As ChildClass
Return DirectCast(Me.OfType(Of ChildClass).
Where(Function(cb) cb.id = val)(0), ChildClass)
End Function
Note - this will fail if no elements of type ChildClass are found, you can prevent it from failing by using .FirstOrDefault instead of manually indexing (0) into the first element. FirstOrDefault will return Nothing if no elements were found.

A class can only be a template for a single object not a template for a collection

I have a simple class List.vb which is the following:
Public Class List
Public fList As List(Of Integer)
Public Sub New()
fList = New List(Of Integer)
fList.Add(1)
fList.Add(2)
fList.Add(3)
fList.Add(4)
fList.Add(5)
End Sub
End Class
The Console application is using this class like the following:
Module Module1
Sub Main()
Dim fObject As List = New List
Dim cnt As Integer = 0
For Each x As Integer In fObject.fList
Console.WriteLine("hello; {0}", fObject.fList.Item(cnt).ToString())
cnt = cnt + 1
Next
Console.WriteLine("press [enter] to exit")
Console.Read()
End Sub
End Module
Can I change the class code so that List.vb is a list(of integer) type?
This would mean that in the Console code I could replace In fObject.fList with just In fObject?
Or am I barking up the wrong tree - should classes be single objects and lists should be collections of classes ?
Yes, you can do that. In order for an object to be compatible with For Each, it must have a GetEnumerator function:
Public Function GetEnumerator() As IEnumerator _
Implements IEnumerable.GetEnumerator
Return New IntListEnum(fList)
End Function
The IntListEnum class must, in turn, implement IEnumerator, like this:
Public Class IntListEnum Implements IEnumerator
Private listInt As List(Of Integer)
Dim position As Integer = -1
Public Sub New(ByVal fList As List(Of Integer))
listInt = fList
End Sub
Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext
position = position + 1
Return (position < listInt.Count)
End Function
Public Sub Reset() Implements IEnumerator.Reset
position = -1
End Sub
Public ReadOnly Property Current() As Object Implements IEnumerator.Current
Get
Try
Return listInt(position)
Catch ex As IndexOutOfRangeException
Throw New InvalidOperationException()
End Try
End Get
End Property
End Class
Now you can make fList private, and iterate your List as follows:
For Each x As Integer In fObject
You can see a complete example here.
The answer that dasblinkenlight has provided is excellent, but if all you need is a list that of integers that is pre-populated, you can just inherit from List(Of Integer) and then have the class populate itself in the constructor:
Public Class List
Inherits List(Of Integer)
Public Sub New()
Add(1)
Add(2)
Add(3)
Add(4)
Add(5)
End Sub
End Class
When you inherit from List(Of Integer), your class automatically gets all of the functionality implemented by that type, so your class also becomes a list class that works the same way. Then, you can just use it like this:
Dim fObject As New List()
For Each x As Integer In fObject
Console.WriteLine("hello; {0}", x)
Next

VB.NET CType: How do I use CType to change an object variable "obj" to my custom class that I reference using a string variable like obj.GetType.Name?

The code below works for the class that I hard coded "XCCustomers" in my RetrieveIDandName method where I use CType. However, I would like to be able to pass in various classes and property names to get the integer and string LIST returned. For example, in my code below, I would like to also pass in "XCEmployees" to my RetrieveIDandName method. I feel so close... I was hoping someone knew how to use CType where I can pass in the class name as a string variable.
Note, all the other examples I have seen and tried fail because we are using Option Strict On which disallows late binding. That is why I need to use CType.
I also studied the "Activator.CreateInstance" code examples to try to get the class reference instance by string name but I was unable to get CType to work with that.
When I use obj.GetType.Name or obj.GetType.FullName in place of the "XCCustomers" in CType(obj, XCCustomers)(i)
I get the error "Type 'obj.GetType.Name' is not defined" or "Type 'obj.GetType.FullName' is not defined"
Thanks for your help.
Rick
'+++++++++++++++++++++++++++++++
Imports DataLaasXC.Business
Imports DataLaasXC.Utilities
Public Class ucCustomerList
'Here is the calling method:
Public Sub CallingSub()
Dim customerList As New XCCustomers()
Dim customerIdAndName As New List(Of XCCustomer) = RetrieveIDandName(customerList, "CustomerId", " CustomerName")
'This code below fails because I had to hard code “XCCustomer” in the “Dim item...” section of my RetrieveEmployeesIDandName method.
Dim employeeList As New XCEmployees()
Dim employeeIdAndName As New List(Of XCEmployee) = RetrieveIDandName(employeeList, "EmployeeId", " EmployeeName")
'doing stuff here...
End Sub
'Here is the method where I would like to use the class name string when I use CType:
Private Function RetrieveIDandName(ByVal obj As Object, ByVal idPropName As String, ByVal namePropName As String) As List(Of IntStringPair)
Dim selectedItems As List(Of IntStringPair) = New List(Of IntStringPair)
Dim fullyQualifiedClassName As String = obj.GetType.FullName
Dim count As Integer = CInt(obj.GetType().GetProperty("Count").GetValue(obj, Nothing))
If (count > 0) Then
For i As Integer = 0 To count - 1
'Rather than hard coding “XCCustomer” below, I want to use something like “obj.GetType.Name”???
Dim Item As IntStringPair = New IntStringPair(CInt(CType(obj, XCCustomers)(i).GetType().GetProperty("CustomerId").GetValue(CType(obj, XCCustomers)(i), Nothing)), _
CStr(CType(obj, XCCustomers)(i).GetType().GetProperty("CustomerName").GetValue(CType(obj, XCCustomers)(i), Nothing)))
selectedItems.Add(Item)
Next
End If
Return selectedItems
End Function
End Class
'+++++++++++++++++++++++++++++++
' Below are the supporting classes if you need to see what else is happening:
Namespace DataLaasXC.Utilities
Public Class IntStringPair
Public Sub New(ByVal _Key As Integer, ByVal _Value As String)
Value = _Value
Key = _Key
End Sub
Public Property Value As String
Public Property Key As Integer
End Class
End Namespace
'+++++++++++++++++++++++++++++++
Namespace DataLaasXC.Business
Public Class XCCustomer
Public Property CustomerId As Integer
Public Property CustomerName As String
End Class
End Namespace
'+++++++++++++++++++++++++++++++
Namespace DataLaasXC.Business
Public Class XCCustomers
Inherits List(Of XCCustomer)
Public Sub New()
PopulateCustomersFromDatabase()
End Sub
Public Sub New(ByVal GetEmpty As Boolean)
End Sub
End Class
End Namespace
'+++++++++++++++++++++++++++++++
Namespace DataLaasXC.Business
Public Class XCEmployee
Public Property EmployeeId As Integer
Public Property EmployeeName As String
End Class
End Namespace
'+++++++++++++++++++++++++++++++
Namespace DataLaasXC.Business
Public Class XCEmployees
Inherits List(Of XCEmployee)
Public Sub New()
PopulateEmployeesFromDatabase()
End Sub
Public Sub New(ByVal GetEmpty As Boolean)
End Sub
End Class
End Namespace
From MSDN
CType(expression, typename)
. . .
typename : Any expression that is legal
within an As clause in a Dim
statement, that is, the name of any
data type, object, structure, class,
or interface.
This is basically saying you can't use CType dynamically, just statically. i.e. At the point where the code is compiled the compiler needs to know what typename is going to be.
You can't change this at runtime.
Hope this helps.
Since List(Of T) implements the non-generic IList interface, you could change your function declaration to:
Private Function RetrieveIDandName(ByVal obj As System.Collections.IList, ByVal idPropName As String, ByVal namePropName As String) As List(Of IntStringPair)
And then your troublesome line would become (with also using the property name parameters):
Dim Item As IntStringPair = New IntStringPair(CInt(obj(i).GetType().GetProperty(idPropName).GetValue(obj(i), Nothing)), _
CStr(obj(i).GetType().GetProperty(namePropName).GetValue(obj(i), Nothing)))
Of course, you could still have the first parameter by Object, and then attempt to cast to IList, but that's up to you.
ctype is used to convert in object type.