Generic Interfaces in .NET

Team-Fly    

 
Application Development Using Visual Basic and .NET
By Robert J. Oberg, Peter Thorsteinson, Dana L. Wyatt
Table of Contents
Chapter 6.  VB.NET in the .NET Framework


The .NET Framework exposes much standard functionality through generic interfaces, which are implemented in various combinations by classes in the Framework itself, and which can also be implemented by your own classes in order to tap into standard functionality defined by the Framework. In this section we will look at several categories of operations that are supported by these standard, generic interfaces.

  • Collections

  • Copying objects

  • Comparing objects

Our survey of generic interfaces is by no means exhaustive, but our sampling should give you a good understanding of how generic interfaces work in the .NET Framework.

Collection Interfaces

Now that we understand the concept of interfaces, we are equipped to take a closer look at collections, and in particular at the ArrayList class that we have used so heavily in the case study. If we look at the definition of ArrayList , we see that it implements four standard interfaces.

 Public Class ArrayList    Implements IList, ICollection, IEnumerable, ICloneable 

The first three interfaces form a simple interface hierarchy, as shown in Figure 6-1. As you go down the hierarchy, additional methods are added until IList specifies a fully featured list.

Figure 6-1. Interface hierarchy for lists.

graphics/06fig01.gif

The fourth interface, ICloneable , is independent and is used to support deep copying. As a simple illustration of the collection interfaces, we provide the program StringList . Here is the Main method. We'll look at the individual helper methods as we examine the various collection interfaces.

 graphics/codeexample.gif ' StringList.vb Imports System Imports System.Collections Module StringList    Private m_list As ArrayList    Sub Main()       ' Initialize strings and show starting state       m_list = New ArrayList(4)       ShowCount()       AddString("Amy")       AddString("Bob")       AddString("Charlie")       ShowEnum(m_list)  ' enumerator       ShowCount()       ' Add two more string and show state again       AddString("David")       AddString("Ellen")       ShowList(m_list) ' for each       ShowCount()       ' Remove two strings from list and show state       RemoveString("David")       RemoveAt(0)       ShowArray(m_list) ' index notation       ShowCount()       ' Try to remove two strings not in list       RemoveString("Amy")       RemoveAt(3)    End Sub    ... 

Here is the output:

 list.Count = 0 list.Capacity = 4 Amy Bob Charlie list.Count = 3 list.Capacity = 4 Amy Bob Charlie David Ellen list.Count = 5 list.Capacity = 8 array[0] = Bob array[1] = Charlie array[2] = Ellen list.Count = 3 list.Capacity = 8 List does not contain Amy No element at index 3 
Interface Documentation

Predefined interfaces are documented in the online .NET Framework SDK documentation. Figure 6-2 illustrates the documentation of the IEnumerable interface. The right-hand pane has a language filter button graphics/filter_icon.jpg , which we have used to show only VB.NET versions. If you are using the interface in one of the .NET Framework classes that implement the interface, you do not need to implement any of the methods yourself, since they are implemented for you. If you are creating your own class that supports an interface, you must provide implementations of all the methods of the interface. In either case, the documentation describes the interface methods for you.

Figure 6-2. .NET Framework SDK documentation for IEnumerable interface.

graphics/06fig02.jpg

Ienumerable and Ienumerator

The basic interface that must be supported by collection classes is IEnumerable , which has a single method, GetEnumerator .

 Public Interface IEnumerable     Function GetEnumerator() As IEnumerator End Interface 

GetEnumerator returns an interface reference to IEnumerator , which is the interface used for iterating through a collection. This interface has the read-only property Current and the methods MoveNext and Reset .

 Public Interface IEnumerator    ReadOnly Property Current As Object    Function MoveNext() As Boolean    Sub Reset() End Interface 

The enumerator is initially positioned before the first element in the collection, and it must be advanced before it is used. The ShowEnum method (in the StringList example) illustrates using an enumerator to iterate through a list.

 Private Sub ShowEnum(ByVal array As ArrayList)  Dim iter As IEnumerator = Array.GetEnumerator()   Dim more As Boolean = iter.MoveNext()  While (more)  Dim str As String = iter.Current  Console.WriteLine(str)  more = iter.MoveNext()  End While End Sub 

This pattern of using an enumerator to iterate through a list is so common that VB.NET provides a special kind of loop, known as For Each , that can be used for iterating through the elements of any collection. Here is the comparable code using For Each .

 Private Sub ShowList(ByVal array As ArrayList)    Dim str As String  For Each str In array  Console.WriteLine(str)    Next End Sub 
Icollection

The ICollection interface is derived from IEnumerable and adds a Count property and a CopyTo method as well as IsSynchronized and SyncRoot properties.

 Public Interface ICollection    Inherits IEnumerable    ReadOnly Property Count As Integer    ReadOnly Property IsSynchronized As Boolean    ReadOnly Property SyncRoot As Object    Sub CopyTo(ByVal array As Array, ByVal index As Integer) End Interface 

The synchronization properties IsSynchronized and SyncRoot can help you deal with thread safety issues. "Is it thread safe?" is a question frequently asked about library code. The short answer to this question for the .NET Framework class library is no. This does not mean that the designers of the Framework did not think about thread safety issues. On the contrary, there are many mechanisms to help you write thread-safe code when you need to. The reason that collections are not automatically thread safe is that your code should not have to pay the performance penalty to enforce synchronization when it is not running in a multithreading scenario. If you do need thread safety, you may use the thread-safety properties as required. We discuss the .NET mechanisms for thread synchronization in Chapter 10.

Our StringList program illustrates use of the Count property of ICollection . The Capacity property is not defined in any interface, but is rather exposed by the ArrayList class itself.

 Private Sub ShowCount()    Console.WriteLine("list.Count = {0}",  m_list.Count  )    Console.WriteLine(_       "list.Capacity = {0}", m_list.Capacity) End Sub 
IList

The IList interface is derived from ICollection and provides methods for adding an item to a list, removing an item, and so on. A default parameterized property ( Item ) is provided that enables array notation to be used. (We discussed parameterized properties in Chapter 4.)

 Public Interface IList    Inherits ICollection, IEnumerable    ReadOnly Property IsFixedSize As Boolean    ReadOnly Property IsReadOnly As Boolean    Default Property Item(ByVal index As Integer) As Object    Function Add(ByVal value As Object) As Integer    Sub Clear()    Function Contains(ByVal value As Object) As Boolean    Function IndexOf(ByVal value As Object) As Integer    Sub Insert(ByVal index As Integer, ByVal value _      As Object)    Sub Remove(ByVal value As Object)    Sub RemoveAt(ByVal index As Integer) End Interface 

Our StringList sample code illustrates using the indexer and the Add , Contains , Remove , and RemoveAt methods.

 Private Sub ShowArray(ByVal array As ArrayList)    Dim i As Integer = 0    While i < array.Count       Console.WriteLine("array[{0}] = {1}", i,  array(i)  )       i += 1    End While End Sub ... Private Sub AddString(ByVal str As String)    If  m_list.Contains(str)  Then       Throw New Exception("list contains " & str)    End If  m_list.Add(str)  End Sub Private Sub RemoveString(ByVal str As String)    If  m_list.Contains(str)  Then  m_list.Remove(str)  Else       Console.WriteLine("List does not contain {0}", str)    End If End Sub Private Sub RemoveAt(ByVal index As Integer)    Try  m_list.RemoveAt(index)  Catch e As ArgumentOutOfRangeException       Console.WriteLine("No element at index {0}", index)    End Try End Sub 

Copy Semantics and ICloneable

Sometimes you have to make a copy of an object. When you copy objects that contain objects and object references, you have to be aware of the nuances of copy semantics. We will compare reference copy, shallow memberwise copy, and deep copy. We will see that by implementing the ICloneable interface in your class, you can perform a deep copy.

Recall that VB.NET has value types and reference types. A value type contains all its own data, while a reference type refers to data stored somewhere else. If a reference variable gets copied to another reference variable, both will refer to the same object. If the object referenced by the second variable is changed, the first variable will also reflect the new value. Sometimes you want this behavior, but sometimes you do not.

Shallow Copy and Deep Copy

A structure in VB.NET automatically implements a memberwise copy, sometimes known as a shallow copy . The Object root class has a protected method, MemberwiseClone , which will perform a memberwise copy of members of a class.

If one or more members of a class are of a reference type, this memberwise copy may not be good enough. The result will be two data member references to the same data, not two independent copies of the data. To actually copy the data itself and not merely the data member references, you will need to perform a deep copy . Deep copy can be provided at either the language level or the library level. In C++ deep copy is provided at the language level through a copy constructor . In VB.NET deep copy is provided by the .NET Framework through a special interface, ICloneable , which you can implement in your classes in order to enable them to perform deep copy.

Example Program

We will illustrate all these ideas in the program CopyDemo . This program makes a copy of a Course instance. The Course class consists of a title and a collection of students.

 graphics/codeexample.gif ' Course.vb Imports System Imports System.Collections Public Class Course  Implements ICloneable  Public Title As String    Public Roster As ArrayList    Public Sub New(ByVal titleText As String)       Title = titleText       Roster = New ArrayList()    End Sub    Public Sub AddStudent(ByVal name As String)       Roster.Add(name)    End Sub    Public Sub Show(ByVal caption As String)       Console.WriteLine("-----{0}-----", caption)       Console.WriteLine(_          "Course : {0} with {1} students", _          Title, Roster.Count)       Dim name As String       For Each name In Roster          Console.WriteLine(name)       Next    End Sub    Public Function  ShallowCopy  () As Course       Return Me.  MemberwiseClone  ()    End Function    Public Function  Clone  () As Object _     Implements ICloneable.Clone     Dim course As Course = New Course(Title)     course.Roster = Roster.Clone()     Return course   End Function End Class 

The test program constructs a Course instance c1 and then makes a copy c2 using reference copy, shallow copy, and deep copy, as described in the next three sections.

Reference Copy by Assignment

The first way the copy is performed is by the straight assignment c2 = c1 . Now we get two references to the same object, and if we make any change through the first reference, we will see the same change through the second reference. The first part of the test program illustrates such an assignment.

 ' CopyDemo.vb Imports System Imports System.Collections Module CopyDemo    Private c1 As Course    Private c2 As Course    Sub Main()        Console.WriteLine("Copy is done via c2 = c1")        InitializeCourse()        c1.Show("original")  c2 = c1  c2.Show("copy")  c2.Title = ".NET Programming"   c2.AddStudent("Charlie")  c2.Show("copy with changed title and new student")        c1.Show("original")        ...     End Sub     Private Sub InitializeCourse()        c1 = New Course("Intro to VB")        c1.AddStudent("John")        c1.AddStudent("Mary")    End Sub End Module 

We initialize with the title "Intro to VB.NET" and two students. We make the assignment c2 = c1 and then change the title and add another student for c2 . We then show both c1 and c2 , and we see that both reflect the changes, meaning that the reference c1 has been copied to c2 , and c2 no longer references the object that it originally did. Here is the output from this first part of the program:

 Copy is done via c2 = c1 -----original----- Course : Intro to VB.NET with 2 students John Mary -----copy----- Course : Intro to VB.NET with 2 students John Mary -----copy with changed title and new student----- Course :  .NET Programming with 3 students  John Mary  Charlie  -----original----- Course :  .NET Programming with 3 students  John Mary  Charlie  
Memberwise Clone

Next we will illustrate doing a memberwise copy, which can be accomplished using the MemberwiseClone method of Object . Since this method is Protected, we cannot call it directly from outside the Course class. Instead, in Course we define a method, ShallowCopy , which is implemented using MemberwiseClone .

 ' Course.vb Imports System Imports System.Collections Public Class Course    Implements ICloneable    ...  Public Function ShallowCopy() As Course   Return Me.MemberwiseClone()  End Function    ... End Class 

Here is the second part of the test program, which calls the ShallowCopy method. Again, we change the title and a student in the second copy.

 ' CopyDemo.vb Imports System Imports System.Collections Module CopyDemo    ...    Sub Main()       ...       Console.WriteLine()       Console.WriteLine(_          "Copy is done via c2 = c1.ShallowCopy()")       InitializeCourse()  c2 = c1.ShallowCopy()  c2.Title = ".NET Programming"       c2.AddStudent("Charlie")       c2.Show("copy with changed title and new student")       c1.Show("original")       ...    End Sub EndModule 

Here is the output of this second part of the program. Now the Title field has its own independent copy, but the Roster collection is just copied by reference, so each copy refers to the same collection of students.

 Copy is done via c2 = c1.ShallowCopy() -----copy with changed title and new student-----  Course : .NET Programming with 3 students  John Mary  Charlie  -----original-----  Course : Intro to VB.NET with 3 students  John Mary  Charlie  
Using ICloneable

The final version of copy relies on the fact that our Course class supports the ICloneable interface and implements the Clone method. To clone the Roster collection, we use the fact that ArrayList also implements the ICloneable interface, as discussed earlier in the chapter.

 ' Course.vb Imports System Imports System.Collections Public Class Course  Implements ICloneable  ...  Public Function Clone() As Object _   Implements ICloneable.Clone  Dim course As Course = New Course(Title)       course.Roster = Roster.Clone()       Return course    End Function    ... End Class 

Here is the third part of the test program, which calls the Clone method. Again, we change the title and a student in the second copy.

 ' CopyDemo.vb Imports System Imports System.Collections Module CopyDemo    ...    Sub Main()       ...       Console.WriteLine()       Console.WriteLine(_          "   Copy is done via c2 = c1.Clone()")       InitializeCourse()  c2 = c1.Clone()  c2.Title = ".NET Programming"       c2.AddStudent("Charlie")       c2.Show("copy with changed title and new student")       c1.Show("original")       ...    End Sub EndModule 

Here is the output from the third part of the program. Now we have completely independent instances of Course . Each has its own title and set of students.

 Copy is done via c2 = c1.Clone() -----copy with changed title and new student-----  Course : .NET Programming with 3 students  John Mary  Charlie  -----original-----  Course : Intro to VB.NET with 2 students  John Mary 

Comparing Objects

We have quite exhaustively studied copying objects. We now examine comparing objects. To compare objects, the .NET Framework uses the interface IComparable . In this section we use the interface IComparable to sort an array.

Sorting an Array

The System.Array class provides a static method, Sort , that can be used for sorting an array. The program ArrayName illustrates applying this Sort method to an array of Name objects, where the Name class simply encapsulates a String through a read-only property Text . Here is the main program.

 graphics/codeexample.gif ' ArrayName.vb ... Module ArrayName    Sub Main()       Dim array() As Name = New Name(10) {}       array(0) = New Name("Michael")       array(1) = New Name("Charlie")       array(2) = New Name("Peter")       array(3) = New Name("Dana")       array(4) = New Name("Bob")  If TypeOf array(0) Is IComparable Then   array.Sort(array)  Else          Console.WriteLine(_             "Name does not implement IComparable")       End If       Dim name As Name       For Each name In array          If Not name Is Nothing Then             Console.WriteLine(name)          End If       Next    End Sub End Module 
Implementing IComparable

In order for the Sort method to function, there must be a way of comparing the objects that are being sorted. This comparison is achieved through the CompareTo method of the interface IComparable . Thus, to sort an array of a type you define, you must implement IComparable for your type.

 Public Interface IComparable    Function CompareTo(ByVal obj As Object) As Integer End Interface 

Here is the implementation of the Name class, with its implementation of IComparable .

 Public Class Name  Implements IComparable  Private m_text As String    Public Sub New(ByVal text As String)        Me.m_text = text    End Sub    Public ReadOnly Property Text() As String       Get           Return m_text       End Get    End Property  Public Function CompareTo(ByVal obj As Object)  _  As Integer Implements IComparable.CompareTo  Dim s1 As String = Me.m_text       Dim s2 As String = obj.Text       Return String.Compare(s1, s2)    End Function    Public Overrides Function ToString() As String       Return Text    End Function End Class 

Understanding Frameworks

Our example offers some insight into the workings of frameworks. A framework is more than just a library. In a typical library, you are only concerned with your code calling library functions. In a framework, you call into the framework and the framework may call back into your class methods . Your program can be viewed as the middle layer of a sandwich.

  • Your code calls the bottom layer.

  • The top layer calls your code.

The .NET Framework is an excellent example of such an architecture. There is rich functionality that you can call directly. There are many interfaces, which you can optionally implement to make your program behave appropriately when called by the framework, often on behalf of other objects.


Team-Fly    
Top
 


Application Development Using Visual BasicR and .NET
Application Development Using Visual BasicR and .NET
ISBN: N/A
EAN: N/A
Year: 2002
Pages: 190

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net