Microsoft Visual Basic: |
|
Overview of Collections |
Introduction |
A database is collection of values. To create these values, you can use a collection class. Fortunately, instead of creating a class from scratch, the .NET Framework provides a very impressive library of interfaces and collection classes. The built-in interfaces of the .NET Framework lay a foundation that other classes use to implement and customize the desired functionality. The various built-in collection classes are meant to satisfy various purposes. Some classes are available to any application and can be used by any Windows control. Some other collection classes are nested in classes that particularly need them. |
The .NET Framework supports collections in various namespaces. While the System.Collections namespace provides regular collection classes, the System.Collections.Generic namespace contains the equivalent generic classes.
One the most routines operations performed on a database consists of reviewing its values. To assist you with this, the .NET Framework provides the IEnumerator and the IEnumerable interfaces that are defined in the System.Collections namespace. Their generic equivalences can be found in the System.Collections.Generic namespace. After implementing these interfaces, you can use the foreach operator to visit each value of the database. To implement the System.Collections.IEnumerator interface, you must derive a class from it. Then, you must define the Reset(), the MoveNext() methods, and the Current property. Here is an example: Imports System.ComponentModel Public Class Enumerator Implements IEnumerator Private Names() As String Private Cur As Integer Public Sub New(ByVal lst() As String) Names = lst Cur = -1 End Sub Public ReadOnly Property Current() Implements IEnumerator.Current Get Return Names(Cur) End Get End Property Public Sub Reset() Implements IEnumerator.Reset Cur = -1 End Sub Public Function Movenext() As Boolean Implements IEnumerator.MoveNext Cur = Cur + 1 If Cur < Names.Length Then Return True Else Return False End If End Function End Class To implement the System.Collections.IEnumerable interface, you must derive a class from it. When implementing the class, you must define an accessory method and the GetEnumerator() method that returns an IEnumerator object. Here is an example: Imports System.Collections Public Class Enumerable Implements IEnumerable Private Names() As String Public Sub Identify(ByVal Values() As String) Names = Values Dim i As Integer For i = 0 To Values.Length - 1 Names(i) = Values(i) Next End Sub Public Function GetEnumerator() As IEnumerator _ Implements IEnumerable.GetEnumerator Return New Enumerator(Names) End Function End Class Once you have implemented the interfaces, you can use For Each. Here is an example: Public Class Exercise Private Sub Exercise_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles MyBase.Load Dim strName As String Dim FullNames(7) As String FullNames(0) = "Gertrude Monay" FullNames(1) = "Paul Bertrand Yamaguchi" FullNames(2) = "Hermine Ngaleu" FullNames(3) = "Francine Mukoko" FullNames(4) = "Joseph Walters" FullNames(5) = "Patricia Katts" FullNames(6) = "Helen Cranston" FullNames(7) = "Paul Motto" Dim coll As Enumerable = New Enumerable coll.Identify(FullNames) For Each strName In coll lbxNames.Items.Add(strName) Next End Sub End Class
While the IEnumerator and the IEnumerable interfaces serve as valuable accessories that allow a collection class to support enumeration, to actually create a collection class, there are other interfaces you can use to implement the functionality you want for your collection. When you want to use a collection in your application, you may first check what classes are available in the .NET Framework. If you don't find a suitable class, you can create your own that implements one or more interfaces. As it happens, the .NET Framework ships with many of them and your next step is to choose which one you prefer. Some of the most commonly used interfaces are
One of the primary pieces of information you should provide about a the values in a database is the number of values that a list is (currently) holding. When creating a collection class, to prepare it to provide this valuable information, you can (should) implement an interface named ICollection. The ICollection interface is defined in the System.Collections namespace while its equivalent of the same name is defined in the System.Collections.Generic namespace. This means that, if you are creating a class that implements it, you should include this namespace in the file. Here is an example for the System.Collections.ICollection interface: Public Class BookCollection Implements ICollection End Class
To assist you with keeping track of the number of items in a collection, the ICollection interface is equipped with a property named Count, which you must implement. To do this, you can create a private member variable that will actually keep a count of the number of items. The Count property can then be used to communicate this information to the clients of the class. Here is an example: Public Class BookCollection Implements ICollection Private NumberOfBooks As Integer Public Sub New() NumberOfBooks = 0 End Sub Public ReadOnly Property Count() As Integer_ Implements ICollection.Count Get Return NumberOfBooks End Get End Property End Class The ICollection interface also allows its implementer to copy some of its items to an array. To provide this functionality, the interface is equipped with a method named CopyTo, which you must implement. The syntax of this method is: Sub CopyTo(array As Array, index As Integer) This method takes two arguments. The first argument is the array that will receive the items. The second argument is the index of the item from where the copying operation will begin. Here is an example: Public Class BookCollection Implements ICollection Private Books() As String Private NumberOfBooks As Integer Public Sub New() NumberOfBooks = 0 End Sub Public ReadOnly Property Count() As Integer _ Implements ICollection.Count Get Return NumberOfBooks End Get End Property Public Sub CopyTo(ByVal ary As Array, ByVal index As Integer) _ Implements ICollection.CopyTo Dim i As Integer Dim bks(Count) As String For i = 0 To Count - 1 bks(i) = Books(i) ary = bks i = i + 1 Next End Sub End Class If you create a collection class, you can provide the ability to enumerate its items. When this is done, some time to time, you will want to identify or to know what item is currently being accessed. In case other collection classes are using the same function at the time you are accessing this information, you should have an object that is responsible for synchronizing the collection. To do this in your ICollection-based class, you must implement a property named SyncRoot. This property must return an Object object. Here is an example: Public Class BookCollection Implements ICollection . . . No Change Public ReadOnly Property SyncRoot() As Object _ Implements ICollection.SyncRoot Get Return Me End Get End Property End Class Besides the ability to specify the number of items in a collection, a class that implements the ICollection interface must retrieve a value that indicates whether its item is synchronized. To give this information, you must implement a Boolean property named IsSynchronized. Here is an example: Public Class BookCollection Implements ICollection . . . No Change Public ReadOnly Property IsSynchronized() As Boolean _ Implements ICollection.IsSynchronized Get Return False End Get End Property End Class System.Collections.ICollection (and System.Collections.Generic.ICollection) exdepends the IEnumerable interface. This means that you should be able to use foreach in your ICollection-based class but you must create the functionality yourself, which is done by implementing the GetEnumerator() method. Even if you don't want to support this feature, you still must provide at least a skeleton for this method. Here is an example: Public Class BookCollection Implements ICollection Private Books() As String Private NumberOfBooks As Integer Public Sub New() NumberOfBooks = 0 End Sub Public ReadOnly Property Count() As Integer _ Implements ICollection.Count Get Return NumberOfBooks End Get End Property Public Sub CopyTo(ByVal ary As Array, ByVal index As Integer) _ Implements ICollection.CopyTo Dim i As Integer Dim bks(Count) As String For i = 0 To Count - 1 bks(i) = Books(i) ary = bks i = i + 1 Next End Sub Public ReadOnly Property SyncRoot() As Object _ Implements ICollection.SyncRoot Get Return Me End Get End Property Public ReadOnly Property IsSynchronized() As Boolean _ Implements ICollection.IsSynchronized Get Return False End Get End Property Public Function GetEnumerator() As IEnumerator _ Implements IEnumerable.GetEnumerator Return Nothing End Function End Class
While it provides the minimum functionality of a collection, the System.Collections.ICollection (and the System.Collections.Generic.ICollection) interface is not equipped to perform the regular operations of a collection class, such as adding, retrieving, or deleting items from a set. To assist you with creating a collection class as complete as possible, the .NET Framework provides an interface named IList. The IList interface is defined in the System.Collections namespace and its equivalent of the same name is defined in the System.Collections.Generic namespace. The interface is equipped with the methods necessary to add, insert, delete, or retrieve items from a collection. Because the functionalities of these methods may not suit you, to use these features, you must create a class that implements them.
As mentioned above, to create a collection, you can derive it from the IList interface. Here is an example: Imports System.Collections Public Class BookCollection Implements IList End Class This System.Collections.IList interface is declared as follows: Public Interface IList _ Implements ICollection, IEnumerable This System.Collections.Generic.IList interface is declared as follows: Public Interface IList(Of T) _ Implements ICollection(Of T), _ IEnumerable(Of T), _ IEnumerable This means that the IList interface exdends both the ICollection and the IEnumerable interfaces. This also implies that you must implement the members of these parent interfaces. In other words, you must implement the Count property, the SyncRoot property, the IsSynchronized property, and the CopyTo() method of the ICollection interface. If you use Microsoft Visual Basic 2008 to create the project, as soon as you type the Implements IList line and press Enter, the studio would generate the complete skeleton code for you: Public Class BookCollection Implements IList Public Sub CopyTo(ByVal array As System.Array, ByVal index As Integer) _ Implements System.Collections.ICollection.CopyTo End Sub Public ReadOnly Property Count() As Integer _ Implements System.Collections.ICollection.Count Get End Get End Property Public ReadOnly Property IsSynchronized() As Boolean _ Implements System.Collections.ICollection.IsSynchronized Get End Get End Property Public ReadOnly Property SyncRoot() As Object _ Implements System.Collections.ICollection.SyncRoot Get End Get End Property Public Function GetEnumerator() As System.Collections.IEnumerator _ Implements System.Collections.IEnumerable.GetEnumerator End Function Public Function Add(ByVal value As Object) As Integer _ Implements System.Collections.IList.Add End Function Public Sub Clear() Implements System.Collections.IList.Clear End Sub Public Function Contains(ByVal value As Object) As Boolean _ Implements System.Collections.IList.Contains End Function Public Function IndexOf(ByVal value As Object) As Integer _ Implements System.Collections.IList.IndexOf End Function Public Sub Insert(ByVal index As Integer, ByVal value As Object) _ Implements System.Collections.IList.Insert End Sub Public ReadOnly Property IsFixedSize() As Boolean _ Implements System.Collections.IList.IsFixedSize Get End Get End Property Public ReadOnly Property IsReadOnly() As Boolean _ Implements System.Collections.IList.IsReadOnly Get End Get End Property Default Public Property Item(ByVal index As Integer) As Object _ Implements System.Collections.IList.Item Get End Get Set(ByVal value As Object) End Set End Property Public Sub Remove(ByVal value As Object) _ Implements System.Collections.IList.Remove End Sub Public Sub RemoveAt(ByVal index As Integer) _ Implements System.Collections.IList.RemoveAt End Sub End Class You can then customize it. From what we learned with ICollection, here are examples of implementing these members for the System.Collections.IList interface: Public Class BookCollection Implements IList Private Counter As Integer Private Objects(10) As Object Public Sub New() Counter = 0 End Sub Public Sub CopyTo(ByVal array As System.Array, ByVal index As Integer) _ Implements System.Collections.ICollection.CopyTo End Sub Public ReadOnly Property Count() As Integer _ Implements System.Collections.ICollection.Count Get Return Counter End Get End Property Public ReadOnly Property IsSynchronized() As Boolean _ Implements System.Collections.ICollection.IsSynchronized Get Return False End Get End Property Public ReadOnly Property SyncRoot() As Object _ Implements System.Collections.ICollection.SyncRoot Get Return Me End Get End Property End Class You must also implement the System.Collections.GetEnumerator() (or the System.Collections.Generic.GetEnumerator()) method of the System.Collections.IEnumerable (or of the System.Collections.Generic.IEnumerable) interface. If you do not have time to completely implement it, you can simply return Nothing. Here is an example for the System.Collections.IList interface: Public Function GetEnumerator() As System.Collections.IEnumerator _ Implements System.Collections.IEnumerable.GetEnumerator Return Nothing End Function
In the next sections, we will see how to add values to a list of a database. As you add or insert values in a list, the Count property grows. If your collection is array-based, when you start it, you specify the number of values that the list will contain. In theory, you cannot add new values beyond that number. In reality, you can increase the size of an array and then add a new item. If your collection is a linked list, you are also not confined to the laws of space (unless your computer runs out of memory). If you create a list whose number of values must be constant, the user cannot add values beyond the maximum allowed number. Therefore, before adding a value, you can first check whether the collection has a fixed size or not. To give you this information, the IList interface is equipped with a Boolean read-only property named IsFxedSize. This property simply lets the user know whether the collection has a fixed number of items. Here is an example of implementing this property for the System.Collections.IList interface: Public ReadOnly Property IsFixedSize() As Boolean _ Implements System.Collections.IList.IsFixedSize Get Return False End Get End Property
Most databases are meant to receive new values. If you want, you can create a list that cannot receive new values. To support this, the IList interface is equipped with the Boolean IsReadOnly property. If a list is read-only, it would prevent the clients from adding items to it. Here is an example of implementing the IsReadOnly property for the System.Collections.IList interface: Public ReadOnly Property IsReadOnly() As Boolean _ Implements System.Collections.IList.IsReadOnly Get Return False End Get End Property
As it should be obvious, the primary operation to perform on a list is to populate it with at least one value. To support this, the System.Collections.IList interface is equipped with a method named Add. Its syntax is: Function Add(value As Object) As Integer This method takes one argument as the value to add to the list. If your collection is an array, you can first check that there is still enough room in the list to add a new item. In reality, this is never an issue with the System.Collections.IList interface:
If the method succeeds with the addition, it returns the position where the value was added in the list. This is usually the last position in the list. Here is an example: Public Function Add(ByVal value As Object) As Integer _ Implements System.Collections.IList.Add ' Check whether there is still room in ' the array to add a new item If Counter < Objects.Length Then ' Since there is room, put the new item to the } Objects(Counter) = value ' increase the number of items Counter = Counter + 1 ' Return the index of the item that was added Return Counter - 1 ' Since the item could not be added, return a negative index Else Return -1 End If End Function
When you call the System.Collections.IList.Add() method, it adds the new value to the } of the list. Sometimes, you will want the new value to be insert somewhere inside the list. To support this operation, both the System.Collections.IList and the System.Collections.Generic.IList interfaces provide a method named Insert. The syntax of the System.Collections.IList.Insert() method is: Sub Insert(index As Integer, value As Object) The syntax of the System.Collections.Generic.IList.Insert() method is: Sub Insert(index As Integer, item As T) This method takes two arguments. The second argument is the value that will be inserted into the list. The argument must hold a valid value. Because this method takes an Object object, if your collection is using a different type of value, you may have to cast it to Object. The first argument is the index of the item that will precede the new one. As mentioned for the System.Collections.IList. Add() method, there are a few things you should know about this operation's success or lack of it:
While using a list, various operations require that you know the object you are currently accessing. To provide this operation, you must create an indexed property. This property should take an index and return the type of object that makes up the list. Here is an example: Default Public Property Item(ByVal index As Integer) As Object _ Implements System.Collections.IList.Item Get Return Objects(index) End Get Set(ByVal value As Object) Objects(index) = value End Set End Property After creating this property, you can then access an item using its index and applying the () operator on its instance. Remember that if you want to use For Each, you must appropriately implement the IEnumerable.GetEnumerator() method.
One of the most valuables features of Visual Basic is to use the For Each loop to enumerate the members of a collection. To make this possible, you must implement the IEnumerator interface in your collection class. Following the rules of interface implementation, you must override the members of IEnumerator.
One of the routine operations you can perform on a list is to find out whether it contains a certain value. To assist you with this operation, the System.Collections.IList interface is equipped with a method named Contains. Its syntax is: Function Contains(value As Object) As Boolean This method takes as argument the value to look for. If the value is found in the list, the method returns true. If no value is found in the collection, this method returns false. Here is an example of implementing this method: Public Function Contains(ByVal value As Object) As Boolean _ Implements System.Collections.IList.Contains Dim i As Integer For i = 0 To Count - 1 If Objects(i) = value Then Return True i = i + 1 End If Next Return False End Function This method calls the Equals() method of the objects that make up the list to find out whether the value argument exists in the collection. If this method produces a wrong result, especially if you are using your own class to represent the item, you may have to override your own Equals() method.
The System.Collections.IList.Contains() method is used to check whether a particular value (already) exists in the collection. If you know that a certain item exists in the collection but you do not know its index inside the list, the IList interface can assist you through a method named IndexOf. Its syntax is: Function IndexOf(value As Object) As Integer This method takes as argument the value to look for in the list. If the value is found in the collection, the method returns its index. If there is no value defined like that, the method returns -1. Here is an example of implementing this method: Public Function IndexOf(ByVal value As Object) As Integer _ Implements System.Collections.IList.IndexOf Dim i As Integer For i = 0 To Count - 1 If Objects(i) = value Then Return 1 i = i + 1 End If Next Return -1 End Function This method calls the Equals() method of the objects that make up the collection to find out whether the value argument exists in the list. If this method produces a wrong result, especially if you are using your own class to represent the value, you may have to override your own Equals() method.
If a value is not necessary in your list, you can delete it. Probably the simplest way to delete a value is to specify its position in the list. To support this operation, both the System.Collections.IList and the System.Collections.Generic.IList interfaces are equipped with a method named RemoveAt. The syntax of the RemoveAt() method is is the same for both interfaces and it is: Sub RemoveAt(index As Integer) This method takes as argument the index of the value to be removed. Here is an example: Public Sub RemoveAt(ByVal index As Integer) _ Implements System.Collections.IList.RemoveAt Dim i As Integer If (index >= 0) And (index < Count) Then For i = index To Count - 1 Objects(i) = Objects(i + 1) Counter = Counter - 1 Next End If End Sub
The problem with deleting a value based on its index is that, if you are not sure, you could delete the wrong value. An alternative is to first precisely define the value you want to get rid of, and then hand the value itself to the compiler that would remove it. To support this approach, the System.Collections.IList interface is equipped with a method named Remove() and whose syntax is: Sub Remove(value As Object) This method takes as argument the value to be deleted. This means that the compiler will first look for the value in the list. If it finds that value, it removes it. If there is no value like that, nothing happens (the compiler doesn't throw an exception. Here is an example of implementing this method: Public Sub Remove(ByVal value As Object) _ Implements System.Collections.IList.Remove RemoveAt(IndexOf(value)) End Sub
To remove all value from a list at once, you can implement Clear() method of the System.Collections.IList interface. Its syntax is: Sub Clear Here is an example of implementing it: Public Class BookCollection Implements IList Private Counter As Integer Private Objects(10) As Object Public Sub New() Counter = 0 End Sub Public Sub CopyTo(ByVal array As System.Array, ByVal index As Integer) _ Implements System.Collections.ICollection.CopyTo End Sub Public ReadOnly Property Count() As Integer _ Implements System.Collections.ICollection.Count Get Return Counter End Get End Property Public ReadOnly Property IsSynchronized() As Boolean _ Implements System.Collections.ICollection.IsSynchronized Get Return False End Get End Property Public ReadOnly Property SyncRoot() As Object _ Implements System.Collections.ICollection.SyncRoot Get Return Me End Get End Property Public Function GetEnumerator() As System.Collections.IEnumerator _ Implements System.Collections.IEnumerable.GetEnumerator Return Nothing End Function Public Function Add(ByVal value As Object) As Integer _ Implements System.Collections.IList.Add ' Check whether there is still room in ' the array to add a new item If Counter < Objects.Length Then ' Since there is room, put the new item to the } Objects(Counter) = value ' increase the number of items Counter = Counter + 1 ' Return the index of the item that was added Return Counter - 1 ' Since the item could not be added, return a negative index Else Return -1 End If End Function Public Sub Clear() Implements System.Collections.IList.Clear Counter = 0 End Sub Public Function Contains(ByVal value As Object) As Boolean _ Implements System.Collections.IList.Contains Dim i As Integer For i = 0 To Count - 1 If Objects(i) = value Then Return True i = i + 1 End If Next Return False End Function Public Function IndexOf(ByVal value As Object) As Integer _ Implements System.Collections.IList.IndexOf Dim i As Integer For i = 0 To Count - 1 If Objects(i) = value Then Return 1 i = i + 1 End If Next Return -1 End Function Public Sub Insert(ByVal index As Integer, ByVal value As Object) _ Implements System.Collections.IList.Insert End Sub Public ReadOnly Property IsFixedSize() As Boolean _ Implements System.Collections.IList.IsFixedSize Get Return False End Get End Property Public ReadOnly Property IsReadOnly() As Boolean _ Implements System.Collections.IList.IsReadOnly Get Return False End Get End Property Default Public Property Item(ByVal index As Integer) As Object _ Implements System.Collections.IList.Item Get Return Objects(index) End Get Set(ByVal value As Object) Objects(index) = value End Set End Property Public Sub Remove(ByVal value As Object) _ Implements System.Collections.IList.Remove RemoveAt(IndexOf(value)) End Sub Public Sub RemoveAt(ByVal index As Integer) _ Implements System.Collections.IList.RemoveAt Dim i As Integer If (index >= 0) And (index < Count) Then For i = index To Count - 1 Objects(i) = Objects(i + 1) Counter = Counter - 1 Next End If End Sub End Class
|
|
||
Home | Copyright © 2008-2016, FunctionX, Inc. | |
|