Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

C# ПІДРУЧНИКИ / c# / MS Press - Oop With MS Vb Net And C# Step By Step (2002)

.pdf
Скачиваний:
126
Добавлен:
12.02.2016
Размер:
2.56 Mб
Скачать

§Dim reader As New System.IO.StreamReader(fullPath)

§Dim nameStart As Integer

§Dim oneline As String

§oneline = reader.ReadLine()

§While (Not (oneline Is Nothing))

§oneline = oneline.Trim()

§If ((oneline <> "") And (Not oneline.StartsWith("‘"))) Then

§m_linesOfCode += 1

§End If

§If (oneline.StartsWith("Public Class")) Then

§nameStart = oneline.IndexOf("Class") + 6

§Dim names() As String

§Dim separators() As Char = {ControlChars.Tab, " "c}

§names = oneline.Substring( _

§nameStart).Trim().Split(separators)

§Dim className As String = names(0).Trim()

§m_classNames.Add(New AClass(className, fullPath))

§End If

§oneline = reader.ReadLine()

§End While

§reader.Close()

§Catch ex As System.Exception

§Throw New System.Exception( _

§"Problems parsing source file: " + ex.Message)

§End Try

§End Sub

§

§// Visual C#

§public void ReadFromFile(string fullPath) {

§try {

§System.IO.StreamReader reader = new

§System.IO.StreamReader(fullPath);

§int nameStart;

§string oneline;

§while ((oneline = reader.ReadLine()) != null) {

§oneline = oneline.Trim();

§// Don’t count blank or comment lines.

§if ((oneline != "") && (!oneline.StartsWith("\\"))) {

§m_linesOfCode++;

§}

§

 

§

if (oneline.StartsWith("public class")) {

§

nameStart = oneline.IndexOf("class") + 6;

§

char[] separators = { ‘ ‘, ‘\t’, ‘{‘};

§

string[] names =

§

oneline.Substring(nameStart).Trim().Split(separators)

 

;

§

string className = names[0].Trim();

§

m_classNames.Add(new AClass(className,fullPath));

§

}

§}

§reader.Close();

§}

§catch (System.Exception ex) {

§throw new System.Exception(

§"Problems parsing source file: " + ex.Message);

§}

}

Add the Indexer or Default property

1.If you’re using Visual Basic, add the following default ReadOnly property:

2.‘ Visual Basic

3.Default Public ReadOnly Property Classes(ByVal index As Intege

r)As AClass

4.Get

5.If (index >= 0) And (index < m_classNames.Count) Then

6.Return CType(m_classNames(index), AClass)

7.Else

8.Throw New System.IndexOutOfRangeException( _

9."Index must be between 0 and " & _

10.m_classNames.Count.ToString() & ".")

11.End If

12.End Get

13.‘Set(ByVal Value As AClass)

14.‘ If (index >= 0) And (index < m_classNames.Count) Then

15.‘ m_classNames(index) = Value

16.‘ Else

17.‘ Throw New System.IndexOutOfRangeException( _

18.‘ "Index must be between 0 and " & _

19.‘ m_classNames.Count.ToString() & ".")

20.‘ End If

21.‘End Set

End Property

To create a default property in Visual Basic, add the Default keyword to the property declaration. Default properties in Visual Basic must have at least one parameter. You can have more than one parameter, and those parameters can be of any type. Only one property in a class can have the Default keyword.

When a property is a default property, you use it by following the instance name with the index in parentheses. You don’t need to include the property name. Note that your code is responsible for checking that the index value from the client code is valid.

This property is read-only. The setter method is shown in comments as an example of implementing a property setter.

22.If you’re using Visual C#, add the following indexer:

23.// Visual C#

24.public AClass this[int indexer] {

25.get {

26.if ((indexer >= 0) && (indexer < m_classNames.Count))

27.return (AClass)m_classNames[indexer];

28.else

29.throw new System.Exception("Index must be between 0 a

nd "

30.+ m_classNames.Count.ToString() + ".");

31.}

32.//set {

33.// m_classNames[indexer] = value;

34.//}

}

The C# indexer doesn’t have a name. Instead, you specify it by using the this keyword. If you’re using Visual Basic, you can access the default property with or without the property name, but there’s no property name for a C#

indexer. You can access the indexed value only by indexing the instance name.

The C# indexer requires no special keyword for a read-only indexer. You obtain the meaning in context. The setter method is shown in comments in the code listing to demonstrate how a setter would be implemented. As in the Visual Basic example, your code is responsible for checking the validity of the indexer value.

Add the LinesOfCode and Count properties

1.Add this code for the LinesOfCode property:

2.‘ Visual Basic

3.Public ReadOnly Property LinesOfCode() As Integer

4.Get

5.Return m_linesOfCode

6.End Get

7.End Property

8.

9.

10.// Visual C#

11.public int LinesOfCode {

12.get { return m_linesOfCode; }

}

13.Add this code for the Count property. When you implemented the SourceFile class, you named this property ClassCount. In this case, a reader already knows you are working with a group of classes. The word Class would be redundant: Classes.ClassCount. So, you use Count: Classes.Count.

14.‘ Visual Basic

15.Public ReadOnly Property Count() As Integer

16.Get

17.Return m_classNames.Count

18.End Get

19.End Property

20.

21.‘ Visual Basic

22.public int Count {

23.get { return m_classNames.Count; }

}

The classes are complete.

Creating the User Interface

You’ll need to add some controls to the form for testing the classes.

Place the controls on the form

1.Drag a DataGrid control onto Form1 and set the Name property to listOfFiles.

2.Drag a Button control onto Form1. Set its Name property to browse and its Text property to Browse. You’ll use this button to browse for a source file that you want to analyze.

3.Drag a Label control onto Form1. Set its Name property to linesOfCode and its Text property to Lines of code. You’ll use this label to display the cumulative lines of code in all the files you analyze.

4.Drag an OpenFileDialog control onto Form1. It will be displayed in the component tray. Set its Name property to openSourceFile. Here’s the complete user interface:

Write the DisplayClasses method

1.In the Solution Explorer window, right-click Form1 and click View Code on the shortcut menu.

2.Add this declaration of one instance of the Classes class:

3.‘ Visual Basic

4.Private m_classes As New Classes()

5.

6.// Visual C#

private Classes m_classes = new Classes();

You need only one instance of the Classes class because it contains multiple instance of the AClass class. Also note that no instances of AClass are created by the code in the form. The instances of AClass are created only by means of the ReadFromFile method of Classes.

7.Add the following method to the Form1 class. Note that this is a private method of the Form1 class. You can call this method only by code within the Form1 class. It’s a helper function for displaying the classes.

8.‘ Visual Basic

9.Private Sub DisplayClasses()

10.Dim classes(m_classes.Count) As AClass

11.Dim i As Integer

12.For i = 0 To m_classes.Count - 1

13.‘ Using the default property

14.classes(i) = m_classes(i)

15.Next

16.listOfFiles.DataSource = classes

17.linesOfCode.Text = _

18."Lines of code: " & m_classes.LinesOfCode.ToString()

19.End Sub

20.

21.// Visual C#

22.private void DisplayClasses() {

23.AClass[] classes = new AClass[m_classes.Count];

24.for (int i = 0; i < m_classes.Count; i++ ) {

25.// Using the indexer

26.classes[i] = m_classes[i];

27.}

28.this.listOfFiles.DataSource = classes;

29.linesOfCode.Text =

30."Lines of code: " + m_classes.LinesOfCode.ToString();

}

In this example, the AClass instances of m_Classes are placed in an array that’s used as the data source for the DataGrid control. As in the first implementation, the public properties of AClass are used as the column headings.

Note the use of the default property and indexer. You don’t need to use the name of the property. Simply typing in the indexing character, ( in Visual Basic or [ in Visual C#, prompts IntelliSense to display the default property or indexer signature.

Tip When you’re using an indexer, it needs to make sense. In the .NET Framework, you typically find indexers on properties whose names are plurals, such as the Rows property of a table, which is a collection of Row objects. It wouldn’t make sense to use an indexer on a Dog class that returned a Leg object!

Program the Browse button

1.In the form designer, double-click the Browse button to create the Click event method in the code editor. Add the following code to the Click event method.

2.‘ Visual Basic

3.Private Sub browse_Click(ByVal sender As System.Object, _

4.ByVal e As System.EventArgs) Handles browse.Click

5.

6. End Sub

7.

8.// Visual C#

9.private void browse_Click(object sender, System.EventArgs e) {

10.}

11.Add this code to the Click event method to prompt the user for a file and parse it into the m_classes object.

12.‘ Visual Basic

13.Try

14.openSourceFile.Filter = "Visual Basic files (*.vb)|*.vb"

15.Dim result As System.Windows.Forms.DialogResult

16.‘ If user selected a file, create a SourceFile from it.

17.result = openSourceFile.ShowDialog()

18.If (result = DialogResult.OK) Then

19.m_classes.ReadFromFile(openSourceFile.FileName)

20.End If

21.DisplayClasses()

22.Catch ex As System.Exception

23.MessageBox.Show(ex.Message)

24.End Try

25.

26.// Visual C#

27.try {

28.openSourceFile.Filter = "Visual C# files (*.cs)|*.cs";

29.System.Windows.Forms.DialogResult result;

30.result = openSourceFile.ShowDialog();

31.if (result == System.Windows.Forms.DialogResult.OK) {

32.m_classes.ReadFromFile(openSourceFile.FileName);

33.}

34.DisplayClasses();

35.}

36.catch (System.Exception ex) {

37.MessageBox.Show(ex.Message);

}

Test the program

1.Press F5 to run the program.

2.Click Browse, and in the Open dialog box navigate to the source files for the project. Select a source file, and click Open.

3.Add some empty classes to the source file to further test the application. An example is shown here.

Quick Reference

 

 

 

 

 

 

To

 

Do this

 

 

 

 

 

 

 

Create a

 

In Visual Basic, the syntax is

 

 

 

Public Property Name() As String

 

 

read-write

 

 

 

 

Get

 

 

property

 

 

 

 

‘ Return a value here.

 

 

 

 

 

 

 

 

End Get

 

 

 

 

Set(ByVal Value As String)

 

 

 

 

‘ Set a value here.

 

 

 

 

End Set

 

 

 

 

End Property

 

 

 

 

 

 

 

 

 

In C#, the syntax is

 

 

 

 

public string Name {

 

 

 

 

get {

 

 

 

 

// Return a value here.

 

 

 

 

set {

 

 

 

 

// Set a value here.

 

 

 

 

}

 

 

 

 

}

 

 

 

 

}

 

 

 

 

 

 

 

 

 

Or

 

 

 

 

 

 

 

 

 

Use the C# Property Wizard.

 

 

 

 

 

 

To

Create a read-only property

Create a write-only property

Create a property that takes a

is parameter (Visual Basic only)

Create documentati on comments for Visual C# properties

Use an array of class instances

DataSource property of the DataGrid control

Create a

Do this

In Visual Basic, the syntax is

Public ReadOnly Property Name() As String Get

‘ Return a value here. End GetEnd Property

In C#, the syntax is public string Name {

get {

// Return a value here.

}

}

Or

Use the C# Property Wizard.

In Visual Basic, the syntax is

Public WriteOnly Property Name() As String Set(ByVal Value As String)

‘ Set a value here. End Set

End Property

In C#, the syntax is public string Name {

set {

// Set a value here.

}}

Or

Use the C# Property Wizard.

In Visual Basic, the syntax

Public Property Classes(ByVal index As Integer) Get

Return a value, based on index End Get

Set(ByVal Value)

Set a value, based on index

End Set

End Property

Add this comment immediately before the property declaration:

///<summary>

///Returns the full path of the source file.

///</summary>public string FullPath { get { return m_fullPath; }

}

Create an array of class instances, and then set the as a data source in a DataGrid to the array.

In Visual Basic

Private m_sourceFiles(MaxFiles) _ As SourceFile

listOfFiles.DataSource = m_sourceFiles

In Visual C#

private SourceFile[] m_sourceFiles = new SourceFile[MaxFiles];

listOfFiles.DataSource = m_sourceFiles;

The syntax is

 

 

 

 

 

 

 

To

 

Do this

 

 

 

 

 

 

 

 

C#

 

public AClass this[int indexer] {

 

 

indexer

 

get

{

 

 

 

 

}

// Return a value here.

 

 

 

 

 

 

 

 

 

set

{

 

 

 

 

}

// Set a value here.

 

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

Create a

 

The syntax is

 

 

 

Default

Public ReadOnly Property Classes_

 

 

Visual Basic

 

 

 

 

(ByVal index As Integer) As AClass

 

 

default

 

 

 

 

Get

 

 

 

property

 

‘ Return a value here.

 

 

 

 

 

 

 

 

 

 

 

 

 

End

Get

 

 

 

 

Set(ByVal Value As AClass)

 

 

 

 

 

‘ Set a value here.

 

 

 

 

End

Set

 

 

 

 

End Property

 

 

 

 

 

 

 

Chapter 4: Working with Methods

Overview

ESTIMATED

3 hr.

TIME

 

 

 

In this chapter, you’ll learn how to

§Create a private method.

§Create an overloaded method.

§Add a control to a form at run time.

§Use the Randomize and ArrayList classes.

§Perform drag-and-drop operations.

In previous chapters, the classes you created were heavy on properties, and most of the work of the class was done in the constructor to initialize the properties. In this chapter, you’ll explore a range of behaviors that objects can display. You’ll create a class that has a private method, which is used by the class but not available to client code. You’ll create an overloaded method, which is a method that can take more than one set of arguments. Finally, in testing the classes you develop, you’ll add a control to a form at run time, rather than in the form designer.

A Deck of Cards

In this chapter, you’ll build three classes to represent the actions of manipulating a deck of playing cards, and you’ll build an application to test these classes. This deck of cards has no particular application but will provide properties and methods that any of several card games might use. Because you don’t have a specific application to target, you’ll

implement a set of methods extensive enough to be useful. At the same time, you must be careful not to add methods that aren’t really part of the abstraction of the class.

The first class, Card, represents one playing card. This class has only two properties, Suit (Hearts, Clubs, Spades, or Diamonds) and Value (Ace, King, Queen, and so on). This class also has a constructor.

The second class is Hand, representing the cards held by one player in a game. You’ll want to be able to deal cards to a hand, find and remove pairs of cards in a hand, add cards to a hand, or remove cards from a hand.

The third class, Deck, represents a deck of cards. The methods and properties of this class mimic the ways that you use a deck of cards in a game. You might want to start with the standard 52-card deck or with a deck limited to certain suits or values. For most card games, you will shuffle the deck. You might want to remove one card or add an extra card. In most games, you’ll also want to deal the cards to players. The classes are shown in the following table.

Class

 

Properties

 

Methods

 

 

 

 

 

 

Card

 

Suit

 

Constructor

 

 

 

 

 

 

 

 

 

Value

 

 

 

 

 

 

 

 

Hand

 

Count

 

Constructor

 

 

 

 

 

 

 

 

 

 

 

Indexer

 

 

 

 

 

 

 

 

 

 

 

Add

 

 

 

 

 

 

 

 

 

 

 

FindAndRemovePairs

 

 

 

 

 

 

 

 

 

 

 

Contains

 

 

 

 

 

 

 

 

 

 

 

Remove

 

 

 

 

 

 

Deck

 

Count

 

Constructor

 

 

 

 

 

 

 

 

 

 

 

Shuffle

 

 

 

 

 

 

 

 

 

 

 

Deal

 

 

 

 

 

 

 

 

 

 

 

Draw

 

 

 

 

 

 

Considerations in Designing Reusable Classes

When you design reusable classes, certain concepts are important to keep in mind. Some of these are object-oriented concepts, while some are general programming concepts.

Containment You won’t know what kind of object is hosting the class. Your Deck class could be a field in a Game class, a Microsoft Windows Form class, or a custom control. Therefore, you want to be careful not to make any assumptions about the context of the Deck class. Although you’ll develop the Deck class in the context of a Windows application, the Deck class won’t contain any references to a form.

You might want to add behavior that’s dependent on the client code. For example, you might want to add code that draws the card. You could add a method that takes an argument to specify a form to draw on. However, you could also add a method that takes a .NET Framework Graphics object. This would allow you to write code to draw the card on any object that can create a Graphics object, and your object would be usable across a wider client base.

Abstraction Be clear about your abstraction; a class should do one thing well. It’s tempting to add a lot of support functions when you’re designing a reusable class. Yet

every method, property, and event that you add to the interface limits your choices in implementation, making it more difficult for the class to do one job well. A clear abstraction is easier for developers to use correctly because the properties and methods make sense and the behavior is predictable.

Interface Provide a complete interface but don’t go overboard. Implement the interface well enough so that the next developer can extend it. For example, the Hand class you implement in this chapter has a method to remove pairs. You could also add a method to remove runs of cards—for example, the Jack, Queen, and King of Hearts. However, you could instead create a new class, based on the Hand class. Because the Hand class exposes all the cards through an indexer and provides a Remove method, you can implement the method to remove the run of cards in the new extended class. Client code Well-designed classes streamline the client code. Much of the looping and decision structures are contained in the class methods rather than in the client code. The method calls are marked by an absence of arguments because the class encapsulates the information needed to execute the method.

In this chapter, you’ll create a Deck class that has a parameterless constructor to create the standard 52-card deck. The Deck class will have a deal method that takes an array of Hand instances to which to deal the cards. Dealing cards to two hands is straightforward and even reads like a problem: get a deck, shuffle it, find a couple of players, and deal the cards to the players.

‘ Visual Basic

Dim aDeck As New Deck()

aDeck.Shuffle()

hand1 = New Hand()

hand2 = New Hand()

aDeck.Deal(New Hand() {hand1, hand2})

// Visual C#

Deck aDeck = new Deck();

aDeck.Shuffle();

hand1 = new Hand();

hand2 = new Hand();

aDeck.Deal(new Hand[] {hand1, hand2});

Creating the Card Class

The first class to implement is the Card class, because the Hand and Deck classes can’t exist without the Card class. You’ll run into fewer compilation errors by implementing the Card class first.

Create the class

1.Create a new project and name it DeckOfCards.

2.On the Project menu, click Add Class. The Add New Item dialog box appears.

3.Name the file Card.vb or Card.cs, depending on the language you’re using.

The suit and value of the card will be based on enumerations.