Beginning Microsoft Visual C# 2008 PHẦN 4 pot

135 345 0
Beginning Microsoft Visual C# 2008 PHẦN 4 pot

Đ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

Chapter 12: Generics 369 4. What is wrong with the following code? Fix it. public class StringGetter < T > { public string GetString < T > (T item) { return item.ToString(); } } 5. Create a generic class called ShortCollection < T > that implements IList < T > and consists of a collection of items with a maximum size. This maximum size should be an integer that can be supplied to the constructor of ShortCollection < T > or defaults to 10. The constructor should also be able to take an initial list of items via a List < T > parameter. The class should function exactly like Collection < T > but throw an exception of type IndexOutOfRangeException if an attempt is made to add too many items to the collection, or if the List < T > passed to the constructor contains too many items. c12.indd 369c12.indd 369 3/24/08 4:23:58 PM3/24/08 4:23:58 PM c12.indd 370c12.indd 370 3/24/08 4:23:59 PM3/24/08 4:23:59 PM 13 Additional OOP Techniques In this chapter, you continue exploring the C# language by looking at a few bits and pieces that haven ’ t quite fit in elsewhere. This isn ’ t to say that these techniques aren ’ t useful — just that they don ’ t fall under any of the headings you ’ ve worked through so far. Specifically, you will look at the following: The :: operator and the global namespace qualifier Custom exceptions and exception recommendations Events Anonymous methods You also make some final modifications to the CardLib code that you ’ ve been building in the last few chapters, and even use CardLib to create a card game. The :: Operator and the Global Namespace Qualifier The :: operator provides an alternative way to access types in namespaces. This may be necessary if you want to use a namespace alias and there is ambiguity between the alias and the actual namespace hierarchy. If that ’ s the case, then the namespace hierarchy is given priority over the namespace alias. To see what this means, consider the following code: using MyNamespaceAlias = MyRootNamespace.MyNestedNamespace; namespace MyRootNamespace { namespace MyNamespaceAlias { ❑ ❑ ❑ ❑ c13.indd 371c13.indd 371 3/24/08 3:46:46 PM3/24/08 3:46:46 PM 372 Part I: The C# Language public class MyClass { } } namespace MyNestedNamespace { public class MyClass { } } } Code in MyRootNamespace might use the following to refer to a class: MyNamespaceAlias.MyClass The class referred to by this code is the MyRootNamespace.MyNamespaceAlias.MyClass class, not the MyRootNamespace.MyNestedNamespace.MyClass class. That is, the namespace MyRootNamespace.MyNamespaceAlias has hidden the alias defined by the using statement, which refers to MyRootNamespace.MyNestedNamespace . You can still access the MyRootNamespace .MyNestedNamespace namespace and the class contained within, but you require different syntax: MyNestedNamespace.MyClass Alternatively, you can use the :: operator: MyNamespaceAlias::MyClass Using this operator forces the compiler to use the alias defined by the using statement, and therefore the code refers to MyRootNamespace.MyNestedNamespace.MyClass . You can also use the keyword global with the :: operator, which is essentially an alias to the top - level, root namespace. This can be useful to make it clearer which namespace you are referring to, as shown here: global::System.Collections.Generic.List < int > This is the class you’d expect it to be, the generic List < T > collection class. It definitely isn ’ t the class defined with the following code: namespace MyRootNamespace { namespace System { namespace Collections { c13.indd 372c13.indd 372 3/24/08 3:46:47 PM3/24/08 3:46:47 PM 373 Chapter 13: Additional OOP Techniques namespace Generic { class List < T > { } } } } } Of course, you should avoid giving your namespaces names that already exist as .NET namespaces, although this problem may arise in large projects, particularly if you are working as part of a large team. Using the :: operator and the global keyword may be the only way you can access the types you want. Custom Exceptions Chapter 7 covered exceptions and explained how you can use try catch finally blocks to act on them. You also saw several standard .NET exceptions, including the base class for exceptions, System.Exception . Sometimes it ’ s useful to derive your own exception classes from this base class for use in your applications, instead of using the standard exceptions. This enables you to be more specific with the information you send to whatever code catches the exception, and it enables catching code to be more specific about which exceptions it handles. For example, you might add a new property to your exception class that permits access to some underlying information, making it possible for the exception ’ s receiver to make the required changes, or just provide more information about the exception ’ s cause. Once you have defined an exception class, you can add it to the list of exceptions recognized by VS using the Debug Exceptions dialog ’ s Add button, and then define exception - specific behavior as shown in Chapter 7 . Exception Base Classes While you can derive exceptions from System.Exception as described in the previous section, best practices dictate that you don ’ t. Instead, you should derive custom exceptions from System .ApplicationException . Two fundamental exception classes exist in the System namespace and derive from Exception : ApplicationException and SystemException . SystemException is used as the base class for exceptions that are predefined by the .NET Framework. ApplicationException is provided for developers to derive their own exception classes. If you follow this model, then you will always be able to differentiate between predefined and custom exceptions by catching exceptions that derive from one of these two base classes. Neither ApplicationException nor SystemException extend the functionality of the Exception class in any way. They exist purely so that you can differentiate between exceptions in the manner described here. c13.indd 373c13.indd 373 3/24/08 3:46:47 PM3/24/08 3:46:47 PM 374 Part I: The C# Language Adding Custom Exceptions to CardLib How to use custom exceptions is, once again, best illustrated by upgrading the CardLib project. The Deck.GetCard() method currently throws a standard .NET exception if an attempt is made to access a card with an index less than 0 or greater than 51, but you ’ ll modify that to use a custom exception. First, you need to create a new class library project called Ch13CardLib and copy the classes from Ch12CardLib as before, changing the namespace to Ch13CardLib as applicable. Next, define the exception. You do this with a new class defined in a new class file called CardOutOfRangeException .cs , which you can add to the Ch13CardLib project with Project Add Class: public class CardOutOfRangeException : ApplicationException { private Cards deckContents; public Cards DeckContents { get { return deckContents; } } public CardOutOfRangeException(Cards sourceDeckContents) : base(“There are only 52 cards in the deck.”) { deckContents = sourceDeckContents; } } An instance of the Cards class is required for the constructor of this class. It allows access to this Cards object through a DeckContents property and supplies a suitable error message to the base Exception constructor, so that it is available through the Message property of the class. Next, add code to throw this exception to Deck.cs (replacing the old standard exception): public Card GetCard(int cardNum) { if (cardNum > = 0 & & cardNum < = 51) return cards[cardNum]; else throw new CardOutOfRangeException(cards.Clone() as Cards); } The DeckContents property is initialized with a deep copy of the current contents of the Deck object, in the form of a Cards object. This means that you see what the contents were at the point where the exception was thrown, so subsequent modification to the deck contents won ’ t “ lose ” this information. c13.indd 374c13.indd 374 3/24/08 3:46:47 PM3/24/08 3:46:47 PM 375 Chapter 13: Additional OOP Techniques To test this, use the following client code (in Ch13CardClient in the downloadable code for this chapter): Deck deck1 = new Deck(); try { Card myCard = deck1.GetCard(60); } catch (CardOutOfRangeException e) { Console.WriteLine(e.Message); Console.WriteLine(e.DeckContents[0]); } Console.ReadKey(); This code results in the output shown in Figure 13 - 1 . Figure 13-1 Here, the catching code has written the exception Message property to the screen. You also displayed the first card in the Cards object obtained through DeckContents , just to prove that you can access the Cards collection through your custom exception object. Events This section covers one of the most frequently used OOP techniques in .NET: events . You start, as usual, with the basics — looking at what events actually are. After that, you ’ ll see some simple events in action and learn what you can do with them. Then you learn how you can create and use events of your own. At the end of this chapter you ’ ll complete your CardLib class library by adding an event. Finally, because this is the last port of call before arriving at some advanced topics, you ’ ll have a bit of fun creating a card game application that uses this class library. What Is an Event? Events are similar to exceptions in that they are raised (thrown) by objects, and you can supply code that acts on them. However, there are several important differences, the most important of which is that there is no equivalent to the try catch structure for handling events. Instead, you must subscribe to them. Subscribing to an event means supplying code that will be executed when an event is raised, in the form of an event handler . c13.indd 375c13.indd 375 3/24/08 3:46:48 PM3/24/08 3:46:48 PM 376 Part I: The C# Language Many handlers can be subscribed to an event, all of which are called when the event is raised. This may include event handlers that are part of the class of the object that raises the event, but event handlers are just as likely to be found in other classes. Event handlers themselves are simply functions. The only restriction on an event handler function is that it must match the return type and parameters required by the event. This restriction is part of the definition of an event and is specified by a delegate . The fact that delegates are used in events is what makes delegates so useful. This is why some space was devoted to them in Chapter 6 . You may want to review that material to refresh your memory about delegates and how you use them. The basic sequence of processing is as follows: First, an application creates an object that can raise an event. For example, suppose an instant messaging application creates an object that represents a connection to a remote user. That connection object might raise an event when a message arrives through the connection from the remote user (see Figure 13 - 2 ). Application Connection Creates Figure 13-2 Next, the application subscribes to the event. Your instant messaging application would do this by defining a function that could be used with the delegate type specified by the event, passing a reference to this function to the event. The event handler function might be a method on another object, such as an object representing a display device to display instant messages when they arrive (see Figure 13 - 3 ). Application Display Connection Subscribes to Creates Figure 13-3 When the event is raised, the subscriber is notified. When an instant message arrives through the connection object, the event handler method on the display device object is called. Because you are using a standard method, the object that raises the event may pass any relevant information via parameters, making events very versatile. In the example case, one parameter might be the text of the instant message, which the event handler could display on the display device object. This is shown in Figure 13 - 4 . c13.indd 376c13.indd 376 3/24/08 3:46:48 PM3/24/08 3:46:48 PM 377 Chapter 13: Additional OOP Techniques Application Display Connection Calls Raises Event Hi Mum Figure 13-4 Handling Events As previously discussed, to handle an event you need to subscribe to it by providing an event handler function whose return type and parameters match that of the delegate specified for use with the event. The following example uses a simple timer object to raise events, which results in a handler function being called. Try It Out Handling Events 1. Create a new console application called Ch13Ex01 and save it in the directory C:\BegVCSharp\Chapter13. 2. Modify the code in Program.cs as follows: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Timers; namespace Ch13Ex01 { class Program { static int counter = 0; static string displayString = “This string will appear one letter at a time. “; static void Main(string[] args) c13.indd 377c13.indd 377 3/24/08 3:46:49 PM3/24/08 3:46:49 PM 378 Part I: The C# Language { Timer myTimer = new Timer(100); myTimer.Elapsed += new ElapsedEventHandler(WriteChar); myTimer.Start(); Console.ReadKey(); } static void WriteChar(object source, ElapsedEventArgs e) { Console.Write(displayString[counter++ % displayString.Length]); } } } 3. Run the application (once it is running, pressing a key will terminate the application). The result, after a short period, is shown in Figure 13 - 5 . Figure 13-5 How It Works The object you are using to raise events is an instance of the System.Timers.Timer class. This object is initialized with a time period (in milliseconds). When the Timer object is started using its Start() method, a stream of events is raised, spaced out in time according to the specified time period. Main() initializes a Timer object with a timer period of 100 milliseconds, so it will raise events 10 times a second when started: static void Main(string[] args) { Timer myTimer = new Timer(100); The Timer object possesses an event called Elapsed , and the event handler required by this event must match the return type and parameters of the System.Timers.ElapsedEventHandler delegate type, which is one of the standard delegates defined in the .NET Framework. This delegate specifies the following return type and parameters: void functionName(object source, ElapsedEventArgs e); The Timer object sends a reference to itself in the first parameter and an instance of an ElapsedEventArgs object in its second parameter. It is safe to ignore these parameters for now; you ’ ll take a look at them a little later. c13.indd 378c13.indd 378 3/24/08 3:46:49 PM3/24/08 3:46:49 PM [...]... application You should receive the build errors shown in Figure 14- 1 Figure 14- 1 8 Add the following code to Farm.cs: namespace Ch14Ex01 { public class Farm : IEnumerable where T : Animal { public void Add(T animal) { animals.Add(animal); } 9 Run the application The result is shown in Figure 14- 2 Figure 14- 2 40 4 c 14. indd 40 4 3/ 24/ 08 3 :48 :10 PM ... 4H, 5H, 6H) or several cards of the same rank (such as 2H, 2D, 2S) 398 c13.indd 398 3/ 24/ 08 3 :46 :56 PM 14 C# 3.0 Language Enhancements The C# language is not static Anders Hejlsberg (the inventor of C#) and others at Microsoft continue to update and refine the language At the time of this writing, the most recent changes are part of version 3.0 of the C# language, which is released as part of the Visual. .. Modify the namespace declaration in the file you have added as follows: namespace Ch14Ex01 5 Add a default constructor to the Cow, Chicken, and SuperCow classes For example, for Cow add the following code: namespace Ch14Ex01 { public class Cow : Animal { public Cow() { } 40 3 c 14. indd 40 3 3/ 24/ 08 3 :48 :10 PM Part I: The C# Language 6 Modify the code in Program.cs as follows: static void Main(string[] args)... default, parameterless constructor that is supplied by the C# compiler if you don’t include a constructor in your class definition To simplify this initialization, you can supply an appropriate nondefault constructor: public class Curry { public Curry(string mainIngredient, string style, int spiciness) { 40 0 c 14. indd 40 0 3/ 24/ 08 3 :48 :09 PM Chapter 14: C# 3.0 Language Enhancements MainIngredient = mainIngredient;... the instantiation and initialization of an array Collection initializers simply extend this syntax to collections: List myIntCollection = new List {5, 9, 10, 2, 99}; 40 2 c 14. indd 40 2 3/ 24/ 08 3 :48 :10 PM Chapter 14: C# 3.0 Language Enhancements By combining object and collection initializers, it is possible to configure collections with simple and elegant code Rather than code like this: List... earlier, which instantiates and initializes an object of type Curry, as follows: Curry tastyCurry = new Curry { MainIngredient = “panir tikka”, Style = “jalfrezi”, Spiciness = 8 }; 40 1 c 14. indd 40 1 3/ 24/ 08 3 :48 :09 PM Part I: The C# Language Often you can put code like that on a single line without seriously degrading readability When you use an object initializer, you cannot explicitly call a constructor... that the changes affect the compilation of C# code, rather than do something completely new That’s a common aspect of C# language improvements, reinforcing the point that these improvements do not reflect enormous changes In this chapter you look at the following: ❑ ❑ Type inference ❑ c 14. indd 399 Initializers Anonymous types 3/ 24/ 08 3 :48 :08 PM Part I: The C# Language ❑ Extension methods ❑ Lambda expressions... Create a new console application called Ch14Ex01 and save it in the directory C:\BegVCSharp\Chapter 14 2 Right-click on the project name in the Solution Explorer window, and select the Add Existing Item option 3 Select the Animal.cs, Cow.cs, Chicken.cs, SuperCow.cs, and Farm.cs files from the C:\BegVCSharp\Chapter12\Ch12Ex 04\ Ch12Ex 04 directory, and click Add 4 Modify the namespace declaration in the... table Console.WriteLine(“{0}’s turn.”, players[currentPlayer].Name); Console.WriteLine(“Current hand:”); foreach (Card card in players[currentPlayer].PlayHand) { Console.WriteLine(card); } 3 94 c13.indd 3 94 3/ 24/ 08 3 :46 : 54 PM Chapter 13: Additional OOP Techniques Console.WriteLine(“Card in play: {0}”, playCard); // Prompt player to pick up card on table or draw a new one bool inputOK = false; do { Console.WriteLine(“Press... { public class MessageArrivedEventArgs : EventArgs 3 84 c13.indd 3 84 3/ 24/ 08 3 :46 :51 PM Chapter 13: Additional OOP Techniques { private string message; public string Message { get { return message; } } public MessageArrivedEventArgs() { message = “No message sent.”; } public MessageArrivedEventArgs(string newMessage) { message = newMessage; } } } 4 Modify Connection.cs as follows: namespace Ch13Ex03 . 369c12.indd 369 3/ 24/ 08 4: 23:58 PM3/ 24/ 08 4: 23:58 PM c12.indd 370c12.indd 370 3/ 24/ 08 4: 23:59 PM3/ 24/ 08 4: 23:59 PM 13 Additional OOP Techniques In this chapter, you continue exploring the C# language. exceptions in the manner described here. c13.indd 373c13.indd 373 3/ 24/ 08 3 :46 :47 PM3/ 24/ 08 3 :46 :47 PM 3 74 Part I: The C# Language Adding Custom Exceptions to CardLib How to use custom exceptions. modification to the deck contents won ’ t “ lose ” this information. c13.indd 374c13.indd 3 74 3/ 24/ 08 3 :46 :47 PM3/ 24/ 08 3 :46 :47 PM 375 Chapter 13: Additional OOP Techniques To test this, use the following

Ngày đăng: 09/08/2014, 14:21

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

  • Đang cập nhật ...

Tài liệu liên quan