1. Trang chủ
  2. » Công Nghệ Thông Tin

Pro C# 2008 and the .NET 3.5 Platform, Fourth Edition phần 4 ppt

140 299 0

Đ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

Thông tin cơ bản

Định dạng
Số trang 140
Dung lượng 4,65 MB

Nội dung

And What of the += and –+ Operators? If you are coming to C# from a C++ background, you may lament the loss of overloading the shorthand assignment operators ( +=, -=, and so forth). Fear not. In terms of C#, the shorthand assignment operators are automatically simulated if a type overloads the related binary operator. Thus, given that the Point structure has already overloaded the + and - operators, you are able to write the following: // Overloading binary operators results in a freebie shorthand operator. static void Main(string[] args) { // Freebie += Point ptThree = new Point(90, 5); Console.WriteLine("ptThree = {0}", ptThree); Console.WriteLine("ptThree += ptTwo: {0}", ptThree += ptTwo); // Freebie -= Point ptFour = new Point(0, 500); Console.WriteLine("ptFour = {0}", ptFour); Console.WriteLine("ptFour -= ptThree: {0}", ptFour -= ptThree); Console.ReadLine(); } Overloading Unary Operators C# also allows you to overload various unary operators, such as ++ and When you overload a unary operator, you will also define a static method via the operator keyword; however, in this case you will simply pass in a single parameter that is the same type as the defining class/structure. For example, if you were to update the Point with the following overloaded operators: public struct Point { // Add 1 to the incoming Point. public static Point operator ++(Point p1) { return new Point(p1.x+1, p1.y+1); } // Subtract 1 from the incoming Point. public static Point operator (Point p1) { return new Point(p1.x-1, p1.y-1); } } you could increment and decrement Point’s x and y values as follows: static void Main(string[] args) { // Applying the ++ and unary operators to a Point. Point ptFive = new Point(1, 1); Console.WriteLine("++ptFive = {0}", ++ptFive); // [2, 2] Console.WriteLine(" ptFive = {0}", ptFive); // [1, 1] // Apply same operators as postincrement/decrement. Point ptSix = new Point(20, 20); CHAPTER 12 ■ INDEXERS, OPERATORS, AND POINTERS392 8849CH12.qxd 9/26/07 11:31 AM Page 392 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Console.WriteLine("ptSix++ = {0}", ptSix++); // [20, 20] Console.WriteLine("ptSix = {0}", ptSix ); // [21, 21] Console.ReadLine(); } Notice in the previous code example we are applying our custom ++ and operators in two unique manners. In C++, it is possible to overload pre- and postincrement/decrement operators separately. This is not possible in C#; however, the return value of the increment/decrement is auto- matically handled “correctly” free of charge (i.e., for an overloaded ++ operator, pt++ has the value of the unmodified object as its value within an expression, while ++pt has the new value applied before use in the expression). Overloading Equality Operators As you may recall from Chapter 6, System.Object.Equals() can be overridden to perform value- based (rather than referenced-based) comparisons between types. If you choose to override Equals() (and the often related System.Object.GetHashCode() method), it is trivial to overload the equality operators ( == and !=). To illustrate, here is the updated Point type: // This incarnation of Point also overloads the == and != operators. public struct Point { public override bool Equals(object o) { return o.ToString() == this.ToString(); } public override int GetHashCode() { return this.ToString().GetHashCode(); } // Now let's overload the == and != operators. public static bool operator ==(Point p1, Point p2) { return p1.Equals(p2); } public static bool operator !=(Point p1, Point p2) { return !p1.Equals(p2); } } Notice how the implementation of operator == and operator != simply makes a call to the over- ridden Equals() method to get the bulk of the work done. Given this, you can now exercise your Point class as follows: // Make use of the overloaded equality operators. static void Main(string[] args) { Console.WriteLine("ptOne == ptTwo : {0}", ptOne == ptTwo); Console.WriteLine("ptOne != ptTwo : {0}", ptOne != ptTwo); Console.ReadLine(); } As you can see, it is quite intuitive to compare two objects using the well-known == and != operators rather than making a call to Object.Equals(). If you do overload the equality operators for a given class, keep in mind that C# demands that if you override the == operator, you must also override the != operator (if you forget, the compiler will let you know). CHAPTER 12 ■ INDEXERS, OPERATORS, AND POINTERS 393 8849CH12.qxd 9/26/07 11:31 AM Page 393 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Overloading Comparison Operators In Chapter 9, you learned how to implement the IComparable interface in order to compare the rela- tive relationship between two like objects. Additionally, you may also overload the comparison operators ( <, >, <=, and >=) for the same class. Like the equality operators, C# demands that if you overload <, you must also overload >. The same holds true for the <= and >= operators. If the Point type overloaded these comparison operators, the object user could now compare Points as follows: // Using the overloaded < and > operators. static void Main(string[] args) { Console.WriteLine("ptOne < ptTwo : {0}", ptOne < ptTwo); Console.WriteLine("ptOne > ptTwo : {0}", ptOne > ptTwo); Console.ReadLine(); } Assuming you have implemented the IComparable interface, overloading the comparison oper- ators is trivial. Here is the updated class definition: // Point is also comparable using the comparison operators. public struct Point : IComparable { public int CompareTo(object obj) { if (obj is Point) { Point p = (Point)obj; if (this.x > p.x && this.y > p.y) return 1; if (this.x < p.x && this.y < p.y) return -1; else return 0; } else throw new ArgumentException(); } public static bool operator <(Point p1, Point p2) { return (p1.CompareTo(p2) < 0); } public static bool operator >(Point p1, Point p2) { return (p1.CompareTo(p2) > 0); } public static bool operator <=(Point p1, Point p2) { return (p1.CompareTo(p2) <= 0); } public static bool operator >=(Point p1, Point p2) { return (p1.CompareTo(p2) >= 0); } } The Internal Representation of Overloaded Operators Like any C# programming element, overloaded operators are represented using specific CIL syntax. To begin examining what takes place behind the scenes, open the OverloadedOps.exe assembly CHAPTER 12 ■ INDEXERS, OPERATORS, AND POINTERS394 8849CH12.qxd 9/26/07 11:31 AM Page 394 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com using ildasm.exe. As you can see from Figure 12-3, the overloaded operators are internally expressed via hidden methods (e.g., op_Addition(), op_Subtraction(), op_Equality(), and so on). Figure 12-3. In terms of CIL, overloaded operators map to hidden methods. Now, if you were to examine the specific CIL instructions for the op_Addition method, you would find that the specialname method decoration has also been inserted by csc.exe: .method public hidebysig specialname static valuetype OverloadedOps.Point op_Addition(valuetype OverloadedsOps.Point p1, valuetype OverloadedOps.Point p2) cil managed { } The truth of the matter is that any operator that you may overload equates to a specially named method in terms of CIL. Table 12-2 documents the C# operator-to-CIL mapping for the most com- mon C# operators. Table 12-2. C# Operator-to-CIL Special Name Road Map Intrinsic C# Operator CIL Representation op_Decrement() ++ op_Increment() + op_Addition() Continued CHAPTER 12 ■ INDEXERS, OPERATORS, AND POINTERS 395 8849CH12.qxd 9/26/07 11:31 AM Page 395 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Table 12-2. Continued Intrinsic C# Operator CIL Representation - op_Subtraction() * op_Multiply() / op_Division() == op_Equality() > op_GreaterThan() < op_LessThan() != op_Inequality() >= op_GreaterThanOrEqual() <= op_LessThanOrEqual() -= op_SubtractionAssignment() += op_AdditionAssignment() ■Note There is a practical reason to know the “special names” of an overloaded operator. Because many lan- guages cannot use types with overloaded operators, programmers of said languages are able to call these internal names statically from the defining type (e.g., Point.op_Addition(myPoint, yourPoint)). Final Thoughts Regarding Operator Overloading As you have seen, C# provides the capability to build types that can respond uniquely to various intrinsic, well-known operators. Now, before you go and retrofit all your classes to support such behavior, you must be sure that the operator(s) you are about to overload make some sort of logical sense in the world at large. For example, let’s say you overloaded the multiplication operator for the MiniVan class. What exactly would it mean to multiply two MiniVan objects? Not much. In fact, it would be very confus- ing for teammates to see the following use of MiniVan objects. // Huh?! This is far from intuitive MiniVan newVan = myVan * yourVan; Overloading operators is generally only useful when you’re building utility types. Strings, points, rectangles, fractions, and hexagons make good candidates for operator overloading. People, managers, cars, database connections, and web pages do not. As a rule of thumb, if an overloaded operator makes it harder for the user to understand a type’s functionality, don’t do it. Use this fea- ture wisely. Also be aware that even if you do not tend to overload operators for your custom classes, numerous types in the base class libraries have already done so. For example, the System.Drawing. dll assembly provides an “official” Point definition that overloads numerous operators. Notice the operator icon from the Visual Studio 2008 Object Browser (see Figure 12-4). CHAPTER 12 ■ INDEXERS, OPERATORS, AND POINTERS396 8849CH12.qxd 9/26/07 11:31 AM Page 396 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Figure 12-4. Numerous types in the base class libraries have already-overloaded operators. ■Source Code The OverloadedOps project is located under the Chapter 12 subdirectory. Understanding Custom Type Conversions Let’s now examine a topic closely related to operator overloading: custom type conversions. To set the stage for the discussion to follow, let’s quickly review the notion of explicit and implicit conver- sions between numerical data and related class types. Recall: Numerical Conversions In terms of the intrinsic numerical types (sbyte, int, float, etc.), an explicit conversion is required when you attempt to store a larger value in a smaller container, as this may result in a loss of data. Basically, this is your way to tell the compiler, “Leave me alone, I know what I am trying to do.” Con- versely, an implicit conversion happens automatically when you attempt to place a smaller type in a destination type that will not result in a loss of data: static void Main() { int a = 123; long b = a; // Implicit conversion from int to long int c = (int) b; // Explicit conversion from long to int } Recall: Conversions Among Related Class Types As shown in Chapter 6, class types may be related by classical inheritance (the “is-a” relationship). In this case, the C# conversion process allows you to cast up and down the class hierarchy. For example, a derived class can always be implicitly cast to a base type. However, if you wish to store a base class type in a derived variable, you must perform an explicit cast: CHAPTER 12 ■ INDEXERS, OPERATORS, AND POINTERS 397 8849CH12.qxd 9/26/07 11:31 AM Page 397 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com // Two related class types. class Base{} class Derived : Base{} class Program { static void Main() { // Implicit cast between derived to base. Base myBaseType; myBaseType = new Derived(); // Must explicitly cast to store base reference // in derived type. Derived myDerivedType = (Derived)myBaseType; } } This explicit cast works due to the fact that the Base and Derived classes are related by classical inheritance. However, what if you have two class types in different hierarchies with no common parent (other than System.Object) that requires conversions? Given that they are not related by classical inheritance, explicit casting offers no help. On a related note, consider value types (e.g., structures). Assume you have two .NET structures named Square and Rectangle. Given that structures cannot leverage classic inheritance (as they are always sealed), you have no natural way to cast between these seemingly related types. While you could build helper methods in the structures (such as Rectangle.ToSquare()), C# allows you to build custom conversion routines that allow your types to respond to the () casting operator. Therefore, if you configured the structures correctly, you would be able to use the follow- ing syntax to explicitly convert between them as follows: // Convert a Rectangle to a Square! Rectangle rect; rect.Width = 3; rect.Height = 10; Square sq = (Square)rect; Creating Custom Conversion Routines Begin by creating a new Console Application named CustomConversions. C# provides two key- words, explicit and implicit, that you can use to control how your types respond during an attempted conversion. Assume you have the following structure definitions: public struct Rectangle { // Public for ease of use; // however, feel free to encapsulate with properties. public int Width, Height; public Rectangle(int w, int h) { Width = w; Height = h; } public void Draw() { for (int i = 0; i < Height; i++) CHAPTER 12 ■ INDEXERS, OPERATORS, AND POINTERS398 8849CH12.qxd 9/26/07 11:31 AM Page 398 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com { for (int j = 0; j < Width; j++) { Console.Write("*"); } Console.WriteLine(); } } public override string ToString() { return string.Format("[Width = {0}; Height = {1}]", Width, Height); } } public struct Square { public int Length; public Square(int l) { Length = l; } public void Draw() { for (int i = 0; i < Length; i++) { for (int j = 0; j < Length; j++) { Console.Write("*"); } Console.WriteLine(); } } public override string ToString() { return string.Format("[Length = {0}]", Length); } // Rectangles can be explicitly converted // into Squares. public static explicit operator Square(Rectangle r) { Square s; s.Length = r.Height; return s; } } Notice that this iteration of the Square type defines an explicit conversion operator. Like the process of overloading an operator, conversion routines make use of the C# operator keyword (in conjunction with the explicit or implicit keyword) and must be defined as static. The incoming parameter is the entity you are converting from, while the operator type is the entity you are converting to. CHAPTER 12 ■ INDEXERS, OPERATORS, AND POINTERS 399 8849CH12.qxd 9/26/07 11:31 AM Page 399 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com In this case, the assumption is that a square (being a geometric pattern in which all sides are of equal length) can be obtained from the height of a rectangle. Thus, you are free to convert a Rectangle into a Square as follows: static void Main(string[] args) { Console.WriteLine("***** Fun with Conversions *****\n"); // Make a Rectangle. Rectangle r = new Rectangle(15, 4); Console.WriteLine(r.ToString()); r.Draw(); Console.WriteLine(); // Convert r into a Square, // based on the height of the Rectangle. Square s = (Square)r; Console.WriteLine(s.ToString()); s.Draw(); Console.ReadLine(); } The output can be seen in Figure 12-5. Figure 12-5. Converting a Rectangle structure to a Square structure While it may not be all that helpful to convert a Rectangle into a Square within the same scope, assume you have a function that has been designed to take Square parameters. // This method requires a Square type. static void DrawSquare(Square sq) { Console.WriteLine(sq.ToString()); sq.Draw(); } Using your explicit conversion operation on the Square type, you can now pass in Rectangle types for processing using an explicit cast: static void Main(string[] args) { // Convert Rectangle to Square to invoke method. CHAPTER 12 ■ INDEXERS, OPERATORS, AND POINTERS400 8849CH12.qxd 9/26/07 11:31 AM Page 400 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Rectangle rect = new Rectangle(10, 5); DrawSquare((Square)rect); Console.ReadLine(); } Additional Explicit Conversions for the Square Type Now that you can explicitly convert Rectangles into Squares, let’s examine a few additional explicit conversions. Given that a square is symmetrical on each side, it might be helpful to provide an explicit conversion routine that allows the caller to cast from a System.Int32 type into a Square (which, of course, will have a side length equal to the incoming integer). Likewise, what if you were to update Square such that the caller can cast from a Square into a System.Int32? Here is the calling logic: static void Main(string[] args) { // Converting a System.Int32 to a Square. Square sq2 = (Square)90; Console.WriteLine("sq2 = {0}", sq2); // Converting a Square to a System.Int32. int side = (int)sq2; Console.WriteLine("Side length of sq2 = {0}", side); Console.ReadLine(); } and here is the update to the Square type: public struct Square { public static explicit operator Square(int sideLength) { Square newSq; newSq.Length = sideLength; return newSq; } public static explicit operator int (Square s) {return s.Length;} } To be honest, converting from a Square into a System.Int32 may not be the most intuitive (or useful) operation. However, this does point out a very important fact regarding custom conversion routines: the compiler does not care what you convert to or from, as long as you have written syn- tactically correct code. Thus, as with overloading operators, just because you can create an explicit cast operation for a given type does not mean you should. Typically, this technique will be most helpful when you’re creating .NET structure types, given that they are unable to participate in classical inheritance (where casting comes for free). Defining Implicit Conversion Routines Thus far, you have created various custom explicit conversion operations. However, what about the following implicit conversion? CHAPTER 12 ■ INDEXERS, OPERATORS, AND POINTERS 401 8849CH12.qxd 9/26/07 11:31 AM Page 401 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com [...]... programming languages In C#, there is no separate preprocessing step Rather, preprocessing directives are processed as part of the lexical analysis phase of the compiler In any case, the syntax of the C# preprocessor directives is very similar to that of the other members of the C family, in that the directives are always prefixed with the pound sign (#) Table 12 -4 defines some of the more commonly used... OPERATORS, AND POINTERS C# Preprocessor Directives Like many other languages in the C family, C# supports the use of various symbols that allow you to interact with the compilation process Before examining various C# preprocessor directives, let’s get our terminology correct The term C# preprocessor directive” is not entirely accurate In reality, this term is used only for consistency with the C and C++ programming... Studio 2008) regarding the compilation of your code files w 41 4 8 849 CH13.qxd 10/2/07 12 :42 PM Page 41 5 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 13 C# 2008 Language Features C# ee -e bo o ks - do w nl oa d o rg 2008, the current release of Microsoft’s flagship NET programming language, introduces a large number of new syntactic constructs, one of which (the lambda... NET"); 41 3 8 849 CH12.qxd 9/26/07 11:31 AM Page 41 4 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 12 s INDEXERS, OPERATORS, AND POINTERS #endif } } } do w nl oa d o rg To create a project-wide symbol, make use of the Conditional compilation symbols text box located on the Build tab of your project’s Properties page (see Figure 12-9) The PreprocessorDirectives project... int PetName 8 849 CH13.qxd 10/2/07 12 :42 PM Page 42 1 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 13 s C# 2008 LANGUAGE FEATURES { get { return carName; } set { carName = value; } } } rg While defining a C# property is not too problematic, you may agree that when your properties simply assign and return the value straightaway as you see here, it is rather verbose to... this feature will offload the work of defining a private backing field and the related C# property member to the compiler using a new bit of syntax To illustrate, under C# 2008, the previous Car type could now be defined as follows: do w nl oa d o class Car { // Automatic property syntax public string PetName { get; set; } } s Note ee -e bo o ks - The Visual Studio 2008 “Prop” code snippet has been... Read-only property? Error! public int MyReadOnlyProp { get; } // Write only property? Error! public int MyWriteOnlyProp { set; } 42 1 8 849 CH13.qxd 10/2/07 12 :42 PM Page 42 2 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 13 s C# 2008 LANGUAGE FEATURES this will result in a compiler error When you are defining an automatic property, it must support both read and write... under the Chapter 13 subdirectory Understanding Automatic Properties As you learned in Chapter 5 during our examination of encapsulation services, NET programming languages prefer the use of type properties to safely obtain and assign private data fields of a type, rather than using traditional GetXXX() and SetXXX() methods Consider the following encapsulated string type: // A Car type using standard property... sizeof, checked, unsafe, and so forth), and during the process came to learn how to work with raw pointer types As stated throughout the chapter’s examination of pointer types, a vast majority of your C# applications will never need to make use of them We wrapped up with an examination of the core C# preprocessor directives, which allow you to interact with the compiler (or in the case of #region/#endregion,... interacting with the LINQ technology set, which you’ll begin to examine in Chapter 14 Given this fact, don’t be too concerned if the usefulness of some of these new constructs is not immediately obvious Once you understand the role of LINQ, the role of many of these new features will become crystal clear Understanding Implicitly Typed Local Variables w fr The first new language feature of C# 2008 we will . consistency with the C and C++ programming languages. In C#, there is no separate preprocessing step. Rather, preprocessing directives are processed as part of the lexical analysis phase of the compiler. In. behind the scenes, open the OverloadedOps.exe assembly CHAPTER 12 ■ INDEXERS, OPERATORS, AND POINTERS3 94 8 849 CH12.qxd 9/26/07 11 :31 AM Page 39 4 www.free-ebooks-download.org Simpo PDF Merge and. case, the syntax of the C# preprocessor directives is very similar to that of the other members of the C family, in that the directives are always prefixed with the pound sign ( #). Table 12 -4 defines

Ngày đăng: 12/08/2014, 23:20

TỪ KHÓA LIÊN QUAN