Microsoft Visual C# 2010 Step by Step (P8) doc

50 377 1
Microsoft Visual C# 2010 Step by Step (P8) doc

Đ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

320 Part III Creating Components Note You can declare an indexer that contains only a get accessor (a read-only indexer) or only a set accessor (a write-only accessor). Comparing Indexers and Arrays When you use an indexer, the syntax is deliberately very array-like. However, there are some important differences between indexers and arrays: n Indexers can use non-numeric subscripts, such as a string as shown in the following example. Arrays can use only integer subscripts: public int this [ string name ] { } // OK Tip Many collection classes, such as Hashtable, that implement an associative lookup based on key/value pairs implement indexers to provide a convenient alternative to using the Add method to add a new value and as an alternative to iterating through the Values property to locate a value in your code. For example, instead of this: Hashtable ages = new Hashtable(); ages.Add("John", 42); you can use this: Hashtable ages = new Hashtable(); ages["John"] = 42; n Indexers can be overloaded (just like methods), whereas arrays cannot: public Name this [ PhoneNumber number ] { } public PhoneNumber this [ Name name ] { } n Indexers cannot be used as ref or out parameters, whereas array elements can: IntBits bits; // bits contains an indexer Method(ref bits[1]); // compile-time error Properties, Arrays, and Indexers It is possible for a property to return an array, but remember that arrays are reference types, so exposing an array as a property makes it possible to accidentally overwrite a lot of data. Look at the following structure that exposes an array property named Data: struct Wrapper { private int[] data; public int[] Data { Chapter 16 Using Indexers 321 get { return this.data; } set { this.data = value; } } } Now consider the following code that uses this property: Wrapper wrap = new Wrapper(); int[] myData = wrap.Data; myData[0]++; myData[1]++; This looks pretty innocuous. However, because arrays are reference types, the variable myData refers to the same object as the private data variable in the Wrapper structure. Any changes you make to elements in myData are made to the data array; the expres- sion myData[0]++ has exactly the same effect as data[0]++. If this is not the intention, you should use the Clone method in the get and set accessors of the Data property to return a copy of the data array, or make a copy of the value being set, as shown here. (The Clone method returns an object, which you must cast to an integer array.) struct Wrapper { private int[] data; public int[] Data { get { return this.data.Clone() as int[]; } set { this.data = value.Clone() as int[]; } } } However, this approach can become very messy and expensive in terms of memory use. Indexers provide a natural solution to this problem—don’t expose the entire array as a property; just make its individual elements available through an indexer: struct Wrapper { private int[] data; public int this [int i] { get { return this.data[i]; } set { this.data[i] = value; } } } The following code uses the indexer in a similar manner to the property shown earlier: Wrapper wrap = new Wrapper(); int[] myData = new int[2]; myData[0] = wrap[0]; 322 Part III Creating Components myData[1] = wrap[1]; myData[0]++; myData[1]++; This time, incrementing the values in the MyData array has no effect on the original array in the Wrapper object. If you really want to modify the data in the Wrapper object, you must write statements such as this: wrap[0]++; This is much clearer, and safer! Indexers in Interfaces You can declare indexers in an interface. To do this, specify the get keyword, the set keyword, or both, but replace the body of the get or set accessor with a semicolon. Any class or struc- ture that implements the interface must implement the indexer accessors declared in the interface. For example: interface IRawInt { bool this [ int index ] { get; set; } } struct RawInt : IRawInt { public bool this [ int index ] { get { } set { } } } If you implement the interface indexer in a class, you can declare the indexer implementa- tions as virtual. This allows further derived classes to override the get and set accessors. For example: class RawInt : IRawInt { public virtual bool this [ int index ] { get { } set { } } } Chapter 16 Using Indexers 323 You can also choose to implement an indexer by using the explicit interface implementation syntax covered in Chapter 12, “Working with Inheritance.” An explicit implementation of an indexer is nonpublic and nonvirtual (and so cannot be overridden). For example: struct RawInt : IRawInt { bool IRawInt.this [ int index ] { get { } set { } } } Using Indexers in a Windows Application In the following exercise, you will examine a simple phone book application and complete its implementation. You will write two indexers in the PhoneBook class: one that accepts a Name parameter and returns a PhoneNumber and another that accepts a PhoneNumber parameter and returns a Name. (The Name and PhoneNumber structures have already been written.) You will also need to call these indexers from the correct places in the program. Familiarize yourself with the application 1. Start Microsoft Visual Studio 2010 if it is not already running. 2. Open the Indexers project, located in the \Microsoft Press\Visual CSharp Step By Step\ Chapter 16\Indexers folder in your Documents folder. This is a Windows Presentation Foundation (WPF) application that enables a user to search for the telephone number for a contact and also find the name of a contact that matches a given telephone number. 3. On the Debug menu, click Start Without Debugging. The project builds and runs. A form appears, displaying two empty text boxes labeled Name and Phone Number. The form also contains three buttons—one to add a name/ phone number pair to a list of names and phone numbers held by the application, one to find a phone number when given a name, and one to find a name when given a phone number. These buttons currently do nothing. Your task is to complete the application so that these buttons work. 4. Close the form, and return to Visual Studio 2010. 5. Display the Name.cs file in the Code and Text Editor window. Examine the Name structure. Its purpose is to act as a holder for names. 324 Part III Creating Components The name is provided as a string to the constructor. The name can be retrieved by using the read-only string property named Text. (The Equals and GetHashCode methods are used for comparing Names when searching through an array of Name values—you can ignore them for now.) 6. Display the PhoneNumber.cs file in the Code and Text Editor window, and examine the PhoneNumber structure. It is similar to the Name structure. 7. Display the PhoneBook.cs file in the Code and Text Editor window, and examine the PhoneBook class. This class contains two private arrays: an array of Name values named names, and an array of PhoneNumber values named phoneNumbers. The PhoneBook class also con- tains an Add method that adds a phone number and name to the phone book. This method is called when the user clicks the Add button on the form. The enlargeIfFull method is called by Add to check whether the arrays are full when the user adds another entry. This method creates two new bigger arrays, copies the contents of the existing arrays to them, and then discards the old arrays. Write the indexers 1. In the PhoneBook.cs file, add a public read-only indexer to the PhoneBook class, as shown in bold in the following code. The indexer should return a Name and take a PhoneNumber item as its index. Leave the body of the get accessor blank. The indexer should look like this: sealed class PhoneBook { public Name this [PhoneNumber number] { get { } } } 2. Implement the get accessor as shown in bold in the following code. The purpose of the accessor is to find the name that matches the specified phone number. To do this, you need to call the static IndexOf method of the Array class. The IndexOf method performs a search through an array, returning the index of the first item in the array that matches the specified value. The first argument to IndexOf is the array to search through (phoneNumbers). The second argument to IndexOf is the item you are search- ing for. IndexOf returns the integer index of the element if it finds it; otherwise, IndexOf returns –1. If the indexer finds the phone number, it should return it; otherwise, it should return an empty Name value. (Note that Name is a structure and so the default constructor sets its private name field to null.) Chapter 16 Using Indexers 325 sealed class PhoneBook { public Name this [PhoneNumber number] { get { int i = Array.IndexOf(this.phoneNumbers, number); if (i != -1) { return this.names[i]; } else { return new Name(); } } } } 3. Add a second public read-only indexer to the PhoneBook class that returns a PhoneNumber and accepts a single Name parameter. Implement this indexer in the same way as the first one. (Again note that PhoneNumber is a structure and therefore always has a default constructor.) The second indexer should look like this: sealed class PhoneBook { public PhoneNumber this [Name name] { get { int i = Array.IndexOf(this.names, name); if (i != -1) { return this.phoneNumbers[i]; } else { return new PhoneNumber(); } } } } Notice that these overloaded indexers can coexist because they return different types, which means that their signatures are different. If the Name and PhoneNumber struc- tures were replaced by simple strings (which they wrap), the overloads would have the same signature and the class would not compile. 4. On the Build menu, click Build Solution. Correct any syntax errors, and then rebuild if necessary. 326 Part III Creating Components Call the indexers 1. Display the MainWindow.xaml.cs file in the Code and Text Editor window, and then locate the findPhoneClick method. This method is called when the Search by Name button is clicked. This method is cur- rently empty. Add the code shown in bold in the following example to perform these tasks: 1.1. Read the value of the Text property from the name text box on the form. This is a string containing the contact name that the user has typed in. 1.2. If the string is not empty, search for the phone number corresponding to that name in the PhoneBook by using the indexer. (Notice that the MainWindow class contains a private PhoneBook field named phoneBook.) Construct a Name object from the string, and pass it as the parameter to the PhoneBook indexer. 1.3. Write the Text property of the PhoneNumber structure returned by the indexer to the phoneNumber text box on the form. The findPhoneClick method should look like this: private void findPhoneClick(object sender, RoutedEventArgs e) { string text = name.Text; if (!String.IsNullOrEmpty(text)) { Name personsName = new Name(text); PhoneNumber personsPhoneNumber = this.phoneBook[personsName]; phoneNumber.Text = personsPhoneNumber.Text; } } Tip Notice the use of the static String method IsNullOrEmpty to determine whether a string is empty or contains a null value. This is the preferred method for testing whether a string contains a value. It returns true if the string has a non-null value and false otherwise. 2. Locate the findNameClick method in the MainWindow.xaml.cs file. It is below the findPhoneClick method. The findName_Click method is called when the Search by Phone button is clicked. This method is currently empty, so you need to implement it as follows. (The code is shown in bold in the following example.) 2.1. Read the value of the Text property from the phoneNumber text box on the form. This is a string containing the phone number that the user has typed. 2.2. If the string is not empty, search for the name corresponding to that phone number in the PhoneBook by using the indexer. 2.3. Write the Text property of the Name structure returned by the indexer to the name text box on the form. Chapter 16 Using Indexers 327 The completed method should look like this: private void findNameClick(object sender, RoutedEventArgs e) { string text = phoneNumber.Text; if (!String.IsNullOrEmpty(text)) { PhoneNumber personsPhoneNumber = new PhoneNumber(text); Name personsName = this.phoneBook[personsPhoneNumber]; name.Text = personsName.Text; } } 3. On the Build menu, click Build Solution. Correct any errors that occur. Run the application 1. On the Debug menu, click Start Without Debugging. 2. Type your name and phone number in the text boxes, and then click Add. When you click the Add button, the Add method stores the information in the phone book and clears the text boxes so that they are ready to perform a search. 3. Repeat step 2 several times with some different names and phone numbers so that the phone book contains a selection of entries. Note that the application performs no checking of the names and telephone numbers that you enter, and you can input the same name and telephone number more than once. To avoid confusion, please make sure that you provide different names and telephone numbers. 4. Type a name that you used in step 2 into the Name text box, and then click Search by Name. The phone number you added for this contact in step 2 is retrieved from the phone book and is displayed in the Phone Number text box. 5. Type a phone number for a different contact in the Phone Number text box, and then click Search by Phone. The contact name is retrieved from the phone book and is displayed in the Name text box. 6. Type a name that you did not enter in the phone book into the Name text box, and then click Search by Name. This time the Phone Number text box is empty, indicating that the name could not be found in the phone book. 7. Close the form, and return to Visual Studio 2010. In this chapter, you have seen how to use indexers to provide array-like access to data in a class. You have learned how to create indexers that can take an index and return the corre- sponding value by using logic defined by the get accessor, and you have seen how to use the set accessor with an index to populate a value in an indexer. 328 Part III Creating Components n If you want to continue to the next chapter Keep Visual Studio 2010 running, and turn to Chapter 17. n If you want to exit Visual Studio 2010 now On the File menu, click Exit. If you see a Save dialog box, click Yes and save the project. Chapter 16 Quick Reference To Do this Create an indexer for a class or structure Declare the type of the indexer, followed by the keyword this and then the indexer arguments in square brackets. The body of the indexer can contain a get and/or set accessor. For example: struct RawInt { public bool this [ int index ] { get { } set { } } } Define an indexer in an interface Define an indexer with the get and/or set keywords. For example: interface IRawInt { bool this [ int index ] { get; set; } } Implement an interface indexer in a class or structure In the class or structure that implements the interface, define the indexer and implement the accessors. For example: struct RawInt : IRawInt { public bool this [ int index ] { get { } set { } } } Implement an indexer defined by an interface by using explicit interface implementation in a class or structure In the class or structure that implements the interface, specify the interface, but do not specify the indexer accessibility. For example: struct RawInt : IRawInt { bool IRawInt.this [ int index ] { get { } set { } } } 329 Chapter 17 Interrupting Program Flow and Handling Events After completing this chapter, you will be able to: n Declare a delegate type to create an abstraction of a method signature. n Create an instance of a delegate to refer to a specific method. n Call a method through a delegate. n Define a lambda expression to specify the code for a delegate. n Declare an event field. n Handle an event by using a delegate. n Raise an event. Much of the code you have written in the various exercises in this book has assumed that statements execute sequentially. Although this is a common scenario, you will find that it is sometimes necessary to interrupt the current flow of execution and perform another, more important, task. When the task has completed, the program can continue where it left off. The classic example of this style of program is the Microsoft Windows Presentation Foundation (WPF) form. A WPF form displays controls such as buttons and text boxes. When you click a button or type text in a text box, you expect the form to respond immediately. The application has to temporarily stop what it is doing and handle your input. This style of operation applies not just to graphical user interfaces but to any application where an opera- tion must be performed urgently—shutting down the reactor in a nuclear power plant if it is getting too hot, for example. To handle this type of application, the runtime has to provide two things: a means of indicating that something urgent has happened and a way of specifying the code that should be run when it happens. This is the purpose of events and delegates. We start by looking at delegates. Declaring and Using Delegates A delegate is a pointer to a method. You can call a method through a delegate by specifying the name of the delegate. When you invoke a delegate, the runtime actually executes the method to which the delegate refers. You can dynamically change the method that a dele- gate references so that code that calls a delegate might actually run a different method each time it executes. The best way to understand delegates is to see them in action, so let’s work through an example. [...]... updated You will add functionality to the application that starts and stops the clocks by using delegates Complete the World Clock application 1 Start Microsoft Visual Studio 2010 if it is not already running 2 Open the Clock project located in the \Microsoft Press \Visual CSharp Step By Step\ Chapter 17\Clock folder in your Documents folder 3 On the Debug menu, click Start Without Debugging The project... You saw how to define anonymous methods and lambda expressions that can be run by using a delegate Finally, you learned how to define and use events to trigger e ­ xecution of a method n If you want to continue to the next chapter Keep Visual Studio 2010 running, and turn to Chapter 18 n If you want to exit Visual Studio 2010 now On the File menu, click Exit If you see a Save dialog box, click Yes and... keyword delegate, followed by the return type, followed by the name of the delegate type, followed by any parameter types For example: delegate void myDelegate(); Create an instance of a d ­ elegate initialized with a single specific method Use the same syntax you use for a class or structure: write the keyword new, followed by the name of the type (the name of the delegate), followed by the argument between... System.Windows.RoutedEventHandler(this.okayClick); } } This code is usually hidden from you When you use the Design View window in Visual Studio 2010 and set the Click property of the okay button to okayClick in the Extensible Application Markup Language (XAML) description of the form, Visual Studio 2010 generates this code for you All you have to do is write your application logic in the event handling 346 Part III ... occurs, this method runs The parameters to this method are required by the definition of the delegate used by the Tick event, but they are not used in this example so you can ignore them This method retrieves the current date and time by using the static Now property of the DateTime class It then converts the time retrieved to the local time by using the TimeZoneInfo.ConvertTime method The hours, minutes,... error at compile time rather than at run time if you try to dequeue an item from circleQueue into a Clock object, for example If you examine the description of the generic Queue class in the Microsoft Visual Studio 2010 documentation, you will notice that it is defined as follows: public class Queue : The T identifies the type parameter and acts as a placeholder for a real type at compile time When... lambda expression takes parameters, you specify them in the parentheses to the left of the => operator You can omit the types of parameters, and the C# compiler will infer their types from the context of the lambda expression You can pass parameters by reference (by using the ref keyword) if you want the lambda expression to be able to change their values other than locally, but this is not recommended n... API stands for application programming interface It is a method, or set of methods, exposed by a piece of software that you can use to control that software You can think of the Microsoft NET Framework as a set of APIs because it provides methods that you can use to control the NET common language runtime and the Microsoft Windows operating system Each machine has its own unique computer-controlled process... production of the articles manufactured by the factory—shaping and folding metal sheets, welding sheets together, painting sheets, and so on Each machine was built and installed by a specialist vendor The machines are all computer controlled, and each vendor has provided a set of APIs that you can use to control its machine Your task is to integrate the different systems used by the machines into a single control... to the RefreshTime method Note  It is not actually necessary to convert from the value returned by DateTime.Now to local time because the value of DateTime.Now is expressed as local time by default However, this is good practice, and you can convert the value of DateTime.Now to the time in any time zone by using this technique—you simply specify the target time zone as the second parameter to the TimeZoneInfo.ConvertTime . Start Microsoft Visual Studio 2010 if it is not already running. 2. Open the Indexers project, located in the Microsoft Press Visual CSharp Step By Step Chapter 16Indexers folder in your Documents. clocks by using delegates. Complete the World Clock application 1. Start Microsoft Visual Studio 2010 if it is not already running. 2. Open the Clock project located in the Microsoft Press Visual. Components n If you want to continue to the next chapter Keep Visual Studio 2010 running, and turn to Chapter 17. n If you want to exit Visual Studio 2010 now On the File menu, click Exit. If you see a

Ngày đăng: 05/07/2014, 16:20

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan