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

CSharp Bible (2002) [eng]-1

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

OR operation on the FileAttributes.AttrReadOnly and FileAttributes.Hidden attributes. The value of the local variable is then written to the console.

Compiling and executing Listing 14-2 produces the following console output:

3

Listing 14-2 outputs the value 3 because the value of the FileAttributes.AttrReadOnly enumeration, 1, was joined in an OR operation with the value of the FileAttributes.Hidden enumeration, 2. Performing a Boolean OR operation on values of 1 and 2 produces a result of 3.

You can also cast an enumerated value to a value having a type of the enumeration's underlying type:

enum IntEnum

{

EnumOne = 1, EnumTwo, EnumThree

}

IntEnum IntEnumValue; int IntValue;

IntEnumValue = EnumTwo;

IntValue = (int) IntEnumValue; // value is 2

This code converts the value in the enumeration variable IntEnumValue to its integer equivalent and assigns the integer IntValue to that value. Because the IntValue variable is a standard integer, it can be set to any legal value for an integer. It is not bound by the value set defined by the enumeration, even though it is assigned a value that came from an enumerated variable.

Using the .NET System.Enum Class

The enum type in C# is actually an alias for the System.Enum class defined in the .NET Framework. You can use any of the members of the .NET System.Enum class on the enumerations that you define.

Retrieving enumeration names

Listing 14-3 illustrates how your code can work with enumerations as System.Enum objects. It is an enhancement of Listing 14-1 that retrieves the current door state and prints the state's name to the console.

Listing 14-3: Retrieving an Enumeration Name with GetName()

using System;

public enum LegalDoorStates

{

DoorStateOpen,

DoorStateClosed

}

class DoorController

{

private LegalDoorStates CurrentState;

public LegalDoorStates State

{

get

{

return CurrentState;

}

set

{

CurrentState = value;

}

}

}

class MainClass

{

public static void Main()

{

DoorController Door; string EnumName;

Door = new DoorController();

Door.State = LegalDoorStates.DoorStateOpen;

EnumName = LegalDoorStates.GetName(typeof(LegalDoorStates), Door.State);

Console.WriteLine(EnumName);

}

}

The Main() method in Listing 14-3 uses the GetName() method of the System.Enum class to obtain a string that represents an enumeration value. The first parameter is a Type object that specifies the enumeration being queried. The expression typeof(LegalDoorStates) returns a

.NET Type object for the specified type - in this case, the LegalDoorStates enumeration. The second parameter is the actual enumeration value whose string representation should be returned. The following statement shows how the GetName() method can be used to obtain the name of an enumerated value:

EnumName = LegalDoorStates.GetName(typeof(LegalDoorStates),

Door.State);

This statement reads as follows: "Return a string that represents the name of the value in the Door.State property. This value is a part of the LegalDoorStates enumeration."

Running and executing Listing 14-3 prints the following to the console:

DoorStateOpen

You can also use the Format() method to retrieve the name of an enumeration value, given its numeric value. The GetName() call in Listing 14-3 could have been replaced with the following call to Format():

EnumName = LegalDoorStates.Format(typeof(LegalDoorStates), 0, "g");

The first parameter to Format() is the same as the first parameter to GetNames(), which is the enumeration type to be used in the call. The second parameter to Format() is the numeric value whose enumeration name is to be returned from the call. The last parameter to Format() is a string that specifies the contents of the string to be returned by the call. The format string can be one of the following:

g, which specifies that the enumeration value with the numerical value matching the value of the second parameter is to be returned

x, which specifies that the value of the second parameter to be returned as a string representing the value in hexadecimal notation

d[P1], which specifies that the value of the second parameter to be returned as a string representing the value in hexadecimal notation

f, which specifies that the value is to be treated as a set of combined enumerated values and that the method should return a comma-delimited value list as a string

The f format value is designed for use with enumerations that represent bit values. Consider the following enumeration:

public enum BitsToSet

{

Bit0Set = 1,

Bit1Set = 2,

Bit2Set = 4,

Bit3Set = 8,

Bit4Set = 16,

Bit5Set = 32,

Bit6Set = 64,

Bit7Set = 128

}

The preceding enumeration represents a set of bits that could be set in a byte. Various bits can be set in a variable using the Boolean OR operator, as in the following example:

BitsToSet Byte;

Byte = BitsToSet.Bit1Set | BitsToSet.Bit3Set | BitsToSet.Bit6Set;

Calling the Format() method on the Byte variable with the f format parameter returns a string representing the names of the enumerated values whose values are found in the variable:

Bit1Set, Bit3Set, Bit6Set

Comparing enumeration values

The CompareTo() method of the System.Enum class can compare one enumeration value to another and returns an integer describing the relationship between the two values. Take a look at Listing 14-4, which compares the value of an enumerated variable to a named value from the same enumeration:

Listing 14-4: Comparing Enumeration Values with CompareTo()

using System;

public class MainClass

{

public enum Color

{

Red = 0,

Orange,

Yellow,

Green,

Blue,

Indigo,

Violet

}

static void Main()

{

Color MyColor;

MyColor = Color.Green;

Console.WriteLine("{0}", MyColor.CompareTo(Color.Red)); Console.WriteLine("{0}", MyColor.CompareTo(Color.Green)); Console.WriteLine("{0}", MyColor.CompareTo(Color.Violet));

}

}

Listing 14-4 declares a class with a public enumeration called Color. Its values range from 0 to 6. The Main() method declares a variable of type Color called MyColor and assigns a value of Green to the variable. It then calls CompareTo() to compare the variable's value to other values in the enumeration. The CompareTo() method returns one of three values:

-1 if the value passed in as the argument to CompareTo() has a higher value than the enumerated value used to call the method

1 if the value passed in as the argument to CompareTo() has a lower value than the enumerated value used to call the method

0 if the two values are equal

In Listing 14-4, the CompareTo() method is called three times. In the first call, the MyColor variable is compared to the value of Red. Because Green, which has a value of 3, has a higher value than Red, which has a value of 0, CompareTo() returns 1. In the second call, the MyColor variable is compared to the value of Green. Because the values are equal, CompareTo() returns 0. In the final call, the MyColor variable is compared to the value of Violet. Because Green, which has a value of 3, has a lower value than Violet, which has a value of 6, CompareTo() returns -1.

The argument used in the call to CompareTo() must be of the same type as the enumeration used to call the method. Using any other type, including the underlying type of the enumeration, produces an error at runtime.

Discovering the underlying type at runtime

Discovering the underlying type of an enumeration at runtime is easy with the GetUnderlyingType() method. This method, which is called on the enumeration type, rather than a variable of the type, takes in a Type parameter representing the enumeration type and returns another Type object representing the enumeration's underlying type. The ToString() method can be called on the returned Type object to obtain a readable name for the type, as shown in the following code:

string FormatString; Type UnderlyingType;

UnderlyingType = BitsToSet.GetUnderlyingType(typeof(BitsToSet));

Console.WriteLine(UnderlyingType.ToString());

This code retrieves the underlying type for an enumeration called BitsToSet and prints the type's name out to the console, which produces a string such as the following:

System.Int32

Retrieving all enumeration values

The GetValues() method returns an array of all enumeration values sorted in ascending order by their numeric value, as shown in the following code:

Array ValueArray;

ValueArray = Color.GetValues(typeof(Color)); foreach(Color ColorItem in ValueArray)

Console.WriteLine(ColorItem.ToString());

This code calls GetValues() on the Color enumeration defined previously. The GetValues() method returns an array, and the items in the array are visited one at a time using the foreach keyword. The name of each item in the array is printed out to the console, as follows:

Red

Orange

Yellow

Green

Blue

Indigo

Violet

Parsing strings to retrieve enumeration values

The Enum class contains a string parsing method called Parse(), which accepts a string as input and returns the enumeration value whose name matches the supplied string, as shown in the following example:

Color.Parse(typeof(Color), "Blue");

This call returns an object representing the enumerated value named Blue in an enumeration called Color. Like many other enumeration methods, the Parse() method is called on the type, rather than a variable of the type. The Parse() method returns an object, which needs to be

casted to a value of the appropriate type. The following example shows how the Parse() method might be used as one of several ways to represent an enumerated value:

Color ColorValue; object ParsedObject;

ParsedObject = Color.Parse(typeof(Color), "Blue");

Console.WriteLine(ParsedObject.GetType().ToString());

ColorValue = (Color)ParsedObject;

Console.WriteLine(ColorValue.ToString());

Console.WriteLine(Color.Format(typeof(Color), ColorValue, "d"));

In this code, Parse() is called on the Color enumeration type and is given an input string of Blue. This call returns an object, and the code writes the object's type to the console. The object is then casted to a variable of type Color, and the enumeration value's name and decimal value is written to the console. This code produces the following output:

MainClass+Color Blue

4

This output shows that the object returned by the Parse() method is of type Color. The casted variable, which is a Color variable, has a string name of Blue and a decimal value of 4.

Summary

Enumerations are used to group a set of related constants. By giving your enumerations a name, you can use that name as a variable type in your code once you have defined the enumeration. Enumerations, by default, are based on a set of int constants. You may override this default by specifying an underlying type for the enumeration. You can use many of the C# numeric types as an underlying type for an enumeration. You should use enumerations when you want the C# compiler to ensure that the constants you work with in your code come from a set of legal values.

By default, the C# compiler assigns numeric values to the identifiers in enumerations. The first identifier has a value of zero, and the other enumerations increase in value from there. If you want to, you can use the assignment operator to set a value for an enumeration identifier when you define the enumeration.

You specify a value in an enumeration by writing the name of the enumeration, a period, and the name of the enumeration identifiers. Enumeration identifiers can be implicitly converted to the enumeration's underlying type. This implicit conversion also enables you to use several of the operators in C# to work with the enumeration values.

All enumerations in C# derive from a .NET base class called System.Enum. The System.Enum class contains several helpful methods that help you get the most out of your enumerations. This chapter examined most of those methods.

Chapter 15: Events and Delegates

In This Chapter

In the general flow of a typical object-oriented piece of software, a piece of code creates an object of a class and calls methods on the object. In this scenario, the caller is the active code because it is the code calling methods. The object is passive, in that it waits around and performs an action only when one of its methods is called.

However, the reverse scenario is also possible. An object can perform work and notify the caller when something happens during the process. This something is called an event, and the object's publication of that event is called raising an event.

Event-driven processing, in which pieces of code inform other pieces of code when interesting events occur, is not new to .NET. The Windows user interface layer has always used a form of events to inform Windows applications when users work with the mouse, press a key on the keyboard, or move a window. ActiveX controls raise events to ActiveX control containers when the user takes an action that affects the control.

The C# language contains special keywords that make it easy for you to fire, publish and subscribe to events in your C# code. You can use these keywords to allow your C# classes to fire and process events with a minimum of effort.

Defining Delegates

When you design the events that your C# classes raise, you need to decide how other pieces of code receive the event. Other pieces of code need to write a method that receives and processes the events you publish. Suppose, for example, that your class implements a Web server and wants to fire an event whenever a request for a page comes in from the Internet. Other pieces of code may want to perform some action when your class fires this new request event, and that code should include a method that is executed when the event is fired.

The method that the users of your class implement to receive and process your events is defined by a C# concept called a delegate. A delegate is a sort of "function stencil" that describes what your user's event handler must look like. A delegate is also a class that has a signature and holds references to methods. It's like a function pointer, but it can hold references to static and instance methods. For instance methods, the delegate stores a reference to the function's entry point, as well as to the object. A delegate defines what the user's event handler should return, and what its parameter list should be.

To define a delegate in C#, use the following syntax:

The C# keyword delegate

The event handler's return type

The delegate identifier

The event handler's parameter list, enclosed in parentheses

If you declare delegates in the class that fires the event, you can prefix them with the public, protected, internal, or private keywords as seem here in a sample delegate definition.

public delegate void EvenNumberHandler(int Number);

In this example, you create a public delegate called EvenNumberHandler that return nothing. This delegate defines only one parameter to be passed in, of type int. The delegate identifier, EvenNumberHandler, can be any name you choose as long as you don't give it the name of a C# keyword.

Defining Events

To clarify what an event actually is, start with an example. You are driving down the road in your car and the low fuel light appears on your dash. What has actually happened is that a sensor in your gas tank signaled the computer that your fuel is low. The computer then fires an event that in turn, illuminates the dash light so you know to purchase more fuel. In simplest terms, an event is a means by which a computer alerts you to a condition.

You use the C# keyword event to define an event that your class fires. In their simplest form, C# event declarations use the following syntax:

The C# keyword event

The event type

The event identifier

The event type matches a delegate identifier, as shown in the following Web server example:

public delegate void NewRequestHandler(string URL);

public class WebServer

{

public event NewRequestHandler NewRequestEvent; // ...

}

This example declares a delegate called NewRequestHandler. The NewRequestHandler defines a delegate that serves as a method template for methods processing the new request event. Any methods that need to process the new request event must follow the calling conventions of the delegate: They must not return any data and must have a single string as their parameter list. The event handler implementations can have any method name as long as their return type and parameter list match the delegate stencil.

The WebServer class defines an event called NewRequestEvent. This event has a type of NewRequestHandler. This means that only event handlers written to match the delegate's calling conventions can be used to process the NewRequestEvent event.

Installing Events

After you write your event handler, you must create a new instance of it and install it into the class that fires the event. You create a new event handler instance by creating a new variable of the delegate type, passing in the name of your event handler method as an argument. Using the Web browser example, the creation of a new event handler instance may look like the following:

public void MyNewRequestHandler(string URL)

{

}

NewRequestHandler HandlerInstance;

HandlerInstance = new NewRequestHandler(MyNewRequestHandler);

After you create the new event handler instance, use the += operator to add it to the event variable:

NewRequestEvent += HandlerInstance;

This statement hooks the HandlerInstance delegate instance, which supports the MyNewRequestMethod method, to the NewRequestEvent event. Using the += operator, you can hook as many delegate instances as you like to an event.

Similarly, you can use the -= operator to remove a delegate instance from an event:

NewRequestEvent -= HandlerInstance;

This statement unhooks the HandlerInstance delegate instance from the NewRequestEvent event.

Firing Events

You can fire an event from a class by using the event identifier (such as the name of the event) as if it were a method. Calling an event as a method fires the event. Firing the new request event in your Web browser example may look something like the following:

NewRequestEvent(strURLOfNewRequest);

The parameters used in the event firing call must match the parameter list of the event's delegate. The delegate of the NewRequestEvent event was defined as accepting a string parameter; therefore, a string must be supplied when the event is fired from the Web browser class.

Tying It All Together

Listing 15-1 shows delegates and events in action. The code implements a class that counts from 0 to 100 and fires an event when an even number is found during the counting process.

Listing 15-1: Retrieving Even-Numbered Events

using System;

public delegate void EvenNumberHandler(int Number);

class Counter

{

public event EvenNumberHandler OnEvenNumber;

public Counter()

{

OnEvenNumber = null;

}

public void CountTo100()

{

int CurrentNumber;

for(CurrentNumber = 0; CurrentNumber <= 100; CurrentNumber++)

{

if(CurrentNumber % 2 == 0)

{

if(OnEvenNumber != null)

{

OnEvenNumber(CurrentNumber);

}

}

}

}

}

class EvenNumberHandlerClass

{

public void EvenNumberFound(int EvenNumber)

{

Console.WriteLine(EvenNumber);

}

}

class MainClass

{

public static void Main()

{

Counter MyCounter = new Counter(); EvenNumberHandlerClass MyEvenNumberHandlerClass = new

EvenNumberHandlerClass(); MyCounter.OnEvenNumber += new

EvenNumberHandler(MyEvenNumberHandlerClass.EvenNumberFound);

MyCounter.CountTo100();

}

}

To compile this application, create a new console application in Visual Studio and paste the source code in or you can simple use Notepad to save the file and then use:

csc <filename>

Listing 15-1 implements three classes:

The Counter class is the class that performs the counting. It implements a public method called CountTo100() and a public event called OnEvenNumber. The OnEvenNumber event is of delegate type EvenNumberHandler.

The EvenNumberHandlerClass class contains a public method called EvenNumberFound. This method serves as the event handler for the Counter class's OnEvenNumber event. It prints out to the console the integer supplied as a parameter.

The MainClass class contains the application's Main() method.

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