Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 135 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
135
Dung lượng
1,19 MB
Nội dung
Part I: The C# Language 234 This indicates that the two types are intended for different purposes. Abstract classes are intended for use as the base class for families of objects that share certain central characteristics, such as a common purpose and structure. Interfaces are intended for use by classes that might differ on a far more fundamental level, but can still do some of the same things. For example, consider a family of objects representing trains. The base class, Train , contains the core definition of a train, such as wheel gauge and engine type (which could be steam, diesel, and so on). However, this class is abstract, because there is no such thing as a “ generic ” train. To create an “ actual ” train, you add characteristics specific to that train. For example, you derive classes such as PassengerTrain , FreightTrain , and 424DoubleBogey , as shown in Figure 9 - 14 . Train PassengerTrain FreightTrain 424DoubleBogey Figure 9-14 Vehicle PassengerTrain FreightTrain Train 424DoubleBogeyCompact SUV Car Pickup Figure 9-15 A family of car objects might be defined in the same way, with an abstract base class of Car and derived classes such as Compact , SUV , and PickUp . Car and Train might even derive from a common base class, such as Vehicle . This is shown in Figure 9 - 15 . c09.indd 234c09.indd 234 3/24/08 3:35:59 PM3/24/08 3:35:59 PM Chapter 9: Defi ning Classes 235 Some of the classes lower in the hierarchy may share characteristics because of their purpose, not just because of what they are derived from. For example, PassengerTrain , Compact , SUV , and Pickup are all capable of carrying passengers, so they might possess an IPassengerCarrier interface. FreightTrain and PickUp can carry heavy loads, so they might both have an IHeavyLoadCarrier interface as well. This is illustrated in Figure 9 - 16 . Compact SUV Pickup «interface» IPassengerCarrier «interface» IHeavyLoadCarrier PassengerTrain FreightTrain 424DoubleBogey Train Vehicle Car Figure 9-16 By breaking down an object system in this way before going about assigning specifics, you can clearly see which situations should use abstract classes rather than interfaces, and vice versa. The result of this example couldn ’ t be achieved using only interfaces or only abstract inheritance. Struct Types Chapter 8 noted that structs and classes are very similar but that structs are value types and classes are reference types. What does this actually mean to you? Well, the easiest way of looking at this is with an example, such as the following Try It Out. c09.indd 235c09.indd 235 3/24/08 3:35:59 PM3/24/08 3:35:59 PM Part I: The C# Language 236 Try It Out Classes versus Structs 1. Create a new console application project called Ch09Ex03 and save it in the directory C:\BegVCSharp\Chapter09. 2. Modify the code as follows: namespace Ch09Ex03 { class MyClass { public int val; } struct myStruct { public int val; } class Program { static void Main(string[] args) { MyClass objectA = new MyClass(); MyClass objectB = objectA; objectA.val = 10; objectB.val = 20; myStruct structA = new myStruct(); myStruct structB = structA; structA.val = 30; structB.val = 40; Console.WriteLine(“objectA.val = {0}”, objectA.val); Console.WriteLine(“objectB.val = {0}”, objectB.val); Console.WriteLine(“structA.val = {0}”, structA.val); Console.WriteLine(“structB.val = {0}”, structB.val); Console.ReadKey(); } } } 3. Run the application. The output is shown in Figure 9 - 17 . Figure 9-17 c09.indd 236c09.indd 236 3/24/08 3:35:59 PM3/24/08 3:35:59 PM Chapter 9: Defi ning Classes 237 How It Works This application contains two type definitions: one for a struct called myStruct , which has a single public int field called val , and one for a class called MyClass that contains an identical field (you look at class members such as fields in Chapter 10 ; for now just understand that the syntax is the same here). Next, you perform the same operations on instances of both of these types: 1. Declare a variable of the type. 2. Create a new instance of the type in this variable. 3. Declare a second variable of the type. 4. Assign the first variable to the second variable. 5. Assign a value to the val field in the instance in the first variable. 6. Assign a value to the val field in the instance in the second variable. 7. Display the values of the val fields for both variables. Although you are performing the same operations on variables of both types, the outcome is different. When you display the values of the val field, both object types have the same value, whereas the struct types have different values. What has happened? Objects are reference types. When you assign an object to a variable you are actually assigning that variable with a pointer to the object it refers to. A pointer, in real code terms, is an address in memory. In this case, the address is the point in memory where the object is found. When you assign the first object reference to the second variable of type MyClass with the following line, you are actually copying this address: MyClass objectB = objectA; This means that both variables contain pointers to the same object. Structs are value types. Instead of the variable holding a pointer to the struct, the variable contains the struct itself. When you assign the first struct to the second variable of type myStruct with the following line, you are actually copying all the information from one struct to the other: myStruct structB = structA; You saw behavior like this earlier in this book for simple variable types such as int . The upshot is that the two struct type variables contain different structs. The entire technique of using pointers is hidden from you in managed C# code, making your code much simpler. It is possible to access lower - level operations such as pointer manipulation in C# using unsafe code, but that is an advanced topic not covered here. c09.indd 237c09.indd 237 3/24/08 3:36:00 PM3/24/08 3:36:00 PM Part I: The C# Language 238 Shallow Copying versus Deep Copying Copying objects from one variable to another by value instead of by reference (that is, copying them in the same way as structs) can be quite complex. Because a single object may contain references to many other objects, such as field members and so on, a lot of processing may be involved. Simply copying each member from one object to another may not work because some of these members might be reference types in their own right. The .NET Framework takes this into account. Simple object copying by members is achievable through the method MemberwiseClone , inherited from System.Object . This is a protected method, but it would be easy to define a public method on an object that called this method. This copying method is known as a shallow copy, in that it doesn ’ t take reference type members into account. This means that reference members in the new object refer to the same objects as equivalent members in the source object, which isn ’ t ideal in many cases. If you want to create new instances of the members in question by copying the values across (rather than the references), you need to perform a deep copy . There is an interface you can implement that enables you to do this in a standard way: ICloneable . If you use this interface, then you must implement the single method it contains, Clone . This method returns a value of type System.Object . You can use whatever processing you want to obtain this object, by implementing the method body however you choose. That means you can implement a deep copy if you want to (although the exact behavior isn ’ t mandatory, so you could perform a shallow copy if desired). You take a closer look at this in Chapter 11 . Summary This chapter showed how you can define classes and interfaces in C#, putting the theory from the last chapter into a more concrete form. You ’ ve learned the C# syntax required for basic declarations as well as the accessibility keywords you can use, the way in which you can inherit from interfaces and other classes, how to define abstract and sealed classes to control this inheritance, and how to define constructors and destructors. You then looked at System.Object , the root base class of any class that you define. It supplies several methods, some of which are virtual , so you can override their implementation. This class also enables you to treat any object instance as an instance of this type, enabling polymorphism with any object. You also examined some of the tools supplied by VS and VCE for OOP development, including the Class View window, the Object Browser window, and a quick way to add new classes to a project. As an extension of this multifile concept, you learned how to create assemblies that can ’ t be executed, but that contain class definitions that you can use in other projects. After that, you took a more detailed look at abstract classes and interfaces, including their similarities and differences, and situations in which you use one or the other. Finally, you revisited the subject of reference and value types, looking at structs (the value type equivalent of objects) in slightly more detail. This led to a discussion about shallow and deep copying of objects, a subject covered in more detail later in the book. The next chapter looks at defining class members, such as properties and methods, which will enable you to take OOP in C# to the level required to create real applications. c09.indd 238c09.indd 238 3/24/08 3:36:00 PM3/24/08 3:36:00 PM Chapter 9: Defi ning Classes 239 Exercises 1. What is wrong with the following code? public sealed class MyClass { // Class members. } public class myDerivedClass : MyClass { // Class members. } 2. How would you define a noncreatable class? 3. Why are noncreatable classes still useful? How do you make use of their capabilities? 4. Write code in a class library project called Vehicles that implements the Vehicle family of objects discussed earlier in this chapter. There are nine objects and two interfaces that require implementation. 5. Create a console application project, Traffic, that references Vehicles.dll (created in question 4). Include a function called AddPassenger that accepts any object with the IPassengerCarrier interface. To prove that the code works, call this function using instances of each object that supports this interface, calling the ToString method inherited from System . Object on each one and writing the result to the screen. c09.indd 239c09.indd 239 3/24/08 3:36:00 PM3/24/08 3:36:00 PM c09.indd 240c09.indd 240 3/24/08 3:36:01 PM3/24/08 3:36:01 PM 10 Defining Class Members This chapter continues exploring class definitions in C# by looking at how you define field, property, and method class members. You start by examining the code required for each of these types, and learn how to generate the structure of this code using wizards. You also learn how to modify members quickly by editing their properties. After covering the basics of member definition, you ’ ll learn some advanced techniques involving members: hiding base class members, calling overridden base class members, nested type definitions, and partial class definitions. Finally, you put theory into practice by creating a class library that you can build on and use in later chapters. In this chapter you learn how to do the following: Work with fields, properties, and method class members . Create a class library . Member Definitions Within a class definition, you provide definitions for all members of the class, including fields, methods, and properties. All members have their own accessibility levels, defined in all cases by one of the following keywords: public — Members are accessible from any code. private — Members are accessible only from code that is part of the class (the default if no keyword is used). internal — Members are accessible only from code within the project (assembly) where they are defined. protected — Members are accessible only from code that is part of either the class or a derived class. ❑ ❑ ❑ ❑ ❑ ❑ c10.indd 241c10.indd 241 3/24/08 3:36:45 PM3/24/08 3:36:45 PM Part I: The C# Language 242 The last two of these can be combined, so protected internal members are also possible. These are only accessible from code - derived classes within the project (more accurately, the assembly). Fields, methods, and properties can also be declared using the keyword static , which means that they are static members owned by the class, rather than by object instances, as discussed in Chapter 8 . Defining Fields Fields are defined using standard variable declaration format (with optional initialization), along with the modifiers discussed previously: class MyClass { public int MyInt; } Public fields in the .NET Framework are named using PascalCasing, rather than camelCasing, and that ’ s the casing methodology used here. That ’ s why the field in this example is called MyInt instead of myInt . This is only a suggested casing scheme, but it makes a lot of sense. There is no recommendation for private fields, which are usually named using camelCasing. Fields can also use the keyword readonly , meaning that the field may be assigned a value only during constructor execution or by initial assignment: class MyClass { public readonly int MyInt = 17; } As noted in the chapter introduction, fields may be declared as static using the static keyword: class MyClass { public static int MyInt; } Static fields are accessed via the class that defines them ( MyClass.MyInt in the preceding example), not through object instances of that class. You can use the keyword const to create a constant value. const members are static by definition, so you don ’ t need to use the static modifier (in fact, it is an error). Defining Methods Methods use standard function format, along with accessibility and optional static modifiers, as shown in this example: c10.indd 242c10.indd 242 3/24/08 3:36:45 PM3/24/08 3:36:45 PM Chapter 10: Defi ning Class Members 243 class MyClass { public string GetString() { return “Here is a string.”; } } Like fields, public methods in the .NET Framework are named using PascalCasing. Remember that if you use the static keyword, then this method is accessible only through the class, not the object instance. You can also use the following keywords with method definitions: virtual — The method may be overridden. abstract — The method must be overridden in non - abstract derived classes (only permitted in abstract classes). override — The method overrides a base class method (it must be used if a method is being overridden). extern — The method definition is found elsewhere. Here ’ s an example of a method override: public class MyBaseClass { public virtual void DoSomething() { // Base implementation. } } public class MyDerivedClass : MyBaseClass { public override void DoSomething() { // Derived class implementation, overrides base implementation. } } If override is used, then sealed may also be used to specify that no further modifications can be made to this method in derived classes — that is, the method can ’ t be overridden by derived classes. Here is an example: public class MyDerivedClass : MyBaseClass { public override sealed void DoSomething() { // Derived class implementation, overrides base implementation. } } ❑ ❑ ❑ ❑ c10.indd 243c10.indd 243 3/24/08 3:36:46 PM3/24/08 3:36:46 PM [...]... icon that looks like two downward pointing chevrons) The resulting view is shown in Figure 10-2 Figure 10-2 249 c10.indd 249 3/ 24/08 3: 36:48 PM Part I: The C# Language In the Class Details window, you can see the information shown in Figure 10 -3 when the class is selected Figure 10 -3 The window shows all the currently defined members for the class and includes spaces so you can add new members simply by... directly is fine The only limitation of automatic properties is that they must include both a get and set accessor — you cannot define read- or write-only properties in this way 2 53 c10.indd 2 53 3/24/08 3: 36:49 PM Part I: The C# Language Additional Class Member Topics Now that you’ve covered the basics of member definition, it’s time to look at some more advanced member topics This section tackles the... partial class MyClass : IMyInteface2 { } is equivalent to this one: public class MyClass : IMyInteface1, IMyInteface2 { } This also applies to attributes, covered in Chapter 27 2 63 c10.indd 2 63 3/24/08 3: 36:52 PM Part I: The C# Language Par tial Method Definitions Partial classes may also define partial methods Partial methods are defined in one partial class definition without a method body, and implemented... that the underlying storage of the enum matches the rank of the card, such that Six is stored as 6, for example When you’ve finished, the diagram should look as shown in Figure 10- 13 Figure 10- 13 268 c10.indd 268 3/ 24/08 3: 36: 53 PM Chapter 10: Defining Class Members The code generated for these two enumerations, in the code files Suit.cs and Rank.cs, is as follows: using System; using System.Collections.Generic;... the Toolbox into the diagram, and then filling in the dialog that appears For example, for the Suit enumeration, fill out the dialog as shown in Figure 10-10 Figure 10-10 267 c10.indd 267 3/ 24/08 3: 36: 53 PM Part I: The C# Language Next, add the members of the enumeration using the Class Details window The values required are shown in Figure 10-11 Figure 10-11 Add the Rank enumeration from the Toolbox... void DoSomething() { Console.WriteLine(“Base imp”); } } public class MyDerivedClass : MyBaseClass { new public void DoSomething() { Console.WriteLine(“Derived imp”); } } 255 c10.indd 255 3/ 24/08 3: 36:49 PM Part I: The C# Language The base class method needn’t be virtual for this to work, but the effect is exactly the same and the preceding code only requires changes to one line The result, for a virtual... the last chapter, you learned that interfaces are defined in a similar way as classes, using code such as the following: interface IMyInterface { // Interface members } 257 c10.indd 257 3/ 24/08 3: 36:50 PM Part I: The C# Language Interface members are defined like class members except for a few important differences: ❑ No access modifiers (public, private, protected, or internal) are allowed — all interface... DoSomethingElse(); } public class MyBaseClass { public void DoSomething() { } } public class MyDerivedClass : MyBaseClass, IMyInterface { public void DoSomethingElse() { } } 259 c10.indd 259 3/ 24/08 3: 36:51 PM Part I: The C# Language Inheriting from a base class that implements a given interface means that the interface is implicitly supported by the derived class Here’s an example: public interface IMyInterface... MyIntProperty { get; } } public class MyBaseClass : IMyInterface { protected int myInt; public int MyIntProperty { get { return myInt; } protected set { myInt = value; } } } 261 c10.indd 261 3/ 24/08 3: 36:51 PM Part I: The C# Language Par tial Class Definitions When you create classes with a lot of members of one type or another, things can get quite confusing, and code files can get very long One thing that... when you exert a little more control over the proceedings For example, you might implement your set block as follows: set { if (value >= 0 && value . writing the result to the screen. c09.indd 239 c09.indd 239 3/ 24/08 3: 36:00 PM3/24/08 3: 36:00 PM c09.indd 240c09.indd 240 3/ 24/08 3: 36:01 PM3/24/08 3: 36:01 PM 10 Defining Class Members This. c09.indd 235 c09.indd 235 3/ 24/08 3: 35:59 PM3/24/08 3: 35:59 PM Part I: The C# Language 236 Try It Out Classes versus Structs 1. Create a new console application project called Ch09Ex 03 and save. that is an advanced topic not covered here. c09.indd 237 c09.indd 237 3/ 24/08 3: 36:00 PM3/24/08 3: 36:00 PM Part I: The C# Language 238 Shallow Copying versus Deep Copying Copying objects