Visual Basic .NET The Complete Reference phần 5 potx

67 298 0
Visual Basic .NET The Complete Reference phần 5 potx

Đ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

Figure 8−1: Using WinCV to look at the built−in enumerations Using the Enum type, we can similarly represent a collection of bit flags for any application. Looking at the enumeration shown in Figure 8−1, one can conclude the following: an expression that checks for IsUnique and IsKey seems perfectly feasible, as does one that verifies whether a file IsCompressed | IsEncrypted or IsHidden AND IsReadOnly with the FileAttributes enumeration. We can do the same for the message flag constants demonstrated in the GetMessages example listed in Chapter 5. Remember the following collection of flags: Dim messageFlag As Integer = 2 Dim isAccessed As Integer = 4 Dim isArchived As Integer = 8 Dim isDeleted As Integer = 16 Dim newMessage As Integer = 32 This collection would be better represented inside an enumeration. We can call the enumeration MessageFlagEnum, as depicted in this code: Public Enum MessageFlagEnum MessageFlag = 0x0001 IsAccessed = 0x0002 IsArchived = 0x0004 IsDeleted = 0x0008 NewMessage = 0x0016 End Enum We don't have to initialize the symbols with hex values, although it doesn't hurt, and the symbol values can still be easily stored in the database. We also don't necessarily need to represent the bit flags in any special sequencinghere I have raised each bit flag to the power of two. However, as you have seen, the Enum semantics don't typically lend themselves to bit−flag semantics, in which it's normal to write an expression evaluating the combined state of more than one symbol. So, when you execute the following code, Dim MsgState As MessageFlagEnum.IsAccessed | MessageFlagEnum.IsArchived Debug.WriteLine(MsgState.ToString()) you are going to get the value 0x0006 to the console instead of the combination of the two symbols. Thus, if the intention is to write "IsAccessed, IsArchived" to the Debug window, it will not happen. To force the enumeration to return the latter, you can pass the "F" argument listed in Table 8−2 to the Format method or Flags 254 to the ToString method as shown in the forthcoming code: Dim MsgState As MessageFlagEnum.IsAccessed | MessageFlagEnum.IsArchived Debug.WriteLine(MsgState.ToString("F")) This is a little cumbersome; however, Microsoft has developed a cleaner approach to making the enumeration think it's a collection of bit flags. We can use the Flags attribute to denote bit−field or bit−flag semantics. While the runtime itself does not distinguish between traditional enumerations and bit fields, you can make this distinction in your code via the Flags attribute, allowing you to use bitwise operators transparently on all bit fields. At runtime, therefore, you get what you ask for. This amended Enum portrays the use of the flags attribute seen here decorating MessageFlagEnum: <Flags> Public Enum MessageFlagEnum MessageFlag = 0x0001 IsAccessed = 0x0002 IsArchived = 0x0004 IsDeleted = 0x0008 NewMessage = 0x0016 End Enum Final Words on Enums Here are some final considerations before you implement the Enum type in your applications: Enumerations can represent any of the ordinal built−in data types (except Char). Remember, the "magic words" are "magic numbers," not "magic Booleans" or "magic characters." • You can assign a value of the underlying type to an enumeration and vice versa (no cast is required by the runtime). • You can create an instance of an enumeration and call the methods of System.Enum, as well as any methods defined on the enumeration's underlying type. However, some languages might not allow you to pass an enumeration as a parameter when an instance of the underlying type is required (or vice versa). • Enums cannot define their own methods.• Enums cannot implement interfaces.• Enums cannot define properties and events.• Reduce operations with Enums that create a lot of boxing/unboxing code as demonstrated earlier in this chapter. Since some of the methods entail casting symbol values to objects, moving values to the heap may detract from performance requirements. Try re−writing your code to use minimal boxing overhead. • Finally, if only one class or object is using the Enum, don't nest or encapsulate it in the class. It won't be long before other classes in the application will need the Enum, and you'll be forced to define it at the level of those classes as well. This may not seem problematic for a small Enum, but imagine the difficulties if dozens of methods were depending on that Enum. Nesting also detracts from the elegant Enum semantics you have at your disposal, because Friend classes still have to access the encapsulating object's interface before they can reference the Enum. As you have seen, all Enums defined for the framework are identified at the same level as all standard classes. • Other than noting the few items in the above list, the enumeration is an especially powerful and fun construct to work with. Note The code for the above enumerations is the Enumerations project in the Vb7cr solution. Final Words on Enums 255 The Object−Reference Model It is important that you obtain an unshakable understanding of the object−reference model and how it differs from the value−type reference model before you progress to programming with classes and objects in the chapters to follow. The object−reference model specifically ensures that objects are accessed only via a reference variable that points to a location in memory where the object is stored. To best illustrate how this model works, let's evaluate an example that shows various ways of accessing and working with objects. Imagine that we are asked to design an object representing the fuel injector of a space ship. The injector's main purpose is to increase the sub−warp (significant percentage of light speed) velocity of the space ship. We can design a class to represent an Injector object containing a number of methods that manipulate the injector and interact with the many other space ship components and services. Assume we already have this classits methods control the velocity of a space shipsince designing and implementing it is not the subject of this section. The Injector class we are discussing follows a pattern written expressly for your creating multiple instances of Injector objects that are completely reentrant. This means you can use them in your application and be sure that the data fields in each object remain completely isolated and protected from any calls to methods in other Injector objects. The data in each Injector object is also completely isolated from the data in other objects that have been created in the same application space. Once you create an Injector object, you access it by referencing its name, as we see in this code: Dim Sim1 As New Injector Sim1.StartInjector() The first part of the expression, Dim Sim1, declares a new variable called Sim1. The second part, New Injector, creates a new Injector object by calling the type's instance constructorthe New method. We create the new Injector object and initialize Sim1 to reference it on the heap. Sim1 is thus a variable reference and a reference variable to an instance of the Injector class. In the early days of OO software development, the object−reference variable and the object were one and the same, like value types; the reference did not function as a "pointer" to the object's data in memory. When you declare the reference variable, you do not necessarily have to create the object and connect the dots. The following code is an example of late binding (see the illustration): Dim Sim1 Note Switching Option Strict to On forces you to declare the variable with the As clause and to thus assign a type at the same time. See Chapter 4, which explains the Option directives, and Chapters The Object−Reference Model 256 7 and 9, which talk about late binding. To create the reference variable and associate it with just the root object, you can use the following code: Dim Sim1 As New Object Here we are referring to nothing more than an instance of Object, which for this purpose is inconsequential. Nevertheless, we have created an object with the specific use of the As keyword and New (albeit New is what breaths life into the object As a Type). The reference variable is tied to the object and can perform certain actions on it. For example, we can create an instance of the Injector class with the following code: Dim Sim1 As New Injector() Now you have an Injector object loaded in memory and you can access it through the name Sim1 as illustrated here. The Injector object is loaded in memory and you can access it through Sim1 using this procedure: Start with Sim1, which is the reference used to manipulate and access data in the object. The reference is then followed by a period thus: Sim1. 1. Visual Studio now automatically gives you a drop−down list of the public members that are available to you. Choose a method you want to call, such as the accessor method or property IsWarpdriveOn, which is shown here: Dim checkIsOn As Boolean checkIsOn = Sim1.IsWarpdriveOn() 2. If the method call requires you to supply arguments, they will go between the braces and each will be separated by commas. This corresponds to the method's parameter list, as you saw in Chapter 7. In the above case, we are calling a method that will return a Boolean value, telling us if the warp drive is on or off. If the return value is True, the drive is on; if it's False . . . . We can also use the method call as follows: If Not Sim1.IsWarpdriveOn() Then '. . . End If 3. The above code takes the flow−of−execution into the If. . . Then structure and processes the code inside. The call to IsWarpDriveOn returned False (the default at start up), so you first need to turn the drive on. This is achieved with the following modification method call: The Object−Reference Model 257 Sim1.StartInjector() The method StartInjector is a Sub procedure and does not return a value. Also it does not need an argument, because you would want to start warp engines and remain at warp 0. Nevertheless, the call modifies the object because the method changes the data and the state of the object. The next step would be to set the warp speed for the simulation. The method call to do that is Sim1.SetWarpSpeed. This requires an argumentthe constant for the newWarpSpeed parameter, from the WarpSpeedEnum. The method takes an Integer and can be written as follows: Sim1.SetWarpSpeed(WarpFactorEnum.Impulse) The above call passes the enumeration symbol to the parameter, which sets the warp speed to WarpFactorEnum.Impulse. But this example is "hard−coded." The following example lets you enter the value at the command line. Using the console class' method to read input from the command line, you can send various arguments for warp speed to the Injector object as follows: Sim1.SetWarpSpeed(CInt(Console.ReadLine())) To get feedback from the object, you can access the warpSpeed field (remember this is one of the instance variables that gets initialized in the instance constructor). But these variables are privately encapsulated in the class and are thus off limits to the consumer. So when you type Sim1 in the IDE, warpSpeed will not be among the publicly accessible members. To access the warp, use the accessor method GetWarpSpeed as shown here: Sim1.GetWarpSpeed() The final example of calling the custom methods in Injector is the call to the accessor method GetMPS. This method multiplies the warp factor passed to the parameter by the speed of light in miles−per−second (MPS) and then returns the value to you. Write it as follows: Sim1.GetMPS(WarpFactorEnum.Impulse) You do not want to hard code the parameter, so pass the return value of the method call to Sim1.GetWarpSpeed as follows: Sim1.GetMPS(Sim1.GetWarpSpeed()) You can then write the return value to the console using the console object's WriteLine method as shown: Console.WriteLine("Light speed is " _ + CStr(Sim1.GetMPS(sim1.GetWarpSpeed())) & " miles−per−second.") What happens if you specify a parameter for warp that is greater than WarpFactorEnum.ImpulsePlusSeven? The class' method SetWarpSpeed evaluates the value as it passes into the method with an If . . . Then statement as follows: If WarpSpeed > WarpFactorEnum.Infinity Then Throw IllegalWarpParamException End If The exception handler "throws" the execution flow into the catch section of the Try . . . Catch structure, which turns off the injector as seen here: The Object−Reference Model 258 warpDrive = False This works because the variable or field named warpDrive is visible to the members of the class. With warpDrive set to False, your code can take the natural course of action to immediately stop the injector. The entire implementation of the console−based simulation is presented in this module: Module WarpSim Sub Main() Dim Sim1 As New Injector() Console.WriteLine("Testing injector simulation ") Try If Not Sim1.IsWarpdriveOn() Then Console.WriteLine("The injector is off. _ Enter to start or any key plus enter to abort test.") If Console.ReadLine() = "" Then Try Console.WriteLine("Starting injector ") Sim1.StartInjector() Console.WriteLine("The injector is on _ ready to engage warp drive ") While Sim1.IsWarpdriveOn Console.WriteLine("Enter warp speed ") Sim1.SetWarpSpeed(CInt(Console.ReadLine())) Console.WriteLine("Warp speed is set to: " & _ CStr(Sim1.GetWarpSpeed())) Console.WriteLine("Light speed is " & _ CStr(Sim1.GetMPS(Sim1.GetWarpSpeed())) & _ " miles−per−second.") If Sim1.GetWarpSpeed() = 0 Then Sim1.StopInjector() End If End While Catch Console.WriteLine(oState.ToString) End Try End If End If Catch Console.WriteLine(oState.ToString) End Try End Sub End Module As you can see, the object's reference variable is a versatile feature. In the unmanaged world, we would also have used it to destroy the object or remove it from the memory it occupies on the heap (such as Sim1.Free or Sim1.Destroy). But in the managed world of .NET, the garbage collector takes care of that (see Chapter 2 for an introduction to the garbage collector). When we are finished with an object, we can cut the connection between it and the reference variablelike cutting a lifeline between a soul and its body. This prompts the garbage collector to clean up. The Object−Reference Model 259 You are essentially placing the object out of scope, which can be noted thus: Sim1 = Nothing Another means of cutting the "lifeline" is to assign the variable reference to another object. You'll need to create this object if you don't have it. Look at how we achieve this: Dim Sim1 As New Injector Dim Sim2 As Object We now have two object variables called Sim1 and Sim2; they refer to different objects. Sim2 refers to Object, which will do nothing for it, while Sim1 refers to an instance of Injector. The objects and their reference variables are demonstrated here. To render Sim2 more useful, we can make it refer to the same object as Sim1 as demonstrated: Sim2 = Sim1 In the illustration, Sim1 and Sim2 now refer to the same Injector object, at the same location in memory. Declaring and using more than one object of the same type is not uncommon. If the class allows this, you can add as many Injector objects as you need to the application. You will often be working with patterns that The Object−Reference Model 260 require you to create more than one instance of the same object. Note You can program a class to allow only one instance of itself to be created. This is called a singleton class; its pattern is demonstrated in Chapter 13. Creating more than one object of the same type requires only another call to the Injector class' constructor. All you need is a new name, as shown here: Dim Sim1 As New Injector() Dim Sim2 As New Injector() You can now reference each object through the variables Sim1 and Sim2 independently, as illustrated. The data in each object is encapsulated in its own field, so modifying data through Sim1.SetWarpSpeed does not affect the warpSpeed field of Sim2. Remember, you have created two distinct variables: Sim1 and Sim2. But you have also explicitly created two Injector objects, and each variable references its own object. For Sim1 and Sim2 to refer to the same object, you will need the following code: Sim1 = Sim2 In other words, if you set the warp speed by calling Sim1.SetWarpSpeed(WarpFactorEnum.ImpulsePlusSeven) and then call Sim2.GetWarpSpeed, the return value will be WarpFactorEnum.ImpulsePlusSeven because Sim1 and Sim2 now refer to the same object (see the discussion on enumerated types). As a further example: Sim1.GetMPS(Sim2.GetWarpSpeed()) This call returns the value for MPS even though you never explicitly made the call to Sim2.StartInjector().The difference between standard types and reference types should now be crystallizing (isn't OO wonderful?). Null Reference We can explicitly cut the reference variable's lifeline to an object by telling it to reference "nada." Using the so−called Null reference (represented by the keyword Nothing in Visual Basic) makes the variable assign to nothing, as show here: Sim2 = Nothing This does not necessarily hasten the work of the garbage collector; nonetheless, it is a good idea to set the Null Reference 261 reference to Nothing when the object becomes orphaned. Does this mean you can still use the reference variable Sim2? Yes it does. It was declared, so re−setting Sim2 = Sim1 is valid because all you are doing is telling Sim2 to get a life, like Sim1. But setting Sim1 = Sim2 will cause catastrophic failure. Why? Sim1 cannot refer to the Null reference, and the code will throw off the NullReferenceException. Make a note somewhere about this Null reference error, because it is easy to cause this bug in your code (see Chapter 11, which covers exception handling). Also, make a note that setting a reference variable to Null does not nullify the actual object, only the reference to it. If there is only one reference to the object, it is orphaned and earmarked for collection. But if more than one variable references the object and just one variable is set to Null, then only that reference is unaffected. Note See Finalization in Chapter 9 and Chapter 17. What the Reference Refers To Experts as well as programmers new to Object−Oriented software development often refer to the reference variablesuch as Sim1as the actual object. This is incorrect. Sim1 and Sim2 are not themselves the objects of class Injector; they are just the reference variables. Thus, it is fallacious to say "the injector Sim1 has been set to " It is accurate to say "the injector that Sim1 refers to has been set to. . .". Naturally when you are sitting around a table talking code with one of your buddies, it's fine to say things like "Sim1 just blew up the space ship; it must be the code in your class." But when you need to prepare formal documentation, use the longer expression. It will help keep your documentation clear and easier to understand. The Object−Reference Model and Equality, Comparison, and Assignment You may also encounter confusion when you test equality and make assignments or comparisons between and among objects. Are you performing these functions with regard to the references or their objects? Actually, you can do both. The Equals method compares objects for assignment or reference data. Equals is inherited from System.Object. To test if one reference compares to another you can use the Is operator. The Is operator is not the same thing as the = operator (see Chapter 5, "Visual Basic .NET Operators" and Chapter 9, "Classes"). This code tests whether Sim1 equals Sim2: If (Sim1 Is Sim2) Then . . . End If If Sim1 and Sim2 reference the same object, then the Is comparison returns True . . . and False if they do not. For example: Dim Sim1 As New Injector() Dim Sim2 As New Injector() Sim1 = Sim2 If (Sim1 Is Sim2) Then Debug.WriteLine("Sim1 is Sim2") End If This might be more easily understood through an illustration. The illustration shows Sim1 and Sim2 referencing the same object; therefore, Is returns True. What the Reference Refers To 262 Let's see what happens when we introduce a third Injector: Dim Sim3 As New Injector() Sim2 = Sim3 If (Sim1 Is Sim2) Then Debug.WriteLine("Sim1 is Sim2") End If Is does not return True anymore, because Sim1 and Sim2 no longer reference the same object. However, Sim2 Is Sim3 returns True. There is a quirk: as long as two or more variable references refer to the same object, they are considered equal. Also, Null references (Sim1 = Nothing) also return True when compared with the Is operator. To compare the objects, you should implement the CompareTo method defined by the IComparable interface or bridge to a comparator (see Chapter 12). You will be able to write code here that compares the bits of objects rather than the reference variables. Chapter 10 provides an in−depth discussion of this subject. What Me Refers To When you have a class that can be instantiated multiple times, you'll find that the Me keywordan internal reference variableconveniently references the object from within its own instance space. From this viewpoint, everything is visible, yet still protected from the outside world. Here we model the Injector object calling its own GetType method: Public Function WhatAmI() As String Return Me.GetType().ToString End Function Note Me is the same as the keyword This in C#. It is also not legal to use it in a class module. As you will learn in the next chapter, there are limits to using Me. For instance, it is not valid in shared classes that cannot be instantiated. Observations Microsoft is not alone in implementing primitives as first−class lightweight objectsseveral other languages have taken the same approach, including ADA and Smalltalk. I scrutinized Java's primitives earlier in this chapter and concluded that they are primitive, or native, types and not first−class objects like .NET's value types. You can place Java primitives on the heap using wrapper The Object−Reference Model and Equality, Comparison, and Assignment 263 [...]... development life−cycle The Object Model The object model is the most abstract of the models because it describes the structure of the objects in your software An object model (and you could call the entire NET Framework one huge object model) identifies the objects and the relationships between them It also defines their attributes and what they do The object model is very much the focus of this chapter... model The dynamic model, on the other hand, describes the control structures of the objects in the object model And the functional model represents the functionality that is achieved by the operations in the object model and the actions in the dynamic model It is important to understand that the models you create can never be exact representations of the actual software system There is an accepted deficiency... with the inner workings of classes and methods or how the methods are executed Model Relationships While each model alone describes various aspects of a software system, the combination of all of them, with references to each other, fully describe the software system For example, the operations in the object model relate to events in the dynamic model and the functionality in the functional model The. .. other to provide a complete model of a system However, each model can be separated from the trio and examined exclusively The best model builders create interconnections between the three models but avoid designing them in such a way that they become inseparable to the extent that the objectives of the models in the first instance are lost A good model is one that incorporates the three views into the. .. functionality, and in the case of abstract classes, the methods, properties, and events are often defined with the MustOverride modifier, which means that you must override and implement the methods in the subclasses So, again, the message is sent, and the interface and the method signature are the same The only difference is the class and the implementation So, polymorphism is served and the type system... three fundamental and distinct models of the software that represent the viewpoints of the model These viewpoints are really your means of looking into the future to see something as close to the final product as possible The three models are defined in the Object Modeling Technique (OMT) as follows: • The object model • The dynamic model • The functional model These three core models, illustrated here,... interfaces, and the relationships between the classes The classes themselves are quadrangles that you divide into several compartments A typical class component in UML contains three specific compartments, as illustrated in Figure 9−2 Figure 9−2: The basic class represented in UML The top compartment represents the class name The middle compartment represents the class attributes or variablesthe data of the class... with the BCL and your custom classes For example, the C# compiler understands how to deal with overloaded operators, but this is not yet available to the Visual Basic programmer, even though operator overloading is well within the realm of possibilities of the Visual Basic compiler You should not see this as a limitation at all, because these "differences" are actually there for a reason, and some of them... classes (aka the base classes) The user classes are the ones you will build from scratch Actually, as you have seen, you first derive theminheritancefrom the base classes (at the least you will derive from Object) and other custom classes User or custom classes need to conform to CLS; otherwise, at best, they will introduce bugs, and, at worst, they will not compile The framework classes are the ones that... chapter and the others to follow there will be an added bonus to being good at OO; besides Visual Basic you'll also be able to easily tackle any designfor any language including J#, C#, Java, or otherwise Note 2 65 Getting the Semantics Correct If you are an experienced OO programmer, you can skim over this chapter, focusing just on the stuff you need to understand NET classes, the Visual Basic NET idioms, . warp 0. Nevertheless, the call modifies the object because the method changes the data and the state of the object. The next step would be to set the warp speed for the simulation. The method call. System.Object. To test if one reference compares to another you can use the Is operator. The Is operator is not the same thing as the = operator (see Chapter 5, " ;Visual Basic .NET Operators". critical mastery of the workings of NET types. There is talk at Microsoft about possibly including generic types in the next major release of the .NET Framework. Whether they make it into the CLS, or

Ngày đăng: 14/08/2014, 01:20

Từ khóa liên quan

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

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

Tài liệu liên quan