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

CSharp Bible (2002) [eng]

.pdf
Скачиваний:
46
Добавлен:
16.08.2013
Размер:
4.29 Mб
Скачать

public override void MakeNoise()

{

Console.WriteLine("Meow!");

}

}

class Dog : Animal

{

public override void MakeNoise()

{

Console.WriteLine("Woof!");

}

}

Notice how each class has its own implementation of the MakeNoise() method. The stage is now set for polymorphism. As shown in Figure 8-4, you have a base class with a method that is overridden in two (or more) derived classes. Polymorphism is the capability of an objectoriented language to correctly call the overridden method based on which derived class is issuing the method call. This usually happens when a collection of derived objects is stored.

Figure 8-4: This example of inheritance shows one base class and two derived classes.

The following code snippet creates a collection of animals, appropriately called a zoo. It then adds a dog and two cats to this zoo.

ArrayList Zoo;

Zoo = new ArrayList(3);

Cat Sasha, Koshka;

Sasha = new Cat();

Koshka = new Cat();

Dog Milou;

Milou = new Dog();

The zoo collection is a polymorphic collection, because all of its classes are derived from the abstract Animal class. You can now iterate through the collection and have each animal make the proper noise:

foreach (Animal a in Zoo)

{

a.MakeNoise();

}

Running the preceding code will result in the following output:

Woof!

Meow!

Meow!

What is going on here? Because C# supports polymorphism, at runtime the program is smart enough to call the dog version of MakeNoise when a dog is retrieved from the zoo, and the cat version of MakeNoise when a cat is retrieved from the zoo.

Listing 8-1 shows the full C# code illustrating polymorphism.

Listing 8-1: The Cat and Dog Classes Demonstrate Polymorphism

using System;

using System.Collections;

namespace PolyMorphism

{

abstract class Animal

{

public abstract void MakeNoise();

}

class Cat : Animal

{

public override void MakeNoise()

{

Console.WriteLine("Meow!");

}

}

class Dog : Animal

{

public override void MakeNoise()

{

Console.WriteLine("Woof!");

}

}

class PolyMorphism

{

static int Main(string[] args)

{

ArrayList Zoo;

Zoo = new ArrayList(3);

Cat Sasha, Koshka;

Sasha = new Cat();

Koshka = new Cat();

Dog Milou;

Milou = new Dog();

Zoo.Add(Milou);

Zoo.Add(Koshka);

Zoo.Add(Sasha);

foreach (Animal a in Zoo)

{

a.MakeNoise();

}

// wait for user to acknowledge the results Console.WriteLine("Hit Enter to terminate..."); Console.Read();

return 0;

}

}

}

Summary

C# is an object-oriented language, and the concepts used in object-oriented languages apply to C#.

Abstract data typesare data types that you define in your own code. Abstract data types are not built into the C# language, unlike primitive data types. You can use abstract types in your code just as you use primitive data types, but only after you define the abstract data type in your code.

Encapsulation is the act of designing classes that provide a full set of functionality to your class's data without exposing the data directly. Oftentimes, you will want to write code that protects your class from illegal values supplied by other pieces of code. Encapsulation enables you to "hide" your data members by making them private, while making methods that get and set the data member's values public. Other pieces of code can call the methods, which can check the values and issue an error if the values are not appropriate.

Inheritance enables you to define one class in terms of another. Derived classes inherit from the base class. Derived classes automatically inherit the state and the behaviors of the base class. You can use inheritance to add new functionality to existing classes without rewriting a new class completely from scratch. Some object-oriented languages support both single inheritance and multiple inheritance, although C# allows only single inheritance.

Polymorphism enables you to treat a collection of classes derived from a single base class in a uniform way. You retrieve the derived classes as a base class, and C# automatically calls the correct method in the derived class.

This chapter discusses object-oriented concepts in a general sense, without examining how the concepts are used in C# code. The rest of the chapters in this part of the book illustrate how these concepts are implemented in C#.

Chapter 9: C# Classes

In This Chapter

The design of C# is heavily influenced by the concepts of object-oriented software development. Because the class is the foundation of object-oriented software, it should come as no surprise that classes are the single most important idea in C#.

Classes in the object-oriented software development world contain code and data. In C#, data is implemented using data members, and code is implemented using methods. Data members are any item for which you can pass data in and out of a class. The two main types of data members are fields and properties. You can write as many data members and methods as you'd like in your C# code, but all of them must be enclosed in one or more classes. The C# compiler issues an error if you try to define any variables or implement any method outside of a class definition.

This chapter discusses the basics of building classes. You learn how to build constructors and destructors, add methods and members as well as using classes after they have been created.

Declaring a Class

Declaring a class is much like declaring a structure. The main difference is that the declaration begins with the C# keyword class, rather than struct. You've already seen a class definition in previous chapters, but let's review the layout of a class declaration:

Optional class modifiers

The keyword class

An identifier used to name the class

Optional base class information

The class body

An optional semicolon

The minimal class declaration in C# looks like the following:

class MyClass

{

}

The curly brackets surround the body of the class. Class variables and methods are placed within the curly brackets.

Understanding the Main Method

Every C# application must contain a method called Main. This is the entry point for the application to run. You can place this method within any class in your project because the compiler is smart enough to look for it when the time arrives.

The Main method has two special requirements that must be met before an application can work correctly. First, the method must be declared as public. This ensures that the method is accessible. Second, the method must be declared as static. The static keyword ensures that only one copy of the method can be loaded at any given time.

With those rules in mind, look at the following code:

class Class1

{

public static void Main()

{

}

}

As you see, this example has one class called Class1. This class contains the Main method into the application. This Main method is where you place all the code to run your application. Although it is OK to put this method in the same class and file as the rest of the code in your application, it's generally a good idea to create a separate class and file for the Main method. This helps other developers who may need to deal with your code.

Using command-line arguments

Many applications built for the Windows platform accept command-line parameters. To accept command-line parameters within your C# application, you must declare a string array as the only parameter to the Main method, as shown in the following code:

class Class1

{

public static void Main(string[] args)

{

foreach (string arg in args) System.Console.WriteLine(arg);

}

}

Here, a typical class contains the Main method. Note that the Main method has one parameter defined, a string array that is to be stored in the args variable. It uses the foreach command to iterate through all the strings that are stored within the args array, and then you write those strings out to the console.

Note If you are using Visual Studio .NET for your C# development, the string array is automatically added for you when you create a console application.

If you were to run the preceding application with no parameters, nothing would happen. If you were to run the application like this:

Sampleap.exe parameter1 parameter2

the output from the application would look like the following:

parameter1

parameter2

Command-line arguments are an excellent way to provide switches to your application; for example, to turn on a log file while your application executes.

Returning values

When you create an application, it often proves useful to return a value to the application that launched it. This value is an indicator to the calling program or batch script that your program

succeeded or failed. You can accomplish this simply by giving your Main method a return value of int, rather than void. When your Main method is ready to end, you must use the return keyword and a value to return, as shown in the following example:

class Class1

{

public static int Main(string[] args)

{

if (args[0] == "fail") return 1;

return 0;

}

}

This application accepts a parameter of fail or any other word — perhaps success. If the word fail is passed as a parameter to this application, the program returns a value of 1, indicating a failure of the program. Otherwise, the program returns 0, indicating that the program exited normally. You could test this program using a simple batch file to run the application and then take certain actions depending on the return code. The following code is a simple batch file that performs this function for you:

@echo off retval.exe success

goto answer%errorlevel%

:answer0

echo Program had return code 0 (Success) goto end

:answer1

echo Program had return code 1 (Failure) goto end

:end

echo Done!

In the preceding code, you can see that the program is called with a parameter of success. When you run this batch program, you should see the following message:

Program had return code 0 (Success)

If you edit this batch program to pass the word fail as a parameter, you then see a message confirming that your program terminated with an exit code of 1.

Understanding the Class Body

The class body can include statements whose function falls into one of the following categories:

Constants

Fields

Methods

Properties

Events

Indexers

Operators

Constructors

Destructors

Types

Using constants

Class constants are variables whose value does not change. Using constants in your code makes the code more readable because constants can include identifiers that describe the meaning of the value to people reading your code. You can write code like the following:

if(Ratio == 3.14159) System.Console.WriteLine("Shape is a circle");

Cross-Reference Constants are covered in Chapter 4, "Expressions."

The C# compiler happily accepts this code, but it may be a bit hard to read, especially for someone reading the code for the first time and wondering what the floating-point value is. Giving a name to the constant — pi, for example — let's you write code like the following:

if(Ratio == Pi)

System.Console.WriteLine("Shape is a circle");

There are other benefits to using constants in your code:

You give constants a value when they are declared, and their name, rather than their value, is used in the code. If for some reason you need to change the value of the constant, you only need to change it in one place: where the constant is declared. If you hardcode the actual value in your C# statements, a change in value means that you need to go through all of your code and change the hardcoded value.

Constants specifically tell the C# compiler that, unlike normal variables, the value of the constant cannot change. The C# compiler issues an error against any code that attempts to change the value of a constant.

The structure of a constant statement is as follows:

The keyword const

A data type for the constant

An identifier

An equals sign

The value of the constant

Defining a constant for pi may look like the following:

class MyClass

{

public const double Pi = 3.1415926535897932384626433832795;

public static void Main()

{

}

}

Cross-Reference This chapter uses the keyword public to describe class body elements as being publicly available to the rest of the code in an application. Chapter 11, "Class Inheritance," looks at alternatives to the public keyword that you can use when you inherit one class from another.

C# enables you to place multiple constant definitions on the same line, provided that all of the constants share the same type. You can separate your class constant definitions with a comma, as follows:

class MyClass

{

public const int One = 1, Two = 2, Three = 3;

public static void Main()

{

}

}

Using fields

Fields are data members of a class. They are variables contained within a class and, in objectoriented terms, manage the class's state. Fields defined in a class can be accessed by any method defined in the same class.

You can define a field just as any variable is defined. Fields have a type and an identifier. You may also define a field without an initial value:

class MyClass

{

public int Field1;

public static void Main()

{

}

}

You can also define fields with an initial value:

class MyClass

{

public int Field1 = 123;

public static void Main()

{

}

}

To mark fields as read-only, prefix the declaration with the readonly keyword. If you use the readonly keyword, it should appear just before the field's type:

class MyClass

{

public readonly int Field1;

public static void Main()

{

}

}

The value of a read-only field may be set when the field is declared or in the constructor of the class. Its value cannot be set at any other time. Any attempt to change the value of a readonly field is caught by the C# compiler and appears as an error:

error CS0191: A readonly field cannot be assigned to (except in a constructor or a variable initializer)

Read-only fields behave much like constants in that they are both initialized when an object of the class is created. The main difference between a constant and a read-only field is that the value of constants must be set at compile time — in other words, you must give them a value when you write your code. Read-only fields enable you to determine a value for the field at runtime. Because you can set the value of a read-only field in a class constructor, you can determine its value at runtime and set it when the code is running.

Suppose, for example, that you are writing a C# class that manages a group of records read from a database. You may want to have the class publish a value that specifies the number of records maintained by the class. A constant is not the right choice for this value because the value of a constant must be set when you write the code, and you won't know how many records the class holds until the code runs. You may decide to put this data in a field whose value can be set after the class starts to execute and the number of records can be determined. Because the number of records don't change during the lifetime of your class, you may want to mark the field as read-only so that other pieces of code cannot change its value.

Using methods

Methods are named blocks of code in a class. You can learn more about methods in Chapter 6. They provide the behaviors of your class. Methods can be executed by any other piece of code in the class and, as you learn in Chapter 11, other classes can execute them as well.

Using properties

Chapter 7 discusses a Point structure that can describe a point on a screen with a resolution of 640 x 480 pixels. One way to implement this structure is to define it with public data members that describe the coordinates of the point:

struct Point

{

public int X; public int Y;

}

Using a structure like this, clients can use the structurename.field syntax to work with one of the fields in the structure:

Point MyPoint = new Point();

MyPoint.X = 100;

This code is easy for clients to write. Clients can access the field directly and use its value in an expression.

The problem with this approach is that clients may set a field to a value allowed by C#, but not logically appropriate for the code. For example, if you're managing a Point structure that is used to describe a point on a screen that is 640 x 480 pixels, the largest logical value for X should be 639 (assuming that legal values for X are between 0 and 639). However, because the X coordinate is specified in the structure as an int, clients can set the value to any legal value in the range of integers:

MyPoint.X = -500;

MyPoint.X = 9000;

This code is accepted by the C# compiler because the values assigned to X are within the legal range of integer values.

One solution to this problem is to make the values private, which makes them inaccessible to other pieces of code, and then add a public method to the structure that sets the value of X:

struct Point

{

private int X; private int Y;

public bool SetX(int NewXValue)

{

if((NewXValue < 0) || (NewXValue > 639)) return false;

X = NewXValue; return true;

}

}

The advantage of this approach is that it forces clients who want to set the value of X to call a method to get the job done:

Point MyPoint = new Point();

MyPoint.SetX(100);

The advantage of the method is that you can write code to validate the new value before it is actually stored in the field, and the code in the method can reject the new value if it is not logically appropriate. Clients would then call the method to set a new value.

While this approach works, calling a method to set a value takes a bit more typing than simply setting a value directly. It is more natural for code to assign a value to a field than to call a method to set it.

Ideally, you'd like the best of both worlds: You would like clients to be able to read and write field values directly using simple assignment statements, but you'd also like code to step in beforehand and do any work that is necessary to either get the latest value of a field or

Соседние файлы в предмете Программирование на C++