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

Accelerated VB 2005 phần 5 docx

43 248 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

Nội dung

Public Sub New(ByVal real As Double, ByVal imaginary As Double) Me.Real = real Me.Imaginary = imaginary End Sub 'System.Object override Public Overrides Function ToString() As String Return System.String.Format("({0}, {1})", Real, Imaginary) End Function Public ReadOnly Property Magnitude() As Double Get Return Math.Sqrt(Math.Pow(Me.Real, 2) + Math.Pow(Me.Imaginary, 2)) End Get End Property Public Shared Operator IsTrue(ByVal c As Complex) As Boolean Return (c.Real <> 0) OrElse (c.Imaginary <> 0) End Operator Public Shared Operator IsFalse(ByVal c As Complex) As Boolean Return (c.Real = 0) AndAlso (c.Imaginary = 0) End Operator Public Shared Widening Operator CType(ByVal d As Double) As Complex Return New Complex(d, 0) End Operator Public Shared Narrowing Operator CType(ByVal c As Complex) As Double Return c.Magnitude End Operator 'Other methods omitted for clarity. End Structure Public Class EntryPoint Shared Sub Main() Dim cpx1 As Complex = New Complex(1.0, 3.0) If cpx1 Then Console.WriteLine("cpx1 is True") Else Console.WriteLine("cpx1 is False") End If Dim cpx2 As Complex = New Complex(0.0, 0.0) CHAPTER 8 ■ OPERATOR OVERLOADING 149 801-6CH08.qxd 2/28/07 12:24 AM Page 149 Console.WriteLine("cpx2 is {0}", IIf(cpx2, "True", "False")) End Sub End Class This code displays the following results: cpx1 is True cpx2 is False You can see the two operators for applying the IsTrue and IsFalse tests to the Complex type. Notice that the declaration syntax looks almost the same as the conversion operators, except it includes the return type of Boolean. Also, you cannot mark these operators as Widen- ing or Narrowing, because they’re not conversion operators. Once you define these two operators on the type, you can use instances of Complex in Boolean test expressions, as shown in the Main method. ■Note In setting Option Strict Off in the previous example, you’re forcing the narrowing conversion to Boolean to be accepted by the compiler.You should do this only if you’re certain that a runtime error is not possible. Alternatively, you can choose to implement a conversion to type Boolean to achieve the same result. Typically, you want to implement this operator implicitly for ease of use. Consider the modified form of the previous example using the widening Boolean conversion operator rather than Operator IsTrue and Operator IsFalse: Imports System Public Structure Complex Private Real As Double Private Imaginary As Double Public Sub New(ByVal real As Double, ByVal imaginary As Double) Me.Real = real Me.Imaginary = imaginary End Sub 'System.Object override Public Overrides Function ToString() As String Return System.String.Format("({0}, {1})", Real, Imaginary) End Function Public ReadOnly Property Magnitude() As Double Get Return Math.Sqrt(Math.Pow(Me.Real, 2) + Math.Pow(Me.Imaginary, 2)) CHAPTER 8 ■ OPERATOR OVERLOADING150 801-6CH08.qxd 2/28/07 12:24 AM Page 150 End Get End Property Public Shared Widening Operator CType(ByVal c As Complex) As Boolean Return (c.Real <> 0) OrElse (c.Imaginary <> 0) End Operator Public Shared Widening Operator CType(ByVal d As Double) As Complex Return New Complex(d, 0) End Operator Public Shared Narrowing Operator CType(ByVal c As Complex) As Double Return c.Magnitude End Operator 'Other methods omitted for clarity. End Structure Public Class EntryPoint Shared Sub Main() Dim cpx1 As Complex = New Complex(1.0, 3.0) If cpx1 Then Console.WriteLine("cpx1 is True") Else Console.WriteLine("cpx1 is False") End If Dim cpx2 As Complex = New Complex(0.0, 0.0) Console.WriteLine("cpx2 is {0}", IIf(cpx2, "True", "False")) End Sub End Class The end result is the same with this example. Now, you may be wondering why you would ever want to implement Operator IsTrue and Operator IsFalse rather than just use a widen- ing Boolean conversion operator. The answer lies in the fact of whether it is valid for your type to be converted to a Boolean type or not. With the latter form, where you implement the widening conversion operator, the following statement would be valid: cpx1 = f This assignment would work because the compiler would find the widening conversion operator at compile time and apply it. The rule of thumb is to provide only enough of what is necessary to get the job done. If all you want is for your type—in this case, Complex—to partici- pate in Boolean test expressions, only implement Operator IsTrue and Operator IsFalse. If you do have a need to implement the widening Boolean conversion operator, you don’t need to implement Operator IsTrue and Operator IsFalse, because they would be redundant. If you CHAPTER 8 ■ OPERATOR OVERLOADING 151 801-6CH08.qxd 2/28/07 12:24 AM Page 151 provide all three, the compiler will go with the widening conversion operator rather than Operator IsTrue and Operator IsFalse, because invoking one is not more efficient than the other, assuming you code them the same. Summary This chapter covered some guidelines for overloading operators, including unary, binary, and conversion operators. Operator overloading is one of the features that makes VB 2005 such a powerful and expressive .NET language. However, just because you can do something doesn’t mean you should. Misuse of widen- ing conversion operators and improperly defined semantics in other operator overloads can be the source of great user confusion, as well as unintended behavior. When it comes to over- loading operators, provide only what is necessary and don’t go counter to the general semantics of the various operators. Unless you’re sure that your code will be consumed by .NET languages that support operator overloading, be sure to provide explicitly named meth- ods that provide the same functionality. In the next chapter, we’ll cover the intricacies and tricks to creating exception-safe and exception-neutral code in the .NET Framework. CHAPTER 8 ■ OPERATOR OVERLOADING152 801-6CH08.qxd 2/28/07 12:24 AM Page 152 Exception Handling The common language runtime (CLR) contains strong support for exceptions. You can create and throw exceptions at a point where code execution cannot continue due to some excep- tional condition (usually a method failure or invalid state). Once exceptions are thrown, the CLR begins the process of unwinding the call stack iteratively frame by frame. 1 As it does so, it cleans up any objects that are local to each stack frame. At some point, a frame on the stack could have an exception handler registered for the type of exception thrown. Once the CLR reaches that frame, it invokes the exception handler to remedy the situation. If the stack unwind finishes and a handler is not found for the exception thrown, then the unhandled exception event for the current application domain may be fired and the application could be aborted. Writing exception-safe code is a difficult art to master. It would be a mistake to assume that the only tasks required when writing exception-safe code are simply throwing exceptions when an error occurs and catching them at some point. Instead, exception-safe coding tech- niques are those with which you can guarantee the integrity of the system in the face of exceptions. When an exception is thrown, the runtime will iteratively unwind the stack while cleaning up. Your job as an exception-safe programmer is to structure your code in such a way that the integrity of the state of your objects is not compromised as the stack unwinds. That is the true essence of exception-safe coding techniques. Handling Exceptions Where should you handle exceptions? You can find the answer by applying a variant of the Expert pattern, which states that work should be done by the entity that is the expert with respect to that work. That is a circuitous way of saying that you should catch the exception at the point where you can actually handle it with some degree of knowledge available to remedy the situation. Sometimes, the catching entity could be close to the point of the exception gen- eration within the stack frame. The code could catch the exception, then take some corrective action, and then allow the program to continue to execute normally. Other times, the only reasonable place to catch an exception is at the entry-point Main method, at which point you 153 CHAPTER 9 1. As each method is called throughout the execution of a program, a frame is built on the stack that contains the passed parameters and any local parameters to the method. The frame is deleted upon return from the method. However, as the method calls other methods, and so on, new frames are stacked on top of the current frame, thus implementing a nested call-stack structure. 801-6CH09.qxd 3/3/07 3:02 AM Page 153 154 CHAPTER 9 ■ EXCEPTION HANDLING could either abort the process after providing some useful data, or you could reset the process as if the application were just restarted. The bottom line is that you should figure out the best way to recover from exceptions and where it makes the most sense to do so. Avoid Using Exceptions to Control Flow It can be tempting to use exceptions to manage the flow of execution in complex methods. This is generally not a good idea. Exceptions are expensive to generate and handle. Therefore, if you were to use them to control execution flow within a method that is at the heart of your application, your performance will likely degrade. Secondly, it trivializes the nature of excep- tions in the first place. The point is to indicate an exceptional condition in a way that you can handle or report it cleanly. Programmers can be rather lazy when it comes to handling error conditions. You’ve probably seen code where the programmer didn’t bother to check the return value of an API function or method call. Exceptions provide a syntactically succinct way to indicate and han- dle error conditions without littering your code with a plethora of If…Then blocks and other traditional (nonexception-based) error-handling constructs. At the same time, the runtime supports exceptions, and it does a lot of work on your behalf when exceptions are thrown. Unwinding the stack is no trivial task in and of itself. Lastly, the point where an exception is thrown and the point where it’s handled can be dis- jointed and have no connection to each other. Thus, it can be difficult when reading code to determine where an exception will get caught and handled. These reasons alone are enough for you to stick to traditional techniques when managing normal execution flow. Mechanics of Handling Exceptions in VB 2005 If you’ve ever used exceptions in other C-style languages such as C++, Java, or even C/C++ using the Microsoft structured exception-handling extensions (__try/__catch/__finally), then you’re already familiar with the basic syntax of exceptions in Visual Basic (VB). In that case, you may find yourself skimming the next few sections or treating the material as a refresher. Throwing Exceptions The act of throwing an exception is actually quite easy. You simply execute a Throw statement where the parameter to the Throw statement is the exception you would like to throw. For example, suppose you’ve written a custom collection class that allows users to access items by index, and you’d like to notify users when an invalid index is passed as a parameter. You could throw an ArgumentOutOfRange exception, such as in the following code: Public Class MyCollection Private Count As Integer Public Function GetItem(ByVal index As Integer) As Object If index < 0 OrElse index >= Count Then Throw New ArgumentOutOfRangeException() 801-6CH09.qxd 3/3/07 3:02 AM Page 154 End If End Function End Class The runtime can also throw exceptions as a side effect to code execution. An example of a system-generated exception is NullReferenceException, which occurs if you attempt to access a field or call a method on an object when, in fact, the reference to the object doesn’t exist. Unhandled Exceptions in .NET 2.0 When an exception is thrown, the runtime begins to search up the stack for a matching Catch block for the exception. As it walks up the execution stack, it unwinds the stack at the same time, cleaning up each frame along the way. If the search ends in the last frame for the thread, and it still finds no handler for the exception, the exception is considered unhandled at that point. In .NET 2.0, any unhandled exception, except AppDomainUnloadException and ThreadAbortException, causes the thread to terminate. It sounds rude, but in reality, this is the behavior you should want from an unhan- dled exception. After all, it’s an unhandled exception. Now that the thread terminates as expected, a big red flag is raised at the point of the exception that allows you to find the prob- lem immediately and fix it. This is a good thing, as you want errors to present themselves as soon as possible and never let the system keep running as if everything were normal. ■Note You can install an unhandled exception filter by registering a delegate with AppDomain.UnhandledException. When an unhandled exception comes up through the stack, this delegate will get called and it will receive an instance of UnhandledExceptionEventArgs. Syntax Overview of the Try Statement The code within a Try block is guarded against exceptions such that, if an exception is thrown, the runtime will search for a suitable Catch block to handle the exception. Whether a suitable Catch block exists or not, if a Finally block is provided, the Finally block will always execute no matter how execution flow leaves the Try block. Let’s look at an example of a Try statement: Imports System Imports System.Collections Imports System.Runtime.CompilerServices Public Class EntryPoint Shared Sub Main() Try Dim list As ArrayList = New ArrayList() list.Add(1) Console.WriteLine("Item 10 = {0}", list(10)) Catch x As ArgumentOutOfRangeException CHAPTER 9 ■ EXCEPTION HANDLING 155 801-6CH09.qxd 3/3/07 3:02 AM Page 155 Console.WriteLine("=== ArgumentOutOfRangeException Handler ===") Console.WriteLine(x) Console.WriteLine("=== ArgumentOutOfRangeException Handler ===") Catch x As Exception Console.WriteLine("=== Exception Handler ===") Console.WriteLine(x) Console.WriteLine("=== Exception Handler ===") Finally Console.WriteLine(Chr(13) + Chr(10) + "Cleaning up ") End Try End Sub End Class Once you see the code in the Try block, you know it is destined to throw an ArgumentOutOfRange exception. Once the exception is thrown, the runtime begins searching for a suitable Catch clause that is part of this Try statement and matches the type of the excep- tion as best as possible. Clearly, the first Catch clause is the one that fits best. Therefore, the runtime will immediately begin executing the statements in this Catch block. We could have left off the declaration of the exception variable x in the Catch clause and only declared the type, but we wanted to demonstrate that exception objects produce a nice stack trace that can be useful during debugging. The second Catch clause will catch exceptions of the general Exception type. Should the code in the Try block throw an exception derived from System.Exception other than ArgumentOutOfRangeException, then this Catch block would handle it. Multiple Catch clauses associated with a single Try block must be ordered such that more specific exception types are listed first. The compiler won’t compile code where more general Catch clauses are listed before more specific Catch clauses. You can verify this by swapping the order of the first two Catch clauses in the previous example. And finally (no pun intended), there is the Finally block. No matter how the Try block is exited, the Finally block will always execute. If there is a suitable Catch block in the same frame as the Finally block, it will execute before the Finally block. You can see this by looking at the output of the previous code example, which looks like the following: === ArgumentOutOfRangeException Handler === System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index at System.Collections.ArrayList.get_Item(Int32 index) at Exceptions.EntryPoint.Main() in C:\Accelerated VB 2005\Projects\Exceptions\Exception2.vb:line 10 === ArgumentOutOfRangeException Handler === Cleaning up CHAPTER 9 ■ EXCEPTION HANDLING156 801-6CH09.qxd 3/3/07 3:02 AM Page 156 Rethrowing Exceptions and Translating Exceptions Within a particular stack frame, you may find it necessary to catch all exceptions or a specific subset of exceptions long enough to do some cleanup and then rethrow the exceptions in order to let them continue to propagate up the stack. To do this, you use the Throw statement with no parameter, as follows: Imports System Imports System.Collections Public Class Entrypoint Shared Sub Main() Try Try Dim list As ArrayList = New ArrayList() list.Add(1) Console.WriteLine("Item 10 = {0}", list(10)) Catch ex As ArgumentOutOfRangeException Console.WriteLine("Do some useful work and then rethrow") 'Rethrow caught exception. Throw Finally Console.WriteLine("Cleaning up ") End Try Catch Console.WriteLine("Done") End Try End Sub End Class Note that any Finally blocks associated with the exception frame that the Catch block is associated with will execute before any higher-level exception handlers are executed. You can see this in the output from the previous code: Do some useful work and then rethrow Cleaning up Done The “Achieving Exception Neutrality” section introduces some techniques that can help you avoid having to catch an exception, do some cleanup, and then rethrow the exception. That sort of work flow is cumbersome, since you must be careful to rethrow the exception appropriately. If you accidentally forget to rethrow, things could get ugly, since you would not likely be remedying the exceptional situation. The techniques introduced will help you achieve the goal of only placing a Catch block where correctional action can occur. CHAPTER 9 ■ EXCEPTION HANDLING 157 801-6CH09.qxd 3/3/07 3:02 AM Page 157 Sometimes, you may find it necessary to “translate” an exception within an exception handler. In this case, you catch an exception of one type, but you throw an exception of a different, possibly more precise, type in the Catch block for the next level of exception handlers to deal with. Consider the following example: Imports System Imports System.Collections Public Class MyException Inherits Exception Public Sub New(ByVal reason As String, ByVal inner As Exception) MyBase.New(reason, inner) End Sub End Class Public Class Entrypoint Shared Sub Main() Try Try Dim list As ArrayList = New ArrayList() list.Add(1) Console.WriteLine("Item 10 = {0}", list(10)) Catch x As ArgumentOutOfRangeException Console.WriteLine("Do some useful work and then rethrow") Throw New MyException("I'd rather throw this", x) Finally Console.WriteLine("Cleaning up ") End Try Catch x As Exception Console.WriteLine(x) Console.WriteLine("Done") End Try End Sub End Class One special quality of the System.Exception type is its ability to contain an inner excep- tion reference via the Exception.InnerException property. This way, when the new exception is thrown, you can preserve the chain of exceptions for the handlers that process them. We recommend you use this useful feature of the standard exception type of VB when you trans- late exceptions. The output from the previous code is as follows: CHAPTER 9 ■ EXCEPTION HANDLING158 801-6CH09.qxd 3/3/07 3:02 AM Page 158 [...]... System.Collections.ArrayList.get_Item(Int32 index) at Exceptions.Entrypoint.Main() in C: \Accelerated VB 20 05\ Projects\Exceptions\Exception4 .vb: line 18 - End of inner exception stack trace at Exceptions.Entrypoint.Main() in C: \Accelerated VB 20 05\ Projects\Exceptions\Exception4 .vb: line 22 Done Keep in mind that you should avoid translating exceptions if possible The more you catch and then rethrow within a... AM Page 159 CHAPTER 9 ■ EXCEPTION HANDLING Do some useful work and then rethrow Cleaning up Exceptions.MyException: I'd rather throw this -> System.ArgumentOutOfRangeException: Index was out of range Must be non-negative and less than the size of the collection Parameter name: index at System.Collections.ArrayList.get_Item(Int32 index) at Exceptions.Entrypoint.Main() in C: \Accelerated VB 20 05\ Projects\Exceptions\Exception4 .vb: line... End Class 159 801-6CH09.qxd 160 3/3/07 3:02 AM Page 160 CHAPTER 9 ■ EXCEPTION HANDLING The first exception is simply lost, and the new exception is propagated up the stack Clearly, this is not desirable You never want to lose track of exceptions, because it becomes virtually impossible to determine what caused an exception in the first place Exceptions Thrown in Finalizers Destructors in VB are not... 801-6CH09.qxd 3/3/07 3:02 AM Page 177 CHAPTER 9 ■ EXCEPTION HANDLING End Get End Property End Class Public Class EntryPoint Shared Sub Main() Dim anEmployeeSSN As String = New String("123 456 78") Dim anEmployeeBD As Date = New Date(1 959 , 3, 12) Dim anEmployee As Employee = New Employee() Try anEmployee.Add(anEmployeeSSN, anEmployeeBD) Catch e As EmployeeVerificationException Console.WriteLine(e.Message + Chr(10)... got it Once that succeeds, you can proceed to add the Employee to the TerminatedEmployees list However, if that fails for some reason, you need to put the Employee back into the ActiveEmployees list 1 65 801-6CH09.qxd 166 3/3/07 3:02 AM Page 166 CHAPTER 9 ■ EXCEPTION HANDLING You may have already spotted a multitude of problems with this approach First of all, what happens if you have a failure to add... SafeBluetoothRadioFindHandle = _ BluetoothFindFirstRadio(New BluetoothFindRadioParams(), RadioHandle) If Not radioFindHandle.IsInvalid Then Dim radioInfo As BluetoothRadioInfo = New BluetoothRadioInfo() radioInfo.dwSize = 52 0 Dim result As UInteger = BluetoothGetRadioInfo(RadioHandle, _ radioInfo) If result = ERROR_SUCCESS Then 'Let's send the contents of the radio info to the console Console.WriteLine("address = {0:X}",... exception to that of the original exception so that the one who catches the TypeInitializationException will at least know why this exception occured in the first place 801-6CH09.qxd 3/3/07 3:02 AM Page 1 75 CHAPTER 9 ■ EXCEPTION HANDLING Finally, the protected constructor allows creation of an exception from a SerializationInfo object You always want to create serializable exceptions so you can use them... value.Length = 9 Then mEmployeeSSN = value Else Throw New EmployeeVerificationException( _ EmployeeVerificationException.Cause.InvalidSSN, _ "Social Security Number must be 9 digits.") End If End Set 1 75 801-6CH09.qxd 176 3/3/07 3:02 AM Page 176 CHAPTER 9 ■ EXCEPTION HANDLING End Property Public Property EmployeeBirthDate() As Date Get Return mEmployeeBirthDate End Get Set(ByVal value As Date) mEmployeeBirthDate... TerminateEmployee() closely, and see where exceptions could get generated The fact is, an exception could be thrown upon execution of any of the methods called by this 801-6CH09.qxd 3/3/07 3:02 AM Page 1 65 CHAPTER 9 ■ EXCEPTION HANDLING method If the index is out of range, then you would expect to see ArgumentOutOfRange exceptions thrown from the first two lines Of course, if the range exception is thrown... Plus, your derived exception can leverage the message and inner exception already encapsulated by System.Exception Working with Allocated Resources and Exceptions One thing you may grapple with in the VB world is the lack of deterministic destruction C++ developers have become accustomed to using constructors and destructors of stack-based objects to manage precious resources This means that you can . C: Accelerated VB 20 05 ProjectsExceptionsException4 .vb: line 18 End of inner exception stack trace at Exceptions.Entrypoint.Main() in C: Accelerated VB 20 05 ProjectsExceptionsException4 .vb: line. C: Accelerated VB 20 05 ProjectsExceptionsException2 .vb: line 10 === ArgumentOutOfRangeException Handler === Cleaning up CHAPTER 9 ■ EXCEPTION HANDLING 156 801-6CH09.qxd 3/3/07 3:02 AM Page 156 Rethrowing. list(10)) Catch x As ArgumentOutOfRangeException CHAPTER 9 ■ EXCEPTION HANDLING 155 801-6CH09.qxd 3/3/07 3:02 AM Page 155 Console.WriteLine("=== ArgumentOutOfRangeException Handler ===") Console.WriteLine(x) Console.WriteLine("===

Ngày đăng: 09/08/2014, 12:22

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