1. Trang chủ
  2. » Công Nghệ Thông Tin

Tài liệu Creating a Generic Class docx

12 306 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Creating a generic class
Định dạng
Số trang 12
Dung lượng 28,46 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

A binary tree is a recursive self-referencing data structure that can either be empty or comprise three elements: some data that is typically referred to as the node and two sub-trees, w

Trang 1

Creating a Generic Class

The NET Framework Class Library contains a number of generic classes readily

available for you You can also define your own generic classes, which is what you will

do in this section Before we do this, I will provide a bit of background theory

The Theory of Binary Trees

In the following exercises, you will define and use a class that represents a binary tree This is a very practical exercise because this class happens to be one that is missing from the System.Collections.Generic namespace A binary tree is a very useful data structure used for a variety of operations, including sorting and searching through data very

quickly There are volumes written on the minutiae of binary trees, but it is not the

purpose of this book to cover binary trees in detail Instead, we will just look at the

pertinent details If you are interested you should consult a book such as The Art of Computer Programming, Volume 3: Sorting and Searching by Donald E Knuth

(Addison-Wesley Professional; 2nd edition 1998)

A binary tree is a recursive (self-referencing) data structure that can either be empty or comprise three elements: some data that is typically referred to as the node and two sub-trees, which are themselves binary trees The two sub-trees are conventionally called the left sub-tree and the right sub-tree because they are typically depicted to the left and right

of the node respectively Each left sub-tree or right sub-tree is either empty, or contains a node and other sub-trees In theory, the whole structure can continue ad infinitum Figure 17-1 shows the structure of a small binary tree

The real power of binary trees becomes evident when you use them for sorting data If you start with an unordered sequence of objects of the same type, you can use them to construct an ordered binary tree and then walk through the tree to visit each node in an ordered sequence The algorithm for inserting an item into an ordered binary tree is shown below:

If the tree, T, is empty

Then

Construct a new tree T with the new item I as the node, and empty left and

right sub-trees

Else

Examine the value of the node, N, of the tree, T

If the value of N is greater than that of the new item, I

Then

If the left sub-tree of T is empty

Trang 2

Then

Construct a new left sub-tree of T with the item I as the node, and

empty left and right sub-trees

Else

Insert I into the left sub-tree of T

End If

Else

If the right sub-tree of T is empty

Then

Construct a new right sub-tree of T with the item I as the node, and

empty left and right sub-trees

Else

Insert I into the right sub-tree of T

End If

End If

End If

Figure 17-1 A binary tree

Notice that this algorithm is recursive, calling itself to insert the item into the left or right sub-tree depending on the value of the item and the current node in the tree

NOTE

The definition of the expression greater than depends on the type of data in the item and node For numeric data, greater than can be a simple arithmetic comparison, for text data

it can be a string comparison, but other forms of data must be given their own means of comparing values This is discussed in more detail when we implement a binary tree in the section “Building a Binary Tree Class Using Generics” later in this chapter

If you start with an empty binary tree and an unordered sequence of objects, you can iterate through the unordered sequence inserting each one into the binary tree by using this algorithm, resulting in an ordered tree Figure 17-2 shows the steps in the process for constructing a tree from a set of 5 integers

Figure 17-2 Constructing an ordered binary tree

Once you have built an ordered binary tree, you can display its contents in sequence by visiting each node in turn and printing the value found The algorithm for achieving this task is also recursive:

If the left sub-tree is not empty

Trang 3

Then

Display the contents of the left sub-tree

End If

Display the value of the node

If the right sub-tree is not empty

Then

Display the contents of the right sub-tree

End If

Figure 17-3 shows the steps in the process for outputting the tree constructed earlier Notice that the integers are now displayed in ascending order

Figure 17-3 Printing an ordered binary tree

Building a Binary Tree Class Using Generics

In the following exercise, you will use generics to define a binary tree class capable of holding almost any type of data The only restriction is that the data type must provide a means of comparing values between different instances

The binary tree class is a class that you might find useful in many different applications Therefore, you will implement it as a class library rather than an application in its own right You can then reuse this class elsewhere without needing to copy the source code and recompile it A class library is a set of compiled classes (and other types such as structs and delegates) stored in an assembly An assembly is a file that usually has the

“.dll” suffix Other projects and applications can make use of the items in a class library

by adding a reference to its assembly, and then bringing its namespaces into scope with using statements You will do this when you test the binary tree class

The System.IComparable and System.IComparable<T> Interfaces

If you need to create a class that requires you to be able to compare values according to some natural (or possibly unnatural) ordering, you should implement the IComparable interface This interface contains a method called CompareTo, which takes a single parameter specifying the object to be compared to the current instance and returns an integer that indicates the result of the comparison as shown in the following table Value Meaning

Less than zero The current instance is less than the value of the parameter

Zero The current instance is equal to the value of the parameter

Greater than zero The current instance is greater than the value of the parameter

Trang 4

As an example, consider the Circle class that was described in Chapter 7, “Creating and Managing Classes and Objects,” and is reproduced below:

class Circle

{

public Circle(double initialRadius)

{

radius = initialRadius;

}

public double Area()

{

return 3.141593 * radius * radius;

}

private double radius;

}

You can make the Circle class comparable by implementing the System.IComparable interface and providing the CompareTo method In the example shown, the CompareTo method compares Circle objects based on their areas The area of a circle with a larger area is greater than a circle with a smaller area

class Circle : System.IComparable

{

public int CompareTo(object obj)

{

Circle circObj = (Circle)obj; // cast the parameter to its real type

if (this.Area() == circObj.Area())

return 0;

if (this.Area() > circObj.Area())

return 1;

return -1;

}

}

If you examine the System.IComparable interface, you will see that its parameter is

defined as an object However, this approach is not typesafe To understand why this is

so, consider what happens if you try to pass something that is not a Circle to the

CompareTo method The System.IComparable interface requires the use of a cast in order

Trang 5

to be able to access the Area method If the parameter is not a Circle but some other type

of object then this cast will fail However, the System namespace also defines the generic IComparable<T> interface, which contains the following methods:

int CompareTo(T other);

bool Equals(T other);

Notice there is an additional method in this interface, called Equals, which should return true if both instances are equals, false if they are not equals

Also notice that these methods take a type parameter (T) rather than an object, and as such, are much safer than the non-generic version of the interface The following code shows how you can implement this interface in the Circle class:

class Circle : System.IComparable<Circle>

{

public int CompareTo(Circle other)

{

if (this.Area() == other.Area())

return 0;

if (this.Area() > other.Area())

return 1;

return -1;

}

public bool Equals(Circle other)

{

return (this.CompareTo(other) == 0);

}

}

The parameters for the CompareTo and Equals method must match the type specified in the interface, IComparable<Circle> In general, it is preferable to implement the

System.IComparable<T> interface rather than System.IComparable You can also

implement both, as many of the types in the NET Framework do

Create the Tree<T> class

1 Start Visual Studio 2005

Trang 6

2 Create a new Visual C# project using the Class Library template (on the File menu, point to New, and then click Project) Name the project BinaryTree and set the Location to \Microsoft Press\Visual CSharp Step By Step\Chapter 17

3 In the Solution Explorer change the name of the file Class1.cs to Tree.cs

4 In the Code and Text Editor window, change the definition of the Class1 class to Tree<T>

The definition of the Tree<T> class should look like this:

public class Tree<T>

{

}

NOTE

It is conventional to use a single letter (often T) to indicate a type parameter, although any C# legal identifier is allowed If a generic class uses multiple type parameters, you must use a different identifier for each type For example, the generic Dictionary class in the NET Framework is defined as Dictionary<K, V> The K type indicates the type used for dictionary keys, and the V type indicates the type used for dictionary values

5 In the Code and Text Editor window, modify the definition of the Tree<T> class

to specify that the type parameter T must denote a type that implements the

generic IComparable<T> interface

The modified definition of the Tree<T> class should look like this:

public class Tree<T> where T : IComparable<T>

{

}

6 Add three private variables to the Tree<T> class; a T variable called data, and two Tree<T> variables called left and right:

7 private T data;

8 private Tree<T> left;

private Tree<T> right;

9 Add a constructor to the Tree<T> class that takes a single T parameter called nodeValue In the constructor, set the data variable to nodeValue, and initialize the left and right variables to null, as shown below:

10 public Tree(T nodeValue)

Trang 7

11 {

12 this.data = nodeValue;

13 this.left = null;

14 this.right = null;

}

15 Add a public property of type T called NodeData to the Tree<T> class This

property should provide get and set accessors allowing the user to read and modify the data variable:

The NodeData property should look like this:

public T NodeData

{

get { return this.data; }

set { this.data = value; }

}

16 Add two more properties to the Tree<T> class called LeftTree and RightTree These properties are very similar to each other, and provide get and set access to the left and right Tree<T> variables respectively

The LeftTree property should look like this (the RightTree property is the same except that it uses the right variable):

public Tree<T> LeftTree

{

get { return this.left; }

set { this.left = value; }

}

17 Add a public method called Insert to the Tree<T> class This method will insert a

T value into the tree

The method definition should look like this:

public void Insert (T newItem)

{

}

The Insert method will follow the algorithm described earlier The programmer will have used the constructor to insert the initial node into the tree, so the Insert method can assume that the tree is not empty The next part of the algorithm is

Trang 8

reproduced below to help you understand the code you will write in the following steps:

Examine the value of the node, N, of the tree, T

If the value of N is greater than that of the new item, I

Then

If the left sub-tree of T is empty

Then

Construct a new left sub-tree of T with the item I as the node, and empty

left and right sub-trees

Else

Insert I into the left sub-tree of T End If

18 In the Insert method, add a statement that declares a local variable of type T, called currentNodeValue Initialize this variable to the value of the NodeData property of the tree, as shown below:

19 public void Insert(T newItem)

20 {

21 T currentNodeValue = this.NodeData;

}

22 Add the following if-else statement to the Insert method, after the definition of the currentNodeValue variable This statement uses the CompareTo method of the IComparable<T> interface to determine whether the value of the current node is greater then the new item:

23 if (currentNodeValue.CompareTo(newItem) > 0)

24 {

25 //Insert the new item into the left sub-tree

26 }

27 else

28 {

29 // Insert the new item into the right sub-tree

}

30 Replace the //Insert the new item into the left sub-tree comment with the following block of code:

31 if (this.LeftTree == null)

32 {

33 this.LeftTree = new Tree<T>(newItem);

34 }

35 else

Trang 9

36 {

37 this.LeftTree.Insert(newItem);

}

These statements check whether the left tree is empty If so, a new left sub-tree is created using the new item; otherwise the new item is inserted into the existing left sub-tree by calling the Insert method recursively

38 Replace the //Insert the new item into the right sub-tree comment with the equivalent code that inserts the new node into the right sub-tree:

39 if (this.RightTree == null)

40 {

41 this.RightTree = new Tree<T>(newItem);

42 }

43 else

44 {

45 this.RightTree.Insert(newItem);

}

46 Add another public method called WalkTree to the Tree<T> class This method will walk through the tree, visiting each node in sequence and printing out its value

The method definition should look like this:

public void WalkTree()

{

}

47 Add the following statements to the WalkTree method These statements

implement the algorithm described earlier for printing the contents of a binary tree:

48 if (this.LeftTree != null)

49 {

50 this.LeftTree.WalkTree();

51 }

52

53 Console.WriteLine(this.NodeData.ToString());

54

55 if (this.RightTree != null)

56 {

57 this.RightTree.WalkTree();

}

Trang 10

58 On the Build menu, click Build Solution The class should compile cleanly, so correct any errors that are reported and rebuild the solution if necessary

In the next exercise, you will test the Tree<T> class by creating binary trees of integers and strings

Test the Tree<T> class

1 On the File menu, point to Add and then click New Project Add a new project using the Console Application template Name the project BinaryTreeTest and set the Location to \Microsoft Press\Visual CSharp Step By Step\Chapter 17

NOTE

Remember that a Visual Studio 2005 solution can contain more than one project You are using this feature to add a second project to the BinaryTree solution for testing the Tree<T> class This is the recommended way of testing class libraries

2 Ensure the BinaryTreeTest project is selected in the Solution Explorer On the Project menu, click Set as Startup Project The BinaryTreeTest project will be highlighted in the Solution Explorer

When you run the application, this is the project that will actually be executed

3 On the Project menu, click Add Reference In the Add Reference dialog box, click the Projects tab Click the BinaryTree project and then click OK

The BinaryTree assembly will appear in the list of references for the

BinaryTreeTest project in the Solution Explorer You will now be able to create Tree<T> objects in the BinaryTreeTest project

NOTE

If the class library project is not part of the same solution as the project that uses it, you must add a reference to the assembly (the “.dll” file) and not to the class

library project You do this by selecting the assembly from the Browse tab in the Add Reference dialog box You will use this technique in the final set of exercises

in this chapter

4 In the Code and Text Editor window displaying the Program class, add the

following using directive to the list at the top of the class:

using BinaryTree;

5 Add the following statements to the Main method:

6 Tree<int> tree1 = new Tree<int>(10);

Ngày đăng: 24/12/2013, 09:16

TỪ KHÓA LIÊN QUAN