Object-Oriented Programming with Visual Basic .NET
only for RuBoard |
Sometimes you'll just want to keep on trucking after an error has occurred rather than handle it. Example 6-7 demonstrates an unstructured approach to ignoring errors. Normally this code would throw a DivideByZeroException , but here On Error Resume Next is used to prevent exceptions from propagating out of the method.
Example 6-7. On Error Resume Next
'Throws System.DivideByZeroException Public Class App Public Shared Sub Main( ) On Error Resume Next Dim x As Integer = 0 Dim y As Integer = 10 \ x 'Use x and y here End Sub End Class
Many of you know that ignoring errors like this is considered bad practice. Even so (or so you say), sometimes it needs to be done. A safer approach would be to turn off error handling specifically where you need to, and then turn it back on when you are done:
Public Sub SomeMethod( ) On Error Goto errHandler On Error Resume Next ' Code that might trigger errors goes here On Error Goto errHandler Exit Sub errHandler: 'Handle errors here End Sub
There are several arguments against this approach. Handling (or not handling) errors in this way does not produce the most readable code. Program flow jumps all over the place. Also, if this technique is used in a method that contains even a small degree of complexity, debugging could turn into a nightmare.
On Error Resume Next results in inefficient code. It might be one line of code in VB, but you'd be amazed by the IL spaghetti that is generated by the compiler. Remember Example 6-7? It uses On Error Resume Next , declares two integers, and performs a division. Example 6-8 shows the IL produced by this example. We will not even attempt to discuss the listing for this "simple" example. The listing is provided only for its magnitude. Just scan it and remember it the next time you are tempted to turn off error handling in your code.
Example 6-8. Disassembly of Resume Next
// Microsoft (R) .NET Framework IL Disassembler. Version 1.0.3705.0 // Copyright (C) Microsoft Corporation 1998-2001. All rights reserved. .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 1:0:3300:0 } .assembly extern Microsoft.VisualBasic { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: .ver 7:0:3300:0 } .assembly extern System { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 1:0:3300:0 } .assembly extern System.Data { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 1:0:3300:0 } .assembly extern System.Xml { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 1:0:3300:0 } .assembly extern System.Web { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: .ver 1:0:3300:0 } .assembly temp3 { .custom instance void [mscorlib]System.Reflection.AssemblyTrademarkAttribute:: .ctor(string) = ( 01 00 00 00 00 ) // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(bool, // bool) // = ( 01 00 01 01 00 00 ) .custom instance void [mscorlib]System.Runtime.InteropServices.GuidAttribute:: .ctor(string) = ( 01 00 24 38 31 36 37 39 30 37 42 2D 30 35 35 38 // ..67907B-0558 2D 34 38 33 30 2D 41 36 34 30 2D 35 44 31 46 46 // -4830-A640-5D1FF 44 42 43 41 34 43 34 00 00 ) // DBCA4C4.. .custom instance void [mscorlib]System.CLSCompliantAttribute::.ctor(bool) = ( 01 00 01 00 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyProductAttribute:: .ctor(string) = ( 01 00 00 00 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyCopyrightAttribute:: .ctor(string) = ( 01 00 00 00 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyCompanyAttribute:: .ctor(string) = ( 01 00 00 00 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyDescriptionAttribute:: .ctor(string) = ( 01 00 00 00 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyTitleAttribute::.ctor(string) = ( 01 00 00 00 00 ) .hash algorithm 0x00008004 .ver 1:0:830:30783 } .module temp3.exe // MVID: {667EE811-3A5C-4236-8B2C-5703ED42C24C} .imagebase 0x11000000 .subsystem 0x00000003 .file alignment 512 .corflags 0x00000001 // Image base: 0x02df0000 // // ============== CLASS STRUCTURE DECLARATION ================== // .namespace temp3 { .class public auto ansi App extends [mscorlib]System.Object { } // end of class App } // end of namespace temp3 // ============================================================= // =============== GLOBAL FIELDS AND METHODS =================== // ============================================================= // =============== CLASS MEMBERS DECLARATION =================== // note that class flags, 'extends' and 'implements' clauses // are provided here for information only .namespace temp3 { .class public auto ansi App extends [mscorlib]System.Object { .method public specialname rtspecialname instance void .ctor( ) cil managed { // Code size 9 (0x9) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor( ) IL_0006: nop IL_0007: nop IL_0008: ret } // end of method App::.ctor .method public static void Main( ) cil managed { .entrypoint .custom instance void [mscorlib]System.STAThreadAttribute::.ctor( ) = ( 01 00 00 00 ) // Code size 134 (0x86) .maxstack 2 .locals init ([0] int32 x, [1] int32 y, [2] int32 _Vb_t_CurrentStatement, [3] class [mscorlib]System.Exception _Vb_t_Exception, [4] int32 _Vb_t_Resume, [5] int32 _Vb_t_OnError) IL_0000: nop IL_0001: call void [Microsoft.VisualBasic]Microsoft.VisualBasic. CompilerServices.ProjectData::ClearProjectError( ) IL_0006: ldc.i4.1 IL_0007: stloc.s _Vb_t_OnError IL_0009: ldc.i4.1 IL_000a: stloc.2 IL_000b: ldc.i4.0 IL_000c: stloc.0 IL_000d: ldc.i4.2 IL_000e: stloc.2 IL_000f: ldc.i4.s 10 IL_0011: ldloc.0 IL_0012: div IL_0013: stloc.1 IL_0014: leave.s IL_007b IL_0016: ldloc.s _Vb_t_Resume IL_0018: ldc.i4.1 IL_0019: add IL_001a: ldc.i4.0 IL_001b: stloc.s _Vb_t_Resume IL_001d: switch ( IL_0001, IL_0009, IL_000d, IL_0014) IL_0032: leave.s IL_0079 IL_0034: isinst [mscorlib]System.Exception IL_0039: brtrue.s IL_003d IL_003b: br.s IL_0048 IL_003d: ldloc.s _Vb_t_OnError IL_003f: brfalse.s IL_0048 IL_0041: ldloc.s _Vb_t_Resume IL_0043: brtrue.s IL_0048 IL_0045: ldc.i4.1 IL_0046: br.s IL_004b IL_0048: ldc.i4.0 IL_0049: br.s IL_004b IL_004b: endfilter IL_004d: castclass [mscorlib]System.Exception IL_0052: dup IL_0053: call void [Microsoft.VisualBasic]Microsoft.VisualBasic. CompilerServices.ProjectData::SetProjectError(class [mscorlib]System.Exception) IL_0058: stloc.3 IL_0059: ldloc.s _Vb_t_Resume IL_005b: brfalse.s IL_005f IL_005d: leave.s IL_0079 IL_005f: ldloc.2 IL_0060: stloc.s _Vb_t_Resume IL_0062: ldloc.s _Vb_t_OnError IL_0064: switch ( IL_0073, IL_0075) IL_0071: leave.s IL_0077 IL_0073: leave.s IL_0077 IL_0075: leave.s IL_0016 IL_0077: rethrow IL_0079: ldloc.3 .try IL_0001 to IL_0034 filter IL_0034 handler IL_004d to IL_0079 IL_007a: throw IL_007b: nop IL_007c: ldloc.s _Vb_t_Resume IL_007e: brfalse.s IL_0085 IL_0080: call void [Microsoft.VisualBasic]Microsoft.VisualBasic. CompilerServices.ProjectData::ClearProjectError( ) IL_0085: ret } // end of method App::Main } // end of class App // ============================================================= } // end of namespace temp3 //*********** DISASSEMBLY COMPLETE *********************** // WARNING: Created Win32 resource file C:\Programming VB.NET Objects\book - version 2\ Example6-7.res
The best way to write code that needs to resume is to use structured error handling in place of outdated , unstructured approaches. Anywhere you need to resume from an error, use nested Try blocks instead. These blocks result in more code, but the code is more readable, easier to debug, and flows nicely from top to bottom:
Try 'Resume #1 Try 'Resume if this code fails Catch e As Exception 'Handle error Finally 'Clean up End Try 'Resume #2 Try 'Resume if this code fails Catch e As Exception 'Handle error Finally 'Clean up End Try Catch e As Exception 'Handle error Finally 'Clean up End Try
only for RuBoard |