VB .NET Language

Introduction

Variables and Data Types

Introduction to Object-Oriented Programming.

The .NET Framework: General Concepts

The .NET Framework Class Library

Delegates and Events

Error Handling in VB .NET  

The Language Reference .. 135

The VB .NET Command-Line Compiler

 Language Elements Not Supported by VB .NET 667

F. VB 6 Language Elements Not Supported by VB .NET

Microsoft Visual Basic began its life just over ten years ago as a kind of amalgamation of Microsoft'sQBasic programming language and a graphical interface design program developed in part by Alan Cooper. Since then, it has become by far the most popular programming language in the world, within installed base that is estimated at five to eight million developers worldwide. The tenth anniversary of Visual Basic coincides with the introduction of Microsoft's new .NET platform, and with a totally revised and revamped version of VB named Visual Basic .NET. The language has been streamlined and modernized, and many old "compatibility" elements have been dropped from the language, while other language elements that were implemented as statements are now either

functions or procedures.

In addition, many of you will be glad to hear that Visual Basic is now a fully object-oriented programming language, with the inclusion of the long sought-after class inheritance, as well as other

OOP features.

We suspect that many of you will greet with mixed emotions, as do we, the fact that Microsoft's Component Object Model (COM), the technology that was at the core of Visual Basic since the release of Version 4.0, has been abandoned in favor of the .NET platform. On the one hand, we find this to bea great relief, because COM can be so complex and confusing. On the other hand, we find this somewhat irritating, because we have invested so much time and effort in learning and using COM. Finally, we find this change somewhat frightening; who knows what pitfalls await us as we become more familiar with this new technology? The best news of all is that, whereas in the past, Visual Basic served as a "wrapper" that simplified and hid much of the complexity of Windows and the Windows operating system, at long last Visual Basic is an "equal player" in the .NET Framework; Visual Basic programmers have full and easy access to the features of the .NET platform, just as Visual C++ and C# programmers do. The extensive changes to the language and the introduction of the .NET platform make a reference guide to the Visual Basic language more essential than ever. At the same time, they make it easy to delineate this book's subject matter. This is a book that focuses on the language elements of Visual Basic .NET? on its statements, functions, procedures, directives, and objects (notably the Err and

Collection objects).

While it's important to emphasize that this book focuses on the Visual Basic language components for the .NET platform, it's also important to emphasize what this book is not:

· It is not a reference guide to Visual Basic for Applications (VBA), the programming language used in all of the major applications in the Microsoft Office suite, as well as in dozens of other third-party applications. As you probably know, VBA is the programming language in previous versions of Visual Basic and in the major Office applications. However, VBA is not the programming language for VB .NET. Indeed, until VB .NET is incorporated into a release of Microsoft Office for .NET, the two languages will differ significantly.

· It is not a reference guide to the .NET Base Class Library (the basic set of services provided by the .NET Framework) or to the .NET Framework Class Library (which consists of the Base Class Library supplemented by the application services provided by the .NET Framework). To be sure, the Framework Class Library is discussed in these pages, and a number of its classes and their members are documented in the book's reference section. But that documentation just scratches the surface; the Framework Class Library consists of over 90namespaces (one of which, incidentally, is Microsoft.VisualBasic, the namespace that defines the objects of the Visual Basic language), several thousand types (classes, interfaces, delegates, and enumerations), and an enormous number of members. In selecting the .NET Framework classes to document in this book, we've tried to focus on .NET elements that replace commonly used features in previous versions of Visual Basic, as well as on .NET elements that expand and enhance the functionality of existing Visual Basic .NET elements insignificant ways.

· It is not a guide to developing applications or components using Visual Basic .NET. In documenting the language, we'll show you some simple code fragments that illustrate the relevant issues and show you how a language element works. On the other hand, we won't show you, for example, how to use the Windows Forms package to build a Windows application, how to develop a web application using ASP.NET, or how to implement a web service.

 Introduction

Since its introduction in 1991, Microsoft Visual Basic has enjoyed unprecedented success. In fact, in slightly more than a decade, it has become the world's most widely used programming language, with an installed base of somewhere between three and five million developers (depending on the particular source you use and whether the estimate includes only the retail versions of the Visual Basic product or the hosted version of Visual Basic for Applications (VBA) as well). The reason for this success is twofold. First, Visual Basic has excelled as a rapid application development (RAD) environment for corporate and commercial applications. Second, Visual Basic offers a programming language and development environment noted for its simplicity and ease of use, making it an extremely attractive choice for those new to programming. With the release of its new .NET platform, Microsoft also released a new version of the Visual Basic language, Visual Basic .NET. VB .NET is a from-the-ground-up rewrite of Visual Basic that not only adds a number of new features, but also differs significantly from previous versions of Visual Basic. From a high-level view, two of these differences are especially noteworthy:

· Until the release of VB .NET, Microsoft focused on creating a unified version of VBA, the language engine used in Visual Basic, which could serve as a "universal batch language" for Windows and Windows applications. With Version 6 of Visual Basic, this goal was largely successful: VB 6.0 featured VBA 6.0, the same language engine that drives the individual applications in the Microsoft Office 2000 suite, Microsoft Project, Microsoft FrontPage, Microsoft Visio, and a host of popular third-party applications such as Autodesk's AutoCAD and Corel's WordPerfect Office 2000. With the release of VB .NET, this emphasis on a unified programming language has, for the moment at least, faded into the background, as the hosted version of Visual Basic continues to be VBA rather than VB .NET.

· Since Version 4, Visual Basic had increasingly been used as a kind of "glue language" to access COM components and their object models, such as ActiveX Data Objects (ADO), Collaborative Data Objects (CDO), or the Outlook object model. Although VB .NET supports COM for reasons of "backward compatibility," VB .NET is designed primarily to work with the .NET Framework rather than with COM. You may be wondering why Microsoft would totally redesign a programming language and development environment that is so wildly successful. As we shall see, there is some method to this madness.

1.1 Why VB .NET?

When Visual Basic was introduced in 1991, Windows 3.0 was a fairly new operating system in need ofapplication and utility software. Although Windows 3.0 itself had proven successful, the graphicalapplications that offered native support for Windows—and upon whose release the ultimate success orfailure of Windows would depend—were slow in coming. The major problem was that C and C++programmers, who had produced the majority of applications for the MS-DOS operating system, werefaced with a substantial learning curve in writing Windows applications and adapting to Windows'event-driven programming model.The introduction of Visual Basic immediately addressed this problem by offering a programming modelthat was thoroughly consistent with Windows' graphical nature. Although Windows marked a radicalchange in the way programs were written, C and C++ programmers continued to produce code asthey always had: a text editor was used to write source code, the source code was compiled into anexecutable, and the executable was finally run under Windows. Visual Basic programmers, on theother hand, worked in a programming environment that its critics derisively labeled a "drawingprogram." Visual Basic automatically created a form (or window) whenever the developer began a newproject. The developer would then "draw" the user interface by dragging and dropping controls from atoolbox onto the form. Finally, the developer would write code snippets that responded to particularevents (such as the window loading or the window being resized). In other words, Visual Basic's initialsuccess was due to its ease of use, which in turn reflected that Visual Basic offered a graphicalprogramming environment that was entirely consistent with the graphical character of Windows itself.To get some sense of the revolutionary character of Visual Basic, it is instructive to compare a simple"Hello World" program for Windows 3.0 written in C (see Example 1-1) with one written in VisualBasic (see Example 1-2). While the former program is over two pages long, its Visual Basiccounterpart takes only three lines of code—and two of them are provided automatically by the VisualBasic environment itself.

Example 1-1. "Hello World" in C

// "Hello World" example//// The user clicks a command button, and a "Hello World"// message box appears.#include <windows.h>LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow){static char szAppName[] = "SayHello" ;HWND hwnd ;MSG msg ;WNDCLASSEX wndclass ;wndclass.cbSize = sizeof (wndclass) ;wndclass.style = CS_HREDRAW | CS_VREDRAW ;wndclass.lpfnWndProc = WndProc ;wndclass.cbClsExtra = 0 ;wndclass.cbWndExtra = 0 ;wndclass.hInstance = hInstance ;wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION) ;wndclass.hCursor = LoadCursor(NULL, IDC_ARROW) ;wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH) ;

35

wndclass.lpszMenuName = NULL ;wndclass.lpszClassName = szAppName ;wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION) ;RegisterClassEx(&wndclass) ;hwnd = CreateWindow(szAppName, "Hello World",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ;ShowWindow(hwnd, iCmdShow) ;UpdateWindow(hwnd) ;while (GetMessage(&msg, NULL, 0, 0)){TranslateMessage(&msg) ;DispatchMessage(&msg) ;}return msg.wParam ;}LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam,LPARAM lParam){int wNotifyCode ;HWND hwndCtl ;static HWND hwndButton ;static RECT rect ;static int cxChar, cyChar ;HDC hdc ;PAINTSTRUCT ps ;TEXTMETRIC tm ;switch (iMsg){case WM_CREATE :hdc = GetDC(hwnd) ;SelectObject(hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;GetTextMetrics(hdc, &tm) ;cxChar = tm.tmAveCharWidth ;cyChar = tm.tmHeight + tm.tmExternalLeading ;ReleaseDC(hwnd, hdc) ;GetClientRect( hwnd, &rect ) ;hwndButton = CreateWindow("BUTTON", "&Say Hello",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,(rect.right-rect.left)/20*9,(rect.bottom-rect.top)/10*4,14 * cxChar, 3 * cyChar,(HWND) hwnd, 1,((LPCREATESTRUCT) lParam) -> hInstance, NULL) ;return 0 ;case WM_SIZE :rect.left = 24 * cxChar ;rect.top = 2 * cyChar ;rect.right = LOWORD (lParam) ;rect.bottom = HIWORD (lParam) ;

36

return 0 ;case WM_PAINT :InvalidateRect(hwnd, &rect, TRUE) ;hdc = BeginPaint(hwnd, &ps) ;EndPaint(hwnd, &ps) ;return 0 ;case WM_DRAWITEM :case WM_COMMAND :wNotifyCode = HIWORD(wParam) ;hwndCtl = (HWND) lParam ;if ((hwndCtl == hwndButton) && (wNotifyCode == BN_CLICKED))MessageBox(hwnd, "Hello, World!", "Greetings", MB_OK) ;ValidateRect(hwnd, &rect) ;break ;case WM_DESTROY :PostQuitMessage (0) ;return 0 ;}return DefWindowProc (hwnd, iMsg, wParam, lParam) ;}

Example 1-2. "Hello World" in Visual Basic

Private Sub Command1_Click( )MsgBox "Hello, World", vbOKOnly Or vbExclamation, "Hi!"End Sub

While Version 1.0 of Visual Basic was relatively underpowered, Microsoft displayed a firm commitmentto Visual Basic and worked very hard to increase its power and flexibility with each new release. Bythe time Version 3.0 was released, Visual Basic offered a programming paradigm that was completelyintuitive, making it easy for novice programmers to get started and produce simple applications veryquickly. At the same time, particularly through its ability to access the Windows ApplicationProgramming Interface (API) and through its support for add-on controls, Visual Basic had become aprogramming tool capable of creating applications of considerable sophistication and complexity.Like VB .NET, Visual Basic Version 4.0, which was released in 1995 to support Microsoft's 32-bitfamily of operating systems, was a complete rewrite of Visual Basic. It featured limited support forobject-oriented programming in the form of class modules (CLS files) and the ability to generate notonly Windows executables, but ActiveX DLLs (also known as COM components) as well.In the periods shortly before and after the release of VB 4, the character of programming changeddramatically. The rise of the Internet as an application platform meant that standalone Windowsapplications were becoming less and less necessary. The increased prominence of distributedapplications that assumed the presence of the Internet marked another change in programmingparadigms. Yet, Visual Basic's real strength remained as it always had been: a great platform fordeveloping standalone Windows applications.This disparity between Visual Basic's strengths and the prevailing programming paradigm, whichemphasized distributed applications and the Internet, created something of a contradiction. On the onehand, Visual Basic excelled at graphically depicting the Windows interface. On the other hand,developers were creating fewer and fewer Windows interfaces. Instead, they were now using Visual

37

Basic primarily to write source code that would eventually be compiled into middle-tier components.Ironically, a programming environment whose real strength and point of departure was its graphicalcharacter was now being used as a text editor, in very much the same way the first generation ofWindows programmers used text editors to create C source code for Windows applications.Moreover, as the popularity of the Internet grew, it became clearer that Visual Basic was not aparticularly good platform for developing Internet applications. With VB 6, Microsoft introduced WebClasses as the preferred technology for Internet application development. Yet, the metaphorpresented by Web Classes (which focused on separating a web application's presentation from itsprogrammatic functionality) was confusing to developers, and as a result, Web Classes never becamepopular. While VB remained critically important for developing middle-tier components for distributedapplications, both it and the Visual Basic community that grew up around it remained strangelyisolated from the Internet as an application platform.Numerous detractors have labeled VB .NET as an entirely new language with little relationship toprevious versions of Visual Basic?a dubious innovation foisted on the Visual Basic community byMicrosoft in an attempt to sell a new version of its development products. However, we don't agree.Instead, we view the introduction of VB .NET as a logical and even necessary step forward in thedevelopment of Visual Basic as a premier programming language. The goal of VB .NET is to addressthe limitations of Visual Basic as a development environment and bring it into the Internet age so that itcan remain the major platform for developing applications of all kinds. Very much like Visual Basic 1.0offered a graphical interface that was suitable for Windows applications, VB .NET and VisualStudio .NET aim at providing a graphical interface that is suitable for developing web applications andfor taking full advantage of the Internet as an application-development platform, as well as fordeveloping Windows applications and components.

1.2 What Is VB .NET?

VB .NET is a programming language designed to create applications that work with Microsoft'snew .NET Framework. The .NET platform in turn aims at addressing many of the limitations of"classic" COM, Microsoft's Component Object Model, which provided one approach toward applicationand component interoperability. These limitations included type incompatibilities when calling COMcomponents, versioning difficulties ("DLL hell") when developing new versions of COM components,and the need for developers to write a certain amount of code (mostly in C++) to handle the COM"plumbing." In contrast to VB, with its reliance on COM, VB .NET offers a number of new features andadvantages. Let's take a look at some of these.

1.2.1 Object Orientation

With the release of Version 4, Visual Basic added support for classes and class modules and in theprocess became an object-oriented programming language. Yet the debate persists about whetherVisual Basic is a "true" object-oriented language or whether it only supports limited features of objectorientation.The debate centers around Visual Basic's support for inheritance, an object-oriented programmingconcept that allows a class to derive its properties and its functionality from another class. Proponentsof the view that Visual Basic is object-oriented point to Visual Basic's support for interface-basedprogramming and the use of virtual base classes. Yet relatively few VB programmers take advantageof interface-based programming. And interface-based programming itself does not allow a derivedclass to inherit the functionality of a base class; only virtual base classes can be inherited using the

Implements keyword.While the object-oriented character of previous versions of VB may be in doubt, there is no questionthat VB .NET is an object-oriented programming language. In fact, even if VB .NET is used to writewhat appears to be procedural code, it is object-oriented "under the hood," so to speak. Let's take as asimple example the clearly procedural, nonobject-oriented program shown in Example 1-3. If we useILDASM (.NET's intermediate language disassembler) to look at the IL generated for this source code

38

(see Figure 1-1), we see that internally, modMain is in fact defined as a class that has two methods,Increment and Main.

Figure 1-1. A procedural program shown using ILDASMExample 1-3. A procedural program for VB .NET

Public Module modMainPublic Sub Main( )Dim x As Integerx = 10MsgBox(Increment(x))End SubPrivate Function Increment(iVar As Integer)Return(iVar+1)End FunctionEnd Module

1.2.2 A Common Type System

Traditionally, one of the problems of calling routines written in languages from Visual Basic or ofcalling Visual Basic routines from other languages is that such inter-language calls presuppose acommon type system. This is the case when calling Win32 API functions from Visual Basic, but it isalso applies to attempts to call methods in a VB COM component from other languages or to callmethods in a non-VB COM component from VB.For instance, until the addition of the AddressOf operator, which allows us to pass a pointer to afunction or subroutine, there was no way to provide a callback function, which is required by mostWin32 API enumeration functions. As another example, it is expected that members of structurespassed to Win32 API functions be aligned on their natural boundaries, something that VBprogrammers had great difficulty accomplishing.Problems of type compatibility tended to occur most often when scripted applications were used to calland pass arguments to COM components. An excellent example is the attempt to pass an array froma script written in JScript to a COM component, since COM sees JScript arrays as a string of commadelimitedvalues rather than a COM-compatible array (called a SafeArray).The .NET platform removes these difficulties by providing a common type system. Ultimately, all datatypes are either classes or structures defined by or inherited from the .NET Base Class Library. Thiscommon type system means that .NET components will be truly language-independent and thata .NET component written in one language will be seamlessly interoperable with .NET componentswritten in any other .NET language. The problem of incompatible types simply disappears.On the surface, VB has retained its old type system. VB still supports the Long data type, for instance,although it is now a 64-bit data type instead of the 32-bit data type of VB 4 through VB 6. Casualinspection of the code shown in Example 1-4 suggests that VB has retained its type system.

39

However, if we use ILDASM to examine the IL generated from this Visual Basic code, we see that VBdata types are merely wrappers for data types provided by the .NET Framework. (See Figure 1-2.)

Figure 1-2. Wrapping the .NET type systemExample 1-4. Using the Visual Basic type system

Public Module modMainPublic Sub Main( )Dim s As String = "This is a string."Dim l As Long = 12344Dim i As Integer = 10End SubEnd Module

The simple program in Example 1-5 also supports this conclusion. The program instantiates aninteger of type Long, a standard Visual Basic data type. It then calls the ToString method?a method ofthe Int64 class?to convert that number to its string representation. In other words, the variable l in

Example 1-5 is really an Int64 data type masquerading as a traditional VB Long data type.

Example 1-5. Calling .NET type methods from a VB data type

Public Module modMainPublic Sub Main( )Dim l As Long = 64.31245Dim s As Strings = l.ToStringMsgBox(s)End SubEnd Module

1.2.3 Access to System Services: The Framework Class Library

40

Ever since VB added support for calls to routines in the Windows and Win32 APIs, many Visual Basicprogrammers came to regard API programming as a kind of black art. Not only were there a confusingand seemingly limitless array of functions that might be called, but also passing parameters to routinesand receiving their return values often seemed to be a mysterious process. Moreover, with the growingemphasis on object-oriented programming, the Win32 API, with its function-based approach toprogramming, seemed more and more archaic.Although the Declare statement remains in VB and programmers can still call the Win32 API androutines in other external Windows DLLs, many of the common system services provided by theWin32 API, as well as by some COM components, are now provided by the .NET Framework ClassLibrary. The Framework Class Library is a collection of types (classes, structures, interfaces,delegates, and enumerations) organized into namespaces.To get some sense of the difference in programming style between the Win32 API and the .NETFramework Class Library, as well as to appreciate the simplicity and ease with which the FrameworkClass Library can be accessed, compare Examples 1-6 and 1-7. Example 1-6 is a VB 6 routine thatcreates a value entry in the registry to load a particular program on Windows startup. Note that all APIconstants must be defined, as must the API functions themselves.In addition, the API functions must be called correctly. In particular, to avoid passing a BSTR ratherthan a C null-terminated string to the RegSetValueEx function, the string must be passed using the

ByVal keyword. This is a common oversight that usually causes an application crash. In contrast,

Example 1-7 shows the comparable VB .NET code that uses the RegistryKey class in theMicrosoft.Win32 namespace of the Framework Class Library. Note that the code is short and simple,and, therefore, far less error-prone.

Example 1-6. Writing to the registry using the Win32 API

Private Const ERROR_SUCCESS = 0&Private Const HKEY_CLASSES_ROOT = &H80000000Private Const HKEY_CURRENT_CONFIG = &H80000005Private Const HKEY_CURRENT_USER = &H80000001Private Const HKEY_DYN_DATA = &H80000006Private Const HKEY_LOCAL_MACHINE = &H80000002Private Const HKEY_PERFORMANCE_DATA = &H80000004Private Const HKEY_USERS = &H80000003Private Const REG_SZ = 1Private Const KEY_SET_VALUE = &H2Private Declare Function RegCloseKey Lib "advapi32.dll" _(ByVal hKey As Long) As LongPrivate Declare Function RegOpenKeyEx Lib "advapi32.dll" _Alias "RegOpenKeyExA" _(ByVal hKey As Long, ByVal lpSubKey As String, _ByVal ulOptions As Long, ByVal samDesired As Long, _phkResult As Long) As LongPrivate Declare Function RegSetValueEx Lib "advapi32.dll" _Alias "RegSetValueExA" _(ByVal hKey As Long, ByVal lpValueName As String, _ByVal Reserved As Long, ByVal dwType As Long, lpData As Any, _ByVal cbData As Long) As LongPrivate Sub LoadByRegistry( )Const cPGM As String = "C:\Test\TestStartup.exe"Dim hKey As Long, nResult As Long

41

nResult = RegOpenKeyEx(HKEY_CURRENT_USER, _"Software\Microsoft\Windows\CurrentVersion\Run", 0, _KEY_SET_VALUE, hKey)If nResult = ERROR_SUCCESS ThenRegSetValueEx hKey, "MyVBApp", 0, REG_SZ, ByVal cPGM, Len(cPGM)RegCloseKey hKeyEnd IfEnd Sub

Example 1-7. Writing to the registry using the Framework Class Library

Private Const cPGM As String = "C:\VB Forum\startup\TestStartup.exe"Private Shared Sub LoadByRegistry( )Dim oReg As RegistryKey = Registry.CurrentUserDim oKey as RegistryKey = _oReg.OpenSubKey("Software\Microsoft\Windows\CurrentVersion\Run", _True)oKey.SetValue("MyVBApp", cPGM)EndSub

1.2.4 A Common Runtime Environment

Although VB traditionally had shielded the developer from many of the intricacies of Windows as anoperating system or of COM as a method for interoperability, nevertheless, some slight knowledge ofhow the system worked was essential, or the developer was sure to run into trouble sooner or later.For instance, consider the following code fragment for VB 6:

Dim oObj As New cSimpleClassSet oObj = NothingIf oObj Is Nothing Then' Perform cleanupEnd If

Because of an idiosyncrasy of VB, objects declared and instantiated using the New keyword on thesame line of code are not actually created until the first reference to that object. As a result, ourattempt to determine if the object is Nothing instead recreates the object, and our cleanup codenever executes.This, at least, is usually a relatively benign error. Much more pernicious, however, are circular objectreferences, where COM objects hold references to one another and therefore cannot be released,even though they've been set to Nothing in code. This situation creates a memory leak thateventually can result in a General Protection Fault.Under .NET, many problems like these are eliminated because of the .NET platform's CommonLanguage Runtime (CLR). The CLR, as its name clearly implies, provides a variety of services toapplications and processes running under the .NET platform, regardless of the language in which theywere originally written. These services include memory management and garbage collection. Theyalso include a unified system of exception handling, which makes it possible to use the same set ofdebugging tools on all code, regardless of the particular .NET language in which it was written.

42

1.3 What Can You Do with VB .NET?

With its language enhancements and its tight integration into the .NET Framework, Visual Basic is athoroughly modernized language that will likely become the premier development tool for creating awide range of .NET applications. In the past, Visual Basic was often seen as a "lightweight" languagethat could be used for particular kinds of tasks, but was wholly unsuitable for others. (It was oftenargued, sometimes incorrectly, that you couldn't create such things as Windows dynamic link librariesor shell extensions using Visual Basic.) In the .NET Framework, VB .NET emerges as an equal player;Microsoft's claim of language independence?that programming language should be a lifestyle choice,rather than a choice forced on the developer by the character of a project?is realized in the .NETplatform.This means that VB .NET can be used to create a wide range of applications and components,including the following:

· Windows console mode applications

· Standard Windows applications

· Windows services

· Windows controls and Windows control libraries

· Web (ASP.NET) applications

· Web services

· Web controls and web control libraries

· .NET classes and namespaces

· Accessing application object models (such as those of the individual applications in theMicrosoft Office suite) using COM automationMost importantly, for the first time with the release of VB .NET, Visual Basic becomes an all-purposedevelopment environment for building Internet applications, an area in which it has traditionally beenweak. This means that the release of this newest version should revitalize Visual Basic, allowing it toremain the tool of choice for developing state-of-the-art software for the next generation of softwaredevelopment.


Chapter 2. Variables and Data Types

Many programmers take the concept of a variable for granted. In this chapter, we take a close look atvariables and their properties, discussing such things as the scope and lifetime of a variable.

2.1 Variables

A variable can be defined as an entity that has the following six properties:

Name

A variable's name is used to identify the variable in code. In VB .NET, a variable name canstart with a Unicode alphabetic character or an underscore, and can be followed by additionalunderscore characters or various Unicode characters, such as alphabetic, numeric, formatting,or combined characters.

Address

Every variable has an associated memory address, which is the location in memory at whichthe variable's value is stored. Note that in many circumstances, the address of a variable willchange during its lifetime, so it would be dangerous to make any assumptions about thisaddress.

Type

The type of a variable, also called its data type, determines the possible values that thevariable can assume. We discuss data types in detail later in the chapter.

Value

The value of a variable is the contents of the memory location at the address of the variable.This is also sometimes referred to as the r-value of the variable, since it is what really appearson the right side of an assignment statement. For instance, in the code:

Dim i As IntegerDim j As Integeri = 5j = i

the final statement can be read as "assign the value of i to memory at the address of j." Forsimilar reasons, the address of a variable is sometimes called its l-value.

Scope

The scope of a variable determines where in a program that variable is visible to the code.Scope is discussed in detail in Section 2.1.1 later in this chapter.

Lifetime

A variable's lifetime determines when and for how long a particular variable exists. It may ormay not be visible (that is, be in scope) for that entire period. For a detailed discussion oflifetime, see Section 2.1.2 later in this chapter.

2.1.1 Variable Scope

44

Variables (and constants) have a scope, which indicates where in the program the variable isrecognized or visible to the code, that is, where it can be referred to in code.

2.1.1.1 Local variables: block-level and procedure-level scope

If a variable is declared inside a code block (a set of statements that is terminated by an End,

Loop, or Next statement), then the variable has block-level scope ; that is, it is visible only within thatblock.For example, consider the following code:

If x <> 0 ThenDim rec As Integerrec = 1/xEnd IfMsgBox CStr(rec)

In this code, the variable rec is not recognized outside the block in which it is defined, so the finalstatement produces an error.It is important to note that the lifetime of a variable always refers to the entire procedure, even if thevariable's scope is block-level. (We discuss this in Section 2.1.2 later in this chapter.) This impliesthat if a block is entered more than once, a block-level variable will retain its value from the previoustime the block code was executed.A variable declared using the Dim keyword within a Visual Basic procedure but not within a code blockhas procedure-level scope. Its scope consists of the procedure in which it is declared.A variable that has block-level scope or procedure-level scope is called a local variable. One of theadvantages of local variables is that the same name can be used in different procedures withoutconflict, since each variable is visible only to its own procedure. Another is that the memory allocatedto the variable can be released as soon as control leaves the procedure, making our code easier tomaintain.

2.1.1.2 Module-level and project-level scope

There are differences in the way scope is handled for variables declared in the Declarations section ofa standard module and a class module. We restrict our discussion here to standard modules,postponing a discussion of class modules until Chapter 3.We first note that a standard module itself can be declared using one of the access modifiers Public,

Friend, or Private (this is the default). Using such a modifier simply restricts the individualmembers to that level of access at most. Thus, for instance, a Public variable declared in a Friend

module has only Friend scope.

2.1.1.2.1 Private access

A variable declared in the Declarations section of a standard module using the Private accessmodifier has module-level scope; that is, it is visible in the entire module, but nowhere else. Using the

Dim keyword also gives the variable module-level scope, but its use is not as clear and should beavoided for readability sake.

2.1.1.2.2 Friend access

45

A variable declared in the Declarations section of a standard module using the Friend accessmodifier is visible in the entire project and thus has project-level scope. However, it is not visible toother projects.

2.1.1.2.3 Public access

A variable declared in the Declarations section of a Public standard module using the Public

access modifier is visible not only to the project in which it is declared, but also to any external projectthat holds a reference to the project. For instance, consider the following module declared in Project1:

Public Module Module1Public iModulePublic As IntegerFriend iModuleFriend As IntegerEnd Module

If Project2 has a reference to Project1, then we can write:

Project1.Module1.iModulePublic = 100

However, the code:

Project1.Module1.iModuleFriend = 100

generates a "not accessible" syntax error.

2.1.2 Variable Lifetime

Variables also have a lifetime. The difference between lifetime and scope is quite simple: lifetimerefers to when, or at what time during program execution the variable is valid; scope refers to where inthe program the variable is recognized by (visible to) the code.To illustrate the difference, consider the following procedure:

Sub ProcedureA( )Dim LocalVar As Integer = 0Call ProcedureBLocalVar = 1End Sub

Note that LocalVar is a local variable. When the line:

Call ProcedureB

is executed, execution switches to ProcedureB. While the lines of ProcedureB are being executed, thevariable LocalVar is out of scope since it is local to ProcedureA. But it is still valid. In other words,the variable still exists and has a value. It is simply not accessible to the code in ProcedureB. In fact,

ProcedureB could also have a local variable named LocalVar, which would have nothing to do withthe variable of the same name in ProcedureA.Once ProcedureB has completed, execution continues in ProcedureA with the line:

LocalVar = 1

which is a valid instruction, since the variable LocalVar is back in scope.

46

Thus, the lifetime of the local variable LocalVar extends from the moment ProcedureA is entered tothe moment it is terminated, including the period during which ProcedureB is being executed as aresult of the call to this procedure, even though during that period, LocalVar is out of scope.We mention again that the lifetime of a block-level variable is the lifetime of the procedure in which it isdefined.

2.1.2.1 Static variables

We have seen that a variable may go in and out of scope during its lifetime. However, once the lifetimeof a variable expires, the variable is destroyed and its value is lost. It is the lifetime that determines the

existence of a variable; its scope determines its visibility.Thus, consider the following procedures:

Sub ProcedureA( )Call ProcedureBCall ProcedureBCall ProcedureBCall ProcedureBCall ProcedureBEnd SubSub ProcedureB( )Dim x As Integerx = 5. . .End Sub

When ProcedureA is executed, it simply calls ProcedureB five times. Each time ProcedureB is called,the local variable x is created anew and destroyed at the end of that call. Thus, x is created anddestroyed five times.Normally, this is just what we want. However, there are times when we would like the lifetime of a localvariable to persist longer than the lifetime of the procedure in which it is declared. For example, wemay want a procedure to do something special the first time it is called, but not in subsequent times.A static variable is a local variable whose lifetime is the lifetime of the entire program. The following VBcode shows how one might use a static variable:

Sub test( )Static bFirstTime As Boolean = TrueIf bFirstTime ThenDebug.WriteLine("first time")bFirstTime = FalseElseDebug.WriteLine("not first time")End IfEnd Sub

Note that we can initialize a static variable, provided that we do so within the variable declaration. Thefollowing code illustrates this point:

Sub StaticTest( )Static st As Boolean = True ' initialize static variable

47

MsgBox(st)st = FalseEnd SubPrivate Sub button1_Click(ByVal sender As System.Object, _ByVal e As System.EventArgs) Handles button1.ClickStaticTest( )End Sub

The first time we hit the button1 command button, StaticTest displays the message True, because thestatic variable st has been initialized to True. However, all subsequent times we hit the button,

StaticTest returns False. This ability to initialize a static variable was missing and was a veryannoying oversight in earlier versions of VB.We could accomplish the same effect by using a module-level variable to keep a record of whether theprocedure has been called, instead of a static local variable. However, it is considered betterprogramming style to use the most restrictive scope possible, which, in this case, is a local variablewith an "extended" lifetime. This helps prevent accidental alteration of the variable in other portions ofthe code. (Remember that this code may be part of a much larger code module, with a lot of thingsgoing on.)

2.2 Declaring Variables and Constants

A variable declaration is an association of a variable name with a data type. In and of itself, this doesnot imply variable creation. However, for nonobject variables, a variable declaration does create avariable. A declaration such as:

Dim x As Integer

creates an Integer variable named x. We can also write:

Dim x As Integer = New Integer( )

which emphasizes the role of the constructor function for the Integer data type. (The constructor is thefunction that VB .NET uses to create the variable.)When multiple variables are declared on the same line, if a variable is not declared with an explicittype declaration, then its type is that of the next variable with an explicit type declaration. Thus, in theline:

Dim x As Long, i, j, k As Integer, s As String

the variables i, j, and k have type Integer. (In VB 6, the variables i and j would have type Variant,which is VB 6's default data type.)VB .NET permits the initialization of variables in the same line as their declaration (at long last!). Thus,we may write:

Dim x As Integer = 5

to declare an Integer variable and initialize it to 5. Similarly, we can declare and initialize more thanone variable on a single line:

Dim x As Integer = 6, y As Integer = 9

48

Note that in this case, each variable that you declare must explicitly be assigned a data type. Youcannot assign each variable an explicit value without explicitly declaring the data type of each variable.Object variables are declared in the same manner:

Dim obj As MyClass

However, this declaration does not create an object variable, and the variable is equal to Nothing atthis point. Object creation requires an explicit call to the object's constructor, as in:

Dim obj As New MyClass( )

or:

Dim obj As MyClass = New Myclass( )

or:

Dim obj As MyClassobj = New MyClass( )

Variables and constants can be declared with any of the following access modifiers:

· Public

· Private

· Friend

· Protected

· Protected Friend

Note also that the Dim keyword can be used as well, but it often defaults to one of the previouslymentioned access modifiers. This is potentially confusing, so the Dim keyword should only be usedwhen required, as it is for local variables.Access modifiers help to specify the scope and accessibility of the variable. We discuss the meaningof these access variables in detail in Chapter 3.Constant declarations are analogous to variable declarations and have the form:

AccessModifier Const Name As Type = Value

where AccessModifier is one of the access modifiers defined earlier. Note that when OptionStrict is On (the default), all constant declarations must have a declared type.

2.3 Data Types

The .NET Common Language Runtime (CLR) includes the Common Type System (CTS), whichdefines the data types that are supported by the CLR. Thus, each of the languages in the .NETFramework (VB, C#, JScript, and Managed C++) implements a subset of a common set of data types.We say subset because, unfortunately, not all of the CTS types are implemented by VB .NET. Forinstance, the CTS includes some unsigned integer data types that are not implemented in VB.As an aside, it is possible to use the VB-unsupported data types in VB by direct use of thecorresponding Base Class Library class. Here is an example illustrating the ability to use the unsigned16-bit integer data type, whose range of values is 0 to 65,535. Note the use of the ToUInt16 method ofthe Convert class to actually get an unsigned 16-bit integer:

49

Dim ui As UInt16ui = Convert.ToUInt16(65535)MsgBox(ui.ToString)

Thus, the native VB data types are wrappers for the CTS data types. To illustrate, the VB Integer datatype is a wrapper for the Int32 structure that is part of the .NET Framework's System namespace. Oneof the members of the Int32 structure is MaxValue, which returns the maximum value allowed for thisdata type. Thus, even though MaxValue is not officially part of VB .NET (nor is it mentioned in the VBdocumentation), we can write:

Dim i As IntegerMsgBox(i.Maxvalue) ' Displays 2147483647

2.3.1 Value and Reference Types

The types defined in the CTS fall into three categories:

· Value types

· Reference types

· Pointer typesHowever, pointer types are not implemented in VB, so we will not discuss these types.The difference between value and reference types is how variables of the corresponding typerepresent that type. When a value-type variable is defined, as in:

Dim int As Integer = 5

a memory location is set aside to hold the actual data (in this case the number 5). In contrast, when areference-type variable is defined, as in:

Dim obj As New CEmployee

the VB compiler creates the object in memory, but then sets the variable obj to a 4-byte memorylocation that contains the address of the object.In short, value-type variables contain the data, whereas reference-type variables point to the data.The distinction between value type and reference type has several consequences, one of which is inthe way assignments work. To illustrate, consider the following class, which has a single property:

Public Class MyClassPublic Age As ShortEnd Class

and the structure MyStruct, also with a single property:

Structure MyStructPublic Age As ShortEnd Structure

Classes are reference types, whereas structures are value types. Now consider the following code,which is thoroughly commented:

' Declare two class variables and two structure variables.Dim objRef1 As MyClass

50

Dim objRef2 As MyClassDim objValue1 As MyStructDim objValue2 As MyStruct' Instance the class.objRef1 = New MyClass( )' Set the Age property to 20.objRef1.Age = 20' Set the second variable to the first variable.' This is an equating of object *references* because' classes are reference types.objRef2 = objRef1' Set the Age property of objRef2 to 30.objRef2.Age = 30' Check the values of the Age property.Debug.WriteLine(objRef1.Age)Debug.WriteLine(objRef2.Age)' Do the same thing with the structure' Instance the structure.objValue1 = New MyStruct( )' Set the Age property to 20.objValue1.Age = 20' Set the second variable to the first variable.' This is an equating of object *values* because' structures are value types.objValue2 = objValue1' Set the Age property of objValue2 to 30.objValue2.Age = 30' Check the values of the Age property.Debug.Writeline(objValue1.Age)Debug.Writeline(objValue2.Age)

Now, the output is:

30302030

To understand what is happening, we need to realize that the reference assignment:

objRef2 = objRef1

sets both variables to the same value. But that value is the address of the object, and so bothvariables point to the same object. Hence, when we change the Age property using the secondvariable, this change is also reflected in the first variable.On the other hand, the value assignment:

objValue2 = objValue1

causes a second structure to be created, setting the new structure's properties to the same value asthe original structure. Thus, changing one structure's Age property does not affect the other structure'sAge property.Note that the VB Array type is also a reference type. To illustrate, consider the following code:

51

Dim iArray1( ) As Integer = {1, 2, 3}Dim iArray2( ) As IntegeriArray2 = iArray1iArray1(0) = 100msgbox(iArray2(0))

The message box displays 100, indicating that both array variables point to the same array.The String data type is a reference type, implemented by the String class. However, it has somecharacteristics of a value type. To illustrate, consider the following code:

Dim s1, s2 As Strings1 = "String 1"s2 = s1s2 = "String 2"MsgBox(s1)

Since this is a reference type, we would expect the last line to produce the message "String 2", butinstead we get "String 1". The reason can be found in Microsoft's documentation:An instance of String is "immutable" because its value cannot be modified once it has been created.Methods that appear to modify a String actually return a new instance of String containing themodification.Thus, the code:

s2 = s1

points s2 to the same string as s1, as is usual with reference types. Then the attempt to modify thestring in the code:

s2 = "String 2"

does not produce the expected result because strings are immutable. Instead, we get a new stringpointed to by s2, while s1 retains its value.The following code supports this conclusion:

Dim s1, s2 As Strings1 = "String 1"' s2 poitns to same string as s1s2 = s1' Show s2 before any changes to the stringMsgBox(s2) ' Displays "String1"' Change the strings2 = "String 2"' Set s1 to Nothings1 = Nothing' Now s1 is nothing and displays accordinglyMsgBox(s1) ' Displays nothing' s2 is a new stringMsgBox(s2) ' Displays "String 2"

Enjoy!

2.3.2 VB Data Types: A Summary

The following lists the data types supported by VB .NET, along with their underlying .NET type,storage requirements, and range of values:

Boolean

.NET CTS type: System.BooleanType: Value (Structure)Storage: 2 bytesValue range: True or False

Byte

.NET CTS type: System.ByteType: Value (Structure)Storage: 1 byteValue range: 0 to 255 (unsigned)

Char

.NET CTS type: System.CharType: Value (Structure)Storage: 2 bytesValue range: A character code from 0 to 65,535 (unsigned)

Date

.NET CTS type: System.DateTimeType: Value (Structure)Storage: 8 bytesValue range: January 1, 1 CE to December 31, 9999

Decimal

.NET CTS type: System.DecimalType: Value (Structure)Storage: 12 bytesValue range: +/-79,228,162,514,264,337,593,543,950,335 with no decimal point; +/-7.9228162514264337593543950335 with 28 places to the right of the decimal; smallestnonzero number is +/-0.0000000000000000000000000001

53

Double (double-precision floating point)

.NET CTS type: System.DoubleType: Value (Structure)Storage: 8 bytesValue range: -1.79769313486231E308 to -4.94065645841247E-324 for negative values;4.94065645841247E-324 to 1.79769313486232E308 for positive values

Integer

.NET CTS type: System.Int32Type: Value (Structure)Storage: 4 bytesValue range: -2,147,483,648 to 2,147,483,647

Long (long integer)

.NET CTS type: System.Int64Type: Value (Structure)Storage: 8 bytesValue range: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807

Object

.NET CTS type: System.ObjectType: Reference (Class)Storage: 4 bytesValue range: Any type can be stored in an Object variable.

Short

.NET CTS type: System.Int16Type: Value (Structure)Storage: 2 bytesValue range: -32,768 to 32,767

Single (single precision floating point)

.NET CTS type: System.Single

54

Type: Value (Structure)Storage: 4 bytesValue range: -3.402823E38 to -1.401298E-45 for negative values; 1.401298E-45 to3.402823E38 for positive values

String (variable-length)

.NET CTS type: System.StringType: Reference (Class)Storage: 10 bytes + (2 * string length)Value range: 0 to approximately 2 billion Unicode characters

User-Defined Type (structure)

.NET CTS type: (inherits from System.ValueType)Type: Value (Structure)Storage: Sum of the sizes of its membersValue range: Each structure member has range determined by its data type and isindependent of the ranges of the other members.Note that the CTS data types are either structures (which are value types) or classes (which arereference types) and are located within the .NET System namespace.

2.3.3 Simple Data Types in Visual Basic

In this section, we discuss data types in general and VB .NET data types in particular.Simple data types can be classified into groups as follows. Note that these groups are not mutuallyexclusive:

Numeric data type

A data type in which the underlying set is a set of numbers and for which the set of operationsincludes the arithmetic operations.

Integer data type

A numeric data type in which the underlying set is a set of integers. (As we will see, VB hasseveral integer data types.)

Floating-point data type

A noninteger data type whose underlying set is a subset of the rational numbers.

Boolean data type

A data type whose underlying set has size 2. This set is usually thought of as {True, False}.

55

Character data type

A data type whose underlying set is a set of characters. Of course, each value must berepresented in memory as a binary string, which can also be interpreted as a number.Nevertheless, this interpretation is not part of a character data type.Let us consider the Visual Basic .NET data types individually.

2.3.3.1 Boolean data type

The Boolean is a 16-bit data type that can only represent two values: True and False. The VBkeywords True and False are used to assign these values to a Boolean variable.When a numeric value is converted to Boolean, any nonzero value is converted to True, and zero isconverted to False. In the other direction, False is converted to zero, and True is converted to -1.(Incidentally, in C, C#, and C++, True is converted to 1. This change was made in Beta 1 of VB .NETto bring it in line with the other languages, but was subsequently changed back in Beta 2.)The underlying .NET data type for Boolean is System.Boolean.

2.3.3.2 Byte data type

The Byte data type is an 8-bit unsigned data type whose range is the set of integers from 0 to 255.According to the documentation, the Byte data type "is used for containing binary data." Since ordinaryarithmetic operations can be used with Byte variables, the data type is, in this sense, an integer datatype. Also, there do not appear to be any special operators, such as shift operators, that would givethe type a "binary data" flavor. Oh well.The underlying .NET data type for Byte is System.Byte.

2.3.3.3 Char data type

The Char data type is a 16-bit character data type with a character code ranging from 0 to 65,535,which represent a single Unicode character. As a data type, Char is new to VB .NET; there was noequivalent in previous versions of Visual Basic.It is important not to confuse the Char and String data types. (We discuss this data type in the "Stringdata type" section.) A string consisting of a single character is not the same as a Char. To illustrate,consider defining a new string and initializing it to a sequence consisting of a repeated singlecharacter, for example, "AAAAA." In earlier versions of VB, this was done as follows:

Dim s As Strings = String$(5, "A")

In VB .NET, this is done using the String class constructor, which has the syntax:

Dim variable As New String(Character, Integer)

If we turn strict type checking on with the Option Strict On statement, the code:

Dim s As New String("A",5)

produces the error message, "Option Strict disallows implicit conversions from String to Char."To get a Char, we must append a c to the end of the string literal. Thus, the following works:

56

Dim s As New String("A"c, 5)

The underlying .NET data type for Char is System.Char.

2.3.3.4 Date data type

Date values are stored as IEEE 64-bit long integers that can represent dates in the range January 1,0001 to December 31, 9999 (which should be plenty), and times from 0:00:00 to 23:59:59.Literal strings must be enclosed in number signs (#) to be recognized as dates. The VB .NET compilerchanges date formats automatically. For instance, if we enter the code:

Dim d As Dated = #November 9, 1948#Msgbox(d)

the compiler changes the second line to:

d = #11/9/1948#

or whatever the regional settings on the host system dictate. The .NET equivalent of Date isSystem.DateTime.

2.3.3.5 Decimal data type

Values of the Decimal data type are stored as 96-bit (12-byte) signed integers, along with an internalscale factor ranging from 0 to 28, which is applied automatically when we set a value for a Decimalvariable. This allows us to enter values from a number of different ranges.For instance, we can use integers (no decimal part) in the range:+/-79,228,162,514,264,337,593,543,950,335in which case the scale factor is set to 0. On the other extreme, we can use values in the range:-7.9228162514264337593543950335 to -0.0000000000000000000000000001on the negative side, or:0.0000000000000000000000000001 to 7.9228162514264337593543950335on the positive side. In this case, the scale factor is set to 28.To write a literal Decimal, append a D, as in:

123456.789D

The type identifier for Decimal is the symbol @, as in:

Dim dec@

The underlying .NET data type for Decimal is System.Decimal. This class has some useful members,such as MaxValue and MinValue, which give the maximum and minimum values of the decimal type.

57

By the way, in previous versions of VB, the Decimal existed only as a Variant data subtype—therewere no variables of type Decimal.

2.3.3.6 Double data type

Values of type Double are IEEE 64-bit (8-byte) floating-point numbers with the range:-1.79769313486231E308 to -4.94065645841247E-324on the negative side, and:4.94065645841247E-324 to 1.79769313486232E308on the positive side.To write a literal Double, we must append an R, as in:

12345.678R

The type identifier for a Double is #, as in:

Dim dbl#

The underlying .NET data type for Double is System.Double.

2.3.3.7 Integer data type

The Integer data type is a 32-bit data type that stores signed integers ranging from:-2^31 to 2^31-1or:-2,147,483,648 to 2,147,483,647Note that this is the native word size on a 32-bit processor, and so the Integer data type providessuperior performance as compared to integer data types of other sizes.Note also that this data type size is new for VB .NET. In VB 6 and earlier, the Integer data type was a16-bit data type.To define a literal Integer, append an I, as in:

123I

The Integer type identifier is the percent sign (%), as in:

Dim int%

The underlying .NET data type for Integer is System.Int32.

2.3.3.8 Long data type

The Long data type is a 64-bit integer data type that stores signed integers ranging from:

58

-2^63 to 2^63-1or:-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807Note that this data type size is new for VB .NET. In VB 6 and earlier, the Long data type was a 32-bitdata type.To define a literal Long, append an L, as in:

123L

The Long type identifier is the ampersand sign (&), as in:

Dim lng&

The underlying .NET data type for Long is System.Int64.

2.3.3.9 Object data type

The Object data type is a pointer data type. That is, a value of type Object is an address thatreferences the object in memory. In VB .NET, the Object data type is the universal data type; anObject variable can refer to (point to) data of any other data type. For instance, the following codeplaces a Long value in an Object variable:

Dim obj As Objectobj = 123L

The underlying .NET data type for Object is System.Object.It is worth noting that when we use variables of type Object, we do pay a performance penaltybecause VB .NET cannot bind the object's method invocations to the actual method code until runtime.This is referred to as late binding. On the other hand, declaring variables of a specific object typeallows early binding at compile time, which is much more efficient. Thus, code such as:

Dim obj As Object. . .obj.AMethod

is much less efficient than:

Dim obj As System.Data.DataSet. . .obj.AMethod

We revisit this issue in more detail later in this chapter.As we have seen, the Object data type is universal. Just as in VB 6, in which you can use the VarType

function to determine the data subtype of a Variant, in VB .NET you can use the VarType function todetermine the data subtype of an object.In addition, the Object class in the Base Class Library's System namespace has a method namedGetType that returns an object of type Type. Thus, if obj is a variable of type Object, then the code:

59

obj.GetType

returns a Type object. In turn, the Type class, which is also a member of the Base Class Library'sSystem namespace, has two methods that return information about the subtype of the object:

· ToString returns a string that describes the subtype of the data. It is roughly equivalent tocalling the VB .NET TypeName function, except that the former method uses the data typename from the .NET Base Class Library, whereas the latter function uses the Visual Basicname.

· GetTypeCode returns an enumeration value from the TypeCode enumeration. It is roughlyequivalent to calling the VB6 VarType function, which, as we have said, is no longer supportedin VB .NET.For reference, the following code generates the values in Table 2-1:

Dim obj As Object

obj = ???

debug.write(obj.GetType.ToString)Debug.Write(TypeName(obj))debug.writeline(Type.GetTypeCode(obj.GetType))

Table 2-1. Values of ToString and GetTypeCode

obj = ??? ToString TypeName GetType

obj = True System.Boolean Boolean 3obj = CByte(100) System.Byte Byte 6obj = #1/1/2000# System.DateTime Date 16obj = CDec(100) System.Decimal Decimal 15obj = CDbl(100) System.Double Double 14obj = CInt(100) System.Int32 Integer 9obj = CLng(100) System.Int64 Long 11obj = CShort(100) System.Int16 Short 7obj = CSng(100) System.Single Single 13obj = "Donna" System.String String 18

2.3.3.10 Short data type

The Short data type is a 16-bit integer data type that stores signed integers ranging from:-2^15 to 2^15-1or:-32,768 to 32,767Note that in earlier versions of Visual Basic, the Short data type is called the Integer data type.To define a literal Short, append an S, as in:

123S

The underlying .NET data type for Short is System.Int16.

2.3.3.11 Single data type

60

Values of type Single are IEEE 32-bit (4-byte) floating-point numbers with the range:-3.402823E38 to -1.401298E-45on the negative side, and:1.401298E-45 to 3.402823E38on the positive side.To write a literal Single, we must append an F (for floating point), as in:

12345.678F

The type identifier for a Single is an exclamation point (!), as in:

Dim sng!

The underlying .NET data type for Single is System.Single.

2.3.3.12 String data type

The String data type represents Unicode strings of up to approximately 2 billion characters. The typeidentifier for the string data type is a dollar sign ($). The underlying .NET data type for this type isSystem.String.To create a new string, we can declare a variable and assign it a string as follows:

Dim sName As StringsName = "Donna"

or equivalently, in one statement:

Dim sName As String = "Donna"

The type identifier for a String is a dollar sign ($), as in:

Dim str$

2.3.3.13 Structure data type: user-defined types

In VB .NET, the Structure type is a powerful data type that has many properties in common withclasses.To declare a structure, we use the Structure statement, whose syntax is:

[Public|Private|Friend] Structure StructureNameNonmethod member declarationsMethod member declarationsEnd Structure

The members of a structure can be variables, properties, methods, or events. Note, however, thateach member must be declared with an access modifier: Public (or Dim), Private, or Friend.

61

The simplest and most common use of structures is to encapsulate related variables. For instance, wemight define a structure as follows:

Structure strPersonPublic Name As StringPublic Address As StringPublic City As StringPublic State As StringPublic Zip As StringPublic Age As ShortEnd Structure

To define a variable of type strPerson, we write (as usual):

Dim APerson As strPerson

To access a member of a structure, we use the dot syntax, as in:

APerson.Name = "Beethoven"

Note that structure members can be other structures or other objects. Structures can also be passedas arguments to functions, or as the return type of a function.As mentioned, structures are similar to classes. For instance, consider the following structure:

Structure strTest' A public nonmethod memberPublic Name As String' A private member variablePrivate msProperty As String' A public method memberPublic Sub AMethod( )Msgbox("Structure method. Property is: " & msProperty)End Sub' A public property memberPublic Property AProperty( ) As StringGetAProperty = msPropertyEnd GetSetmsProperty = ValueEnd SetEnd PropertyEnd Structure

Now we can set the structure's property and invoke its method as follows:

Dim str As strTeststr.AProperty = "Donna"str.AMethod( )

Although structures are similar to classes, they do not support the following class features:

· Structures cannot explicitly inherit, nor can they be inherited.

· All constructors for a structure must be parameterized.

· Structures cannot define destructors.

· Member declarations cannot include initializers nor can they use the As New syntax or specifyan initial array size.

2.3.4 Data Type Conversion

The process of converting a value of one data type to another is called conversion or casting. A castoperator can be applied to a literal value or to a variable of a given type. For instance, we have:

Dim lng As LongDim int As Integer = 6' Cast an Integer variable to a Longlng = CLng(Int)' Cast a literal integer to a Longlng = CLng(12)

A cast can be widening or narrowing. A widening cast is one in which the conversion is to a target datatype that can accommodate all values in the source data type, such as casting from Short to Integer orInteger to Double. In such a case, no data is ever lost, and the cast will not generate an error. A

narrowing cast is one in which the target data type cannot accommodate all values in the source datatype. In this case, data may be lost, and the cast may not succeed.Under VB .NET, conversions are made in two ways: implicitly and explicitly. An implicit conversion isdone by the compiler when circumstances warrant it (and if it is legal). For instance, if we write:

Dim lng As Longlng = 54

then the compiler casts the Integer 54 as a Long.The type of implicit conversion that the compiler will do depends in part on the setting of the OptionStrict value. For instance, if Option Strict is On, only widening casts can be implicit; so then thefollowing code:

Dim b As Booleanb = "True"

generates a type conversion error, whereas if we add the line:

Option Strict Off

to the beginning of the module, then the previous code executes without error.

Explicit conversion requires explicitly calling a conversion function (or cast operator). The typeconversion functions supported by VB .NET all have the form:

Cname(expression)

where expression is an expression that is in the range of the target data type. Specifically, we havethe following conversion functions:

CBool

Converts any valid String or numeric expression to Boolean. When a numeric value isconverted to Boolean, any nonzero value is converted to True, and zero is converted to

False.

CByte

63

Converts any numeric expression in the range 0 to 255 to Byte, while rounding any fractionalpart.

CChar

Takes a string argument and returns the first character of the string as a Char data type.

CDate

Converts any valid representation of a date or time to Date.

CDbl

Converts any expression that can be evaluated to a number in the range of a Double toDouble.

CDec

Converts any expression that can be evaluated to a number in the range of a Decimal toDecimal.

CInt

Converts any numeric expression in the range of Integer (-2,147,483,648 to 2,147,483,647) toInteger, while rounding any fractional part.

CLng

Converts any expression that can be evaluated to a number in the range of a Long to Long,while rounding any fractional part.

CObj

Converts any expression that can be interpreted as an object to Object. For instance, the code:

Dim obj As Objectobj = CObj("test")

casts the string "test" to type Object and places it in the Object variable obj.

CShort

Converts any numeric expression in the range -32,768 to 32,767 to Short, while rounding anyfractional part.

CSng

Converts any expression that can be evaluated to a number in the range of a Single to Single.If the numeric expression is outside the range of a Single, an error occurs.

CStr

If the expression input to CStr is Boolean, the function returns one of the strings "True" or"False." For an expression that can be interpreted as a date, the return value is a string

64

representation of that date, in the date format defined by the regional settings of the hostcomputer. For a numeric expression, the return value is a string representing the number.

CType

A general-purpose conversion function, CType has the following syntax:

CType(expression, typename)

where expression is an expression or variable, and typename is the data type to which itwill be converted. The function supports conversions to and from the standard data types, aswell as to and from object data types, structures, and interfaces.

2.4 Arrays

The array data type is a fundamental data type in most languages, including Visual Basic. An array isused to store a collection of similar data types or objects.Many authors of programming books misuse the terms associated with arrays, so let's begin byestablishing the correct terminology. In fact, if you will indulge us, we would like to begin with a formaldefinition of the term array.

2.4.1 Definition of Array

Let S1, S2 , SN be finite sets, and let T be a data type (such as Integer). Then an array of type T is afunction:

arr:S1 x S2 x  x SN

T

where S1 x S2 x  x SN is the Cartesian product of the sets S1, S2 , SN. (This is the set of all n-tupleswhose coordinates come from the sets Si.)For arrays in VB .NET (and the other languages that implement the Common Language Runtime), thesets Si must have the form:

Si={0,1,,Ki}

In other words, each set Si is a finite set of consecutive integers starting with 0.Each position in the Cartesian product is referred to as a coordinate of the array. For each coordinate,the integer Ki is called the upper bound of the coordinate. The lower bound is 0 for all arrays inVB .NET.

2.4.2 Dimension of an Array

The number N of coordinates in the domain of the function arr is called the dimension (or sometimes

rank) of the array. Thus, every array has a dimension (note the singular); it is not correct to refer to thedimensions of an array (note the plural). An array of dimension 1 is called a one-dimensional array, anarray of dimension 2 is called a two-dimensional array, and so on.

2.4.3 Size of an Array

Along with a dimension, every array has a size. For instance, the one-dimensional array:

65

arr:{0,1,,5}T

has size 6. The two-dimensional array:

arr:{0,1,,5}x{0,1,,8}T

has size 6x9. The three-dimensional array:

arr:{0,1,,5}x{0,1,,8}x{0,1}T

has size 6x9x2.

2.4.4 Arrays in VB .NET

In VB .NET, all arrays have lower bound 0. This is a change from earlier versions of VB, where wecould choose the lower bound of an array.The following examples show various ways to declare a one-dimensional array:

' Implicit constructor: No initial size and no initializationDim Days( ) As Integer' Explicit constructor: No initial size and no initializationDim Days() As Integer = New Integer( ) {}' Implicit constructor: Initial size but no initializationDim Days(6) As Integer' Explicit constructor: Initial size but no initializationDim Days( ) As Integer = New Integer(6) {}' Implicit constructor: Initial size implied by initializationDim Days( ) As Integer = {1, 2, 3, 4, 5, 6, 7}' Explicit constructor, Initial size and initializationDim Days( ) As Integer = New Integer(6) {1, 2, 3, 4, 5, 6, 7}

Note that an array declaration can:

· Call the array's constructor implicitly or explicitly. (The constructor is the function that VB .NETuses to create the array.)

· Specify an initial size for each dimension or leave the initial size unspecified.

· Initialize the elements of the array or not.It is important to note that in the declaration:

Dim ArrayName(X) As ArrayType

the number X is the upper bound of the array. Thus, the array elements are ArrayName(0) throughArrayName(X), and the array has X+1 elements.

66

Multidimensional arrays are declared similarly. For instance, the following example declares andinitializes a two-dimensional array:

Dim X(,) As Integer = {{1, 2, 3}, {4, 5, 6}}

and the following code displays the contents of the array:

Debug.Write(X(0, 0))Debug.Write(X(0, 1))Debug.Writeline(X(0, 2))Debug.Write(X(1, 0))Debug.Write(X(1, 1))Debug.Write(X(1, 2))123456

In VB .NET, all arrays are dynamic: there is no such thing as a fixed-size array. The declared sizeshould be thought of simply as the initial size of the array, which is subject to change using the ReDim

statement. Note, however, that the dimension of an array cannot be changed.Moreover, unlike with VB 6, the ReDim statement cannot be used for array declaration, but can beused only for array redimensioning. All arrays must be declared initially using a Dim (or equivalent)statement.

2.4.4.1 Redimensioning arrays

The ReDim statement is used to change the size of an array. This is referred to as redimensioning -- aterm no doubt invented by someone who didn't know the difference between the dimension of an arrayand the size of an array! In any case, redimensioning changes the size of the array, not its dimension.In fact, as we have already mentioned, the dimension of an array cannot be changed.The UBound function returns the upper limit of an array coordinate. Its syntax is:

UBound(MyArray, CoordinateIndex)

where CoordinateIndex is the index of the coordinate for which we want the upper bound.Here is an example of array redimensioning:

Dim MyArray(10, 10) As IntegerMsgbox(UBound(MyArray, 2)) ' Displays 10ReDim MyArray(15, 20)Msgbox(UBound(MyArray, 2)) ' Displays 20

When an array is redimensioned using the ReDim statement without qualification, all data in the arrayis lost; that is, the array is reinitialized. However, the Preserve keyword, when used with ReDim,redimensions the array while retaining all current values. Note that when using the Preserve

keyword, only the last coordinate of an array can be changed. Thus, referring to the array definedearlier, the following code generates an error:

ReDim Preserve MyArray(50, 20)

You will probably not be surprised to learn that redimensioning an array is a time-intensive process.Hence, when redimensioning, we face the ubiquitous dichotomy between saving space and savingtime. For instance, consider the code segment used to populate an array:

67

Dim MyArray(100) As IntegerDim i As Integer, iNext As IntegeriNext = 0Do While (Some condition)If (some condition here) Then' Add element to arrayIf ubound(MyArray) < iNext ThenReDim Preserve MyArray(iNext + 100)End IfMyArray(iNext) = (whatever)iNext = iNext + 1End IfLoop

The key issue here is to decide how much to increase the size of the array each time resizing isnecessary. If we want to avoid using any extra space, we could increase the size of the array by 1each time:

ReDim Preserve MyArray(iNext + 1)

But this would be very inefficient. Alternatively, we could kick up the size by 1,000:

ReDim Preserve MyArray(iNext + 1000)

But this uses a lot of extra space. Sometimes experimentation is required to find the right compromisebetween saving space and saving time.

2.5 Object Variables and Their Binding

In VB .NET, classes and their objects are everywhere. Of course, there are the classes and objectsthat we create in our own applications. There are also the classes in the .NET Framework ClassLibrary. In addition, many applications take advantage of the objects that are exposed by otherapplications, such as ActiveX Data Objects (ADO), Microsoft Word, Excel, Access, various scriptingapplications, and more. The point is that for each object we want to manipulate, we will need todeclare a variable of that class type. For instance, if we create a class named CPerson, then in orderto instantiate a CPerson object, we must declare a variable:

Dim APerson As CPerson

Similarly, if we decide to use the ADO Recordset object, we will need to declare a variable of typeADO.Recordset:

Dim rs As ADO.Recordset

Even though object variables are declared in the same manner as nonobject variables, there are somesignificant differences. In particular, the declaration:

Dim obj As MyClass

does not create an object variable—it only binds a variable name with a class name. To actuallyconstruct an object and set the variable to refer to that object, we need to call the constructor of theclass. This function, discussed in detail in Chapter 3, is responsible for creating objects of the class.Constructors are called using the New keyword, as in:

Dim obj As MyClass = New Myclass( )

68

or:

Dim obj As MyClassobj = New MyClass( )

VB .NET also provides a shortcut that does not mention the constructor explicitly:

Dim obj As New MyClass( )

(In earlier versions of VB, we use the Set statement, which is no longer supported.)

2.5.1 Late Binding Versus Early Binding

The object-variable declaration:

Dim obj As Class1

explicitly mentions the class from which the object will be created (in this case it is Class1). Because ofthis, VB can obtain and display information about the class members, as we can see in VB'sIntellisense, shown in Figure 2-1.

Figure 2-1. Intellisense showing member list

As you know, Intellisense also shows the signature of a method, as shown in Figure 2-2.

Figure 2-2. Intellisense showing method signature

Of course, Intellisense is very helpful during program development. However, more important is thatthe previous object-variable declaration allows VB to bind the object's methods to actual functionaddresses at compile time. This is known as early binding.An alternative to using a declaration that specifically mentions that class is a generic object-variabledeclaration that uses the As Object syntax:

Dim obj As Object

While it is true that obj can hold a reference to any object, we pay a major penalty for this privilege.VB can no longer get information about the class and its members because it does not know whichclass the object obj belongs to!

69

As a result, VB's Intellisense cannot help us with member syntax. More importantly, we pay a largeperformance penalty because VB cannot bind any of the classes properties or methods at compiletime—it must wait until runtime. This is referred to as late binding.In summary, explicit object-variable declarations allow for early binding and thus are much moreefficient than generic declarations, which use late binding. Hence, explicit object-variable declarationsshould be used whenever possible.

2.6 The Collection Object

VB .NET implements a special object called the Collection object that acts as a container for objects ofall types. In fact, Collection objects can hold other objects, as well as nonobject data.In some ways, the Collection object is an object-oriented version of the Visual Basic array. It supportsthe following four methods:

Add

Adds an item to the collection. Along with the data itself, you can specify a key value by whichthe member can be referenced.

Count

Returns the number of items in the collection.

Item

Retrieves a member from the collection either by its index (or ordinal position in the collection)or by its key (assuming that a key was provided when the item was added to the collection).

Remove

Deletes a member from the collection using the member's index or key.For example, the following code defines a collection object named colStates to hold information aboutU.S. states and then adds two members to it, using the state's two-letter abbreviation as a key:

Dim colStates As New CollectioncolStates.Add("New York", "NY")colStates.Add("Michigan", "MI")

Like members of an array, the members of a collection can be iterated using the For EachNext

construct. Also like arrays, collection members are accessible by their index value, although the lowerbound of a collection object's index is always 1.Arrays and collections each have advantages and disadvantages. Some of the advantages ofcollections over arrays are:

· New collection members can be inserted before or after an existing member in index order.Moreover, indexes are maintained automatically by VB, so we don't need to adjust the indexesmanually.

· Collection members can be referenced by key value. This feature makes collections similar toassociative arrays (which are used by languages such as Perl).Note that when deleting collection members by index, it is important to iterate though the indexes inreverse order because member deletion changes the indexes of other members.

70

2.7 Parameters and Arguments

The terms parameter and argument are often used interchangeably, although they have entirelydifferent meanings. Let us illustrate with an example. Consider the following function, which replicatesa string a given number of times:

Function RepeatString(ByVal sInput As String, ByVal iCount As Integer) _As StringDim i As IntegerFor i = 1 To iCountRepeatString = RepeatString & sInputNextEnd Function

The variables sInput and iCount are the parameters of this function. Note that each parameter hasan associated data type.Now, when we call this function, we must replace the parameters by variables, constants, or literals,as in:

s = RepeatString("Donna", 4)

The items that we use in place of the parameters are called arguments.

2.7.1 Passing Arguments

Arguments can be passed to a function in one of two ways: by value or by reference. Incidentally,argument passing is often called parameter passing, although it is the arguments and not theparameters that are being passed.The declaration of RepeatString given earlier contains the keyword ByVal in front of each parameter.This specifies that arguments are passed by value to this function. Passing by value means that theactual value of the argument is passed to the function. This is relevant when an argument is a variable.For instance, consider the following code:

Sub Inc(ByVal x As Integer)x = x + 1End SubDim iAge As Integer = 20Inc(iAge)Msgbox(iAge)

The final line:

Msgbox(iAge)

actually displays the number 20. In other words, the line:

Inc(iAge)

does nothing. The reason is that the argument iAge is passed to the procedure Inc by value. Sinceonly the value (in this case 20) is passed, that value is assigned to a local variable named x within theprocedure. This local variable is increased to 21, but once the procedure ends, the local variable isdestroyed. The variable iAge is not passed to the procedure, so its value is not changed.

71

On the other hand, if we modify the definition of the procedure Inc, replacing ByVal with ByRef, thestory is different:

Sub Inc(ByRef x As Integer)x = x + 1End Sub

In this case, what is passed to the procedure Inc is a reference to the argument iAge. Hence, theprocedure actually operates on the variable passed to it, incrementing the value of iAge to 21. Putanother way, the variable represented by the parameter x is actually the passed variable iAge.In VB .NET, the default method of argument passing for arguments is by value. This is a change fromearlier versions of VB, in which the default method was by reference.

2.7.2 Passing Objects

There is a subtlety in argument passing with parameters of any object type. Actually, the subtletyoccurs because an object variable is a pointer ; that is, it contains a reference to (or the address of)the object.If we pass an object variable by value, we are passing the contents of the variable, which is theaddress of the object. Thus, any changes made in the called procedure affects the object itself, not acopy of the object. This seems like passing by reference, but it is not. Think of it this way: passing the

value of an object's address is passing a reference to the object.On the other hand, if we pass an object variable by reference, we are passing the address of thevariable. In other words, we are passing the address of the address of the object! In languages thatsupport pointers, this is referred to as a double pointer.Let us illustrate with an example. Consider the following code, and imagine that the form containingthis code has two textboxes: TextBox1 with text "TextBox1" and TextBox2 with text "TextBox2":

Public Function GetText(ByVal txt As TextBox) As String' Change reference to textboxtxt = Textbox2End FunctionSub DoitDim t As TextBoxt = TextBox1GetText(t)msgbox(t.Text) ' Displays TextBox1 when ByVal, _' TextBox2 when ByRefEnd Sub

Now, here is what happens when we execute DoIt. Note that the argument is passed to GetText byvalue in this case.

· The TextBox variable t is assigned to TextBox1, as shown in Figure 2-3.

Figure 2-3. Assigning an object reference

72

· GetText is called, passing t by value. Since t contains the address aaaa of the TextBox1object, the local variable txt is given the value aaaa, as shown in Figure 2-4.

Figure 2-4. Passing an object by value

· The single line of code in GetText is executed, which now causes txt to point to TextBox2,as shown in Figure 2-5.

Figure 2-5. Assigning a new object reference

· Upon return from GetText, t is unaffected, so the MsgBox function displays the string"TextBox1."Now suppose we change the ByVal keyword to ByRef in GetText. Here is what happens:

· The TextBox variable t is assigned to TextBox1, as shown previously in Figure 2-3.

· GetText is called, passing t by reference. Hence, txt is t. This is quite different from txt and

t containing the same value, as in the ByVal case. The situation is shown in Figure 2-6.

Figure 2-6. Passing an object by reference

· The single line of code in GetText is executed, which now causes txt (and hence t) to pointto TextBox2, as shown in Figure 2-7.

Figure 2-7. Assigning a new object reference

73

· Upon return from GetText, t is now pointing to TextBox2, so the MsgBox function displays thestring "TextBox2."

2.7.3 Optional Arguments

In VB .NET, parameters can be declared as optional using the Optional keyword, as shown in thefollowing code:

Sub Calculate(Optional ByVal Switch As Boolean = False)

In VB .NET, all optional parameters must declare a default value, which is passed to the procedure ifthe calling program does not supply that parameter.The following rules apply to optional arguments:

· Every optional argument must specify a default value, and this default must be a constantexpression (not a variable).

· Every argument following an optional argument must also be optional.Note that in earlier versions of VB, you could omit the default value and, if the parameter was of typeVariant, you could use the IsMissing function to determine if a value was supplied. This is not possiblein VB .NET, and the IsMissing function is not supported.

2.7.4 ParamArray

Normally, a procedure definition specifies a fixed number of parameters. However, the ParamArray

keyword, which is short for Parameter Array, permits us to declare a procedure with an unspecifiednumber of parameters. Therefore, each call to the procedure can use a different number ofparameters.Suppose, for instance, that we want to define a function to take the average of a number of test scores,but the number of scores may vary. Then we declare the function as follows:

Function GetAverage(ParamArray ByVal Scores( ) As Single) As SingleDim i As IntegerFor i = 0 To UBound(Scores)GetAverage = GetAverage + CSng(Scores(i))NextGetAverage = GetAverage / (UBound(Scores) + 1)End Function

Now we can make calls to this function with a varying number of arguments:

74

Msgbox(GetAverage(1, 2, 3, 4, 5))Msgbox(GetAverage(1, 2, 3))

The following rules apply to the use of ParamArray:

· A procedure can only have one parameter array, and it must be the last parameter in theprocedure.

· The parameter array must be passed by value, and you must explicitly include ByVal in theprocedure definition.

· The parameter array must be a one-dimensional array. If the type is not declared, it isassumed to be Object.

· The parameter array is automatically optional. Its default value is an empty one-dimensionalarray of the parameter array's data type.


Chapter 3. Introduction to Object-OrientedProgramming

In this chapter, we present a brief and succinct introduction to object-oriented programming. Since thisis not a book on object-oriented programming per se, we will confine our attention to those topics thatare important to VB .NET programming.

3.1 Why Learn Object-Oriented Techniques?

As you may know, Visual Basic has implemented some features of object-oriented programming sinceVersion 4. However, in terms of object-orientation, the move from Version 6 to VB .NET has beendramatic. Many people did not consider VB 6 (or earlier versions) to be a truly object-orientedprogramming language. Whatever your thoughts may have been on this matter, it seems clear thatVB .NET is an object-oriented programming language by any reasonable definition of the term.You may be saying to yourself: "I prefer not to use object-oriented techniques in my programming."This is something you could easily have gotten away with in VB 6. But in VB .NET, the structure ofthe .NET Framework—specifically the .NET Base Class Library—as well as the documentation, is soobject-oriented that you can no longer avoid understanding the basics of object-orientation, even if youdecide not to use them in your applications.

3.2 Principles of Object-Oriented Programming

It is often said that there are four main concepts in the area of object-oriented programming:

· Abstraction

· Encapsulation

· Inheritance

· PolymorphismEach of these concepts plays a significant role in VB .NET programming at one level or another.Encapsulation and abstraction are "abstract" concepts providing motivation for object-orientedprogramming. Inheritance and polymorphism are concepts that are directly implemented in VB .NETprogramming.

3.2.1 Abstraction

Simply put, an abstraction is a view of an entity that includes only those aspects that are relevant for aparticular situation. For instance, suppose that we want to create a software component that providesservices for keeping a company's employee information. For this purpose, we begin by making a list ofthe items relevant to our entity (an employee of the company). Some of these items are:

· FullName

· Address

· EmployeeID

· Salary

· IncSalary

· DecSalaryNote that we include not only properties of the entities in question, such as FullName, but also actions

that might be taken with respect to these entities, such as IncSalary, to increase an employee's salary.Actions are also referred to as methods, operations, or behaviors. We will use the term methods, sincethis term is used by VB .NET.

76

Of course, we would never think of including an IQ property, since this would not be politically correct,not to mention discriminatory and therefore possibly illegal. Nor would we include a property calledHairCount, which gives the number of hairs on the employee's right arm, because this information is ofabsolutely no interest to us, even though it is part of every person's being.In short, we have abstracted the concept of an employee—we have included only those propertiesand methods of employees that are relevant to our needs. Once the abstraction is complete, we canproceed to encapsulate these properties and methods within a software component.

3.2.2 Encapsulation

The idea of encapsulation is to contain (i.e., encapsulate) the properties and methods of anabstraction, and expose only those portions that are absolutely necessary. Each property and methodof an abstraction is called a member of the abstraction. The set of exposed members of an abstractionis referred to collectively as the public interface (or just interface) of the abstraction (or of the softwarecomponent that encapsulates the abstraction).Encapsulation serves three useful purposes:

· It permits the protection of these properties and methods from any outside tampering.

· It allows the inclusion of validation code to help catch errors in the use of the public interface.For instance, it permits us to prevent the client of the employee software component fromsetting an employee's salary to a negative number.

· It frees the user from having to know the details of how the properties and methods areimplemented.Let us consider an example that involves the Visual Basic Integer data type, which is nicelyencapsulated for us by VB. As you undoubtedly know, an integer is stored in the memory of a PC as astring of 0s and 1s called a binary string. In Visual Basic, integers are interpreted in a form called

two's-complement representation, which permits the representation of both negative and non-negativevalues.For simplicity, let us consider 8-bit binary numbers. An 8-bit binary number has the forma7a6a5a4a3a2a1a0, where each of the a1s is a 0 or a 1. We can think of it as appearing in memory asshown in Figure 3-1.

Figure 3-1. An 8-bit binary number

In the two's-complement representation, the leftmost bit, a7 (called the most significant bit), is the signbit. If the sign bit is 1, the number is negative. If the sign bit is 0, the number is positive.The formula for converting a two's-complement representation a7a6a5a4a3a2a1a0 of a number to adecimal representation is:

decimal rep. = -128a7 + 64a6 + 32a5 + 16a4 + 8a3 + 4a2 + 2a1 + a0

To take the negative of a number when it is represented in two's-complement form, we must take thecomplement of each bit (that is, change each 0 to a 1 and each 1 to a 0) and then add 1.At this point you may be saying to yourself, "As a programmer, I don't have to worry about thesedetails. I just write code like:

x = -16y = -x

77

and let the computer and the programming language worry about which representation to use andhow to perform the given operations."This is precisely the point behind encapsulation. The details of how signed integers are interpreted bythe computer (and the compiler), as well as how their properties and operations are implemented, areencapsulated in the integer data type itself and are thus hidden from us, the users of the data type.Only those portions of the properties and operations that we need in order to work with integers areexposed outside of the data type. These portions form the public interface for the Integer data type.Moreover, encapsulation protects us from making errors. For instance, if we had to do our ownnegating by taking Boolean complements and adding 1, we might forget to add 1! The encapsulateddata type takes care of this automatically.Encapsulation has yet another important feature. Any code that is written using the exposed interfaceremains valid even if the internal workings of the Integer data type are changed for some reason, aslong as the interface is not changed. For instance, if we move the code to a computer that storesintegers in one's-complement representation, then the internal procedure for implementing theoperation of negation in the integer data type will have to be changed. However, from theprogrammer's point of view, nothing has changed. The code:

x = -16y = -x

is just as valid as before.

3.2.3 Interfaces

As VB programmers, we must implement encapsulation through the use of software components. Forinstance, we can create a software component to encapsulate the Employee abstraction discussedearlier.In VB .NET, the methods of an interface are realized as functions. On the other hand, a property, aswe see later in this chapter, is realized as a private variable that stores the property's value togetherwith a pair of public functions—one to set the variable and one to retrieve the variable. These functionsare sometimes referred to as accessor methods of the property. It is the set of exposed functions(ordinary methods and accessor methods) that constitute the interface for an abstraction.In general, a software component may encapsulate and expose more than one abstraction—hence,more than one interface. For example, in a more realistic setting, we might want a software componentdesigned to model employees to encapsulate an interface called IIdentification (the initial "I" isfor interface) that is used for identification purposes. This interface might have properties such asName, Social Security number, Driver's License number, Age, Birthmarks, and so on. Moreover, thesoftware component might also encapsulate an interface called IEducation for describing theemployee's educational background. Such an interface might implement properties such as EducationLevel, Degrees, College Attended, and so on.The interface of each abstraction exposed by a software component is also referred to as an interface

of the software component. Thus, the Employee component implements at least two interfaces:

IIdentification and IEducation. Note, however, that the term interface is often used to refer tothe set of all exposed properties and methods of a software component, in which case a componenthas only one interface.Referring to our original Employee abstraction, its interface might consist of the functions shown in

Table 3-1. (Of course, this interface is vastly oversimplified, but it is more than sufficient to illustratethe concepts.)

78

Table 3-1. Members of the Employee interface

Type Name

Property FullName: GetFullName(), SetFullName( )Property Address: GetAddress(), SetAddress( )Property EmployeeID: GetEmployeeID(), SetEmployeeID( )Property Salary: GetSalary(), SetSalary( )Method IncSalary( )Method DecSalary( )Using the term interface as a set of functions, while quite common, poses a problem. Just listing thefunctions of the interface by name (as done previously) does not provide enough information to callthose functions. Thus, a more useful definition of interface would be the set of signatures of the publicfunctions of a software component.To clarify this, let us discuss one of the most important distinctions in object-oriented programming—the distinction between a function declaration and an implementation of that function.By way of example, consider the following sorting function:

Function Sort(a( ) as Integer, iSize as Integer) as BooleanFor i = 1 to iSizeFor j = i+1 to iSizeIf a(j) < a(i) Then swap a(i), a(j)Next jNext ISort = TrueEnd Function

The first line in this definition:

Function Sort(a( ) as Integer, iSize as Integer) as Boolean

is the function declaration. It supplies information on the number and types of parameters and thereturn type of the function. The body of the function:

For i = 1 to iSizeFor j = i+1 to iSizeIf a(j) < a(i) Then swap a(i), a(j)Next jNext iSort = True

represents the implementation of the function. It describes how the function carries out its intendedpurpose.Note that it is possible to alter the implementation of the function without changing the declaration. Infact, the current function implementation sorts the array a using a simple selection-sort algorithm, butwe could replace that sorting method with any one of a number of other methods (bubble sort,insertion sort, quick sort, and so on).Now consider a client of the Sort function. The client only needs to know the function declaration inorder to use the function. It need not know (and probably doesn't want to know) anything about theimplementation. Thus, it is the function declaration, and not the implementation, that forms theinterface for the function.

79

The signature of a function is the function name and return type, as well as the names, order, andtypes of its parameters. A function declaration is simply a clear way of describing the function'ssignature. Note that Microsoft does not consider the return type of a function to be part of thefunction's signature. By signature, they mean what is generally termed the function's argumentsignature. The reasons for doing this become clearer later in the chapter when we discuss overloading,although it would have been better (as usual) if they were more careful with their terminology.Under this more specific definition of interface, the interface for our employee component might be asfollows (in part):

Function GetFullName(lEmpID As Long) As StringSub SetFullName(lEmpID As Long, sName As String). . .Sub IncSalary(sngPercent As Single)Sub DecSalary(sngPercent As Single)

3.3 Classes and Objects

Generally speaking, a class is a software component that defines and implements one or moreinterfaces. (Strictly speaking, a class need not implement all the members of an interface. We discussthis later when we talk about abstract members.) In different terms, a class combines data, functions,and types into a new type. Microsoft uses the term type to include classes.

3.3.1 Class Modules in VB .NET

Under Visual Studio.NET, a VB class module is inserted into a project using the Add Class menu itemon the Project menu. This inserts a new module containing the code:

Public Class ClassNameEnd Class

Although Visual Studio stores each class in a separate file, this isn't a requirement. It is the

ClassEnd Class construct that marks the beginning and end of a class definition. Thus, the codefor more than one class as well as one or more code modules (which are similarly delimited by the

ModuleEnd Module construct) can be contained in a single source code file.The CPerson class defined in the next section is an example of a VB class module.

3.3.2 Class Members

In VB .NET, class modules can contain the following types of members:

Data members

This includes member variables (also called fields) and constants.

Event members

Events are procedures that are called automatically by the Common Language Runtime inresponse to some action that occurs, such as an object being created, a button being clicked,a piece of data being changed, or an object going out of scope.

Function members

80

This refers to both functions and subroutines. A function member is also called a method. Aclass' constructor is a special type of method. We discuss constructors in detail later in thischapter.

Property members

A property member is implemented as a Private member variable together with a special typeof VB function that incorporates both accessor functions of the property. We discuss thesyntax of this special property function in Section 3.3.5 later in the chapter.

Type members

A class member can be another class, which is then referred to as a nested class.The following CPerson class illustrates some of the types of members:

Public Class CPerson' -------------' Data Members' -------------' Member variablesPrivate msName As StringPrivate miAge As Integer' Member constantPublic Const MAXAGE As Short = 120' Member eventPublic Event Testing( )' ----------------' Function Members' ----------------' MethodPublic Sub Test( )RaiseEvent Testing( )End SubProperty Age( ) As IntegerGetAge = miAgeEnd GetSet(ByVal Value As Integer)' Some validationIf Value < 0 ThenMsgBox("Age cannot be negative.")ElsemiAge = ValueEnd IfEnd SetEnd Property' PropertyProperty Name( ) As String' Accessors for the propertyGetName = msName

81

End GetSet(ByVal Value As String)msName = ValueEnd SetEnd Property' Overloaded constructorOverloads Sub New( )End Sub' Constructor that initializes nameOverloads Sub New(ByVal sNewName As String)msName = sNewNameEnd SubSub Dispose( )' Code here to clean upEnd SubEnd Class

3.3.3 The Public Interface of a VB .NET Class

We have seen that, when speaking in general object-oriented terms, the exposed members of asoftware component constitute the component's public interface (or just interface). Now, in VB .NET,each member of a class module has an access type, which may be Public, Private, Friend,

Protected, or Protected Friend. We discuss each of these in detail later in this chapter. Suffice itto say, a VB .NET class module may accordingly have Public, Private, Friend, Protected, and

Protected Friend members.Thus, we face some ambiguity in defining the concept of the public interface of a VB .NET class. Thespirit of the term might indicate that we should consider any member that is exposed outside of theclass itself as part of the public interface of the class. This would include the Protected, Friend,and Protected Friend members, as well as the Public members. On the other hand, some mightargue that the members of the public interface must be exposed outside of the project in which theclass resides, in which case only the Public members would be included in the interface. Fortunately,we need not make too much fuss over the issue of what exactly constitutes a VB .NET class' publicinterface, as long as we remain aware that the term may be used differently by different people.

3.3.4 Objects

A class is just a description of some properties and methods and does not have a life of its own (withthe exception of shared members, which we discuss later). In general, to execute the methods anduse the properties of a class, we must create an instance of the class, officially known as an object.Creating an instance of a class is referred to as instancing, or instantiating, theclass.There are three ways to instantiate an object of a VB .NET class. One method is to declare a variableof the class' type:

Dim APerson As CPerson

and then instantiate the object using the New keyword as follows:

APerson = New CPerson( )

We can combine these two steps as follows:

82

Dim APerson As New CPerson( )

or:

Dim APerson As CPerson = New CPerson( )

The first syntax is considered shorthand for the second.

3.3.5 Properties

Properties are members that can be implemented in two different ways. In its simplest implementation,a property is just a public variable, as in:

Public Class CPersonPublic Age As IntegerEnd Class

The problem with this implementation of the Age property is that it violates the principle ofencapsulation; anyone who has access to a CPerson object can set its Age property to any Integervalue, even negative integers, which are not valid ages. In short, there is no opportunity for datavalidation. (Moreover, this implementation of a property does not permit its inclusion in the publicinterface of the class, as we have defined that term.)The "proper" object-oriented way to implement a property is to use a Private data member along with aspecial pair of function members. The Private data member holds the property value; the pair offunction members, called accessors, are used to get and set the property value. This promotes dataencapsulation, since we can restrict access to the property via code in the accessor functions, whichcan contain code to validate the data. The following code implements the Age property.

Private miAge As IntegerProperty Age( ) As IntegerGetAge = miAgeEnd GetSet(ByVal Value As Integer)' Some validationIf Value < 0 ThenMsgBox("Age cannot be negative.")ElsemiAge = ValueEnd IfEnd SetEnd Property

As you can see from the previous code, VB has a special syntax for defining the property accessors.As soon as we finish typing the line:

Property Age( ) As Integer

the VB IDE automatically creates the following template:

Property Age( ) As IntegerGet

83

End GetSet(ByVal Value As Integer)End SetEnd Property

Note the Value parameter that provides access to the incoming value. Thus, if we write:

Dim cp As New CPerson( )cp.Age = 20

then VB passes the value 20 into the Property procedure in the Value argument.

3.3.6 Instance and Shared Members

The members of a class fall into two categories:

Instance members

Members that can only be accessed through an instance of the class, that is, through anobject of the class. To put it another way, instance members "belong" to an individual objectrather than to the class as a whole.

Shared (static) members

Members that can be accessed without creating an instance of the class. These members areshared among all instances of the class. More correctly, they are independent of any particularobject of the class. To put it another way, shared members "belong" to the class as a whole,rather than to its individual objects or instances.Instance members are accessed by qualifying the member name with the object's name. Here is anexample:

Dim APerson As New CPerson( )APerson.Age = 50

To access a shared member, we simply qualify the member with the class name. For instance, theString class in the System namespace of the .NET Base Class Library has a shared method calledCompare that compares two strings. Its syntax (in one form) is:

Public Shared Function Compare(String, String) As Integer

This function returns 0 if the strings are equal, -1 if the first string is less than the second, and 1 if thefirst string is greater than the second. Since the method is shared, we can write:

Dim s As String = "steve"Dim t As String = "donna"MsgBox(String.Compare(s, t)) ' Displays 1

Note the way the Compare method is qualified with the name of the String class.Shared members are useful for keeping track of data that is independent of any particular instance ofthe class. For instance, suppose we want to keep track of the number of CPerson objects in existenceat any given time. Then we write code such as the following:

' Declare a Private shared variable to hold the instance count

84

Private Shared miInstanceCount As Integer' Increment the count in the constructor' (If there are additional constructors,' this code must be added to all of them.)Sub new( )miInstanceCount += 1End Sub' Supply a function to retrieve the instance countShared Function GetInstanceCount( ) As IntegerReturn miInstanceCountEnd Function' Decrement the count in the destructorOverrides Protected Sub Finalize( )miInstanceCount -= 1MyBase.FinalizeEnd Sub

Now, code such as the following accesses the shared variable:

Dim steve As New CPerson( )MsgBox(CPerson.GetInstanceCount) ' Displays 1Dim donna As New CPerson( )MsgBox(CPerson.GetInstanceCount) ' Displays 2

3.3.7 Class Constructors

When an object of a particular class is created, the compiler calls a special function called the class'

constructor or instance constructor. Constructors can be used to initialize an object when necessary.(Constructors take the place of the Class_Initialize event in earlier versions of VB.)We can define constructors in a class module. However, if we choose not to define a constructor, VBuses a default constructor. For instance, the line:

Dim APerson As CPerson = New CPerson( )

invokes the default constructor of our CPerson class simply because we have not defined a customconstructor.To define a custom constructor, we just define a subroutine named New within the class module. Forinstance, suppose we want to set the Name property to a specified value when a CPerson object isfirst created. Then we can add the following code to the CPerson class:

' Custom constructorSub New(ByVal sName As String)Me.Name = sNameEnd Sub

Now we can create a CPerson object and set its name as follows:

Dim APerson As CPerson = New CPerson("fred")

or:

Dim APerson As New CPerson("fred")

85

Note that because VB .NET supports function overloading (discussed later in this chapter), we candefine multiple constructors in a single class, provided each constructor has a unique argumentsignature. We can then invoke any of the custom constructors simply by supplying the correct numberand type of arguments for that constructor.Note also that once we define one or more custom constructors, we can no longer invoke the default(that is, parameterless) constructor with a statement such as:

Dim APerson As New CPerson( )

Instead, to call a parameterless constructor, we must specifically add the constructor to the classmodule:

' Default constructorSub New( ) End Sub

3.3.8 Finalize, Dispose, and Garbage Collection

In VB 6, a programmer can implement the Class_Terminate event to perform any clean up proceduresbefore an object is destroyed. For instance, if an object held a reference to an open file, it might beimportant to close the file before destroying the object itself.In VB .NET, the Terminate event no longer exists, and things are handled quite differently. Tounderstand the issues involved, we must first discuss garbage collection.When the garbage collector determines that an object is no longer needed (which it does, for instance,when the running program no longer holds a reference to the object), it automatically runs a special

destructor method called Finalize. However, it is important to understand that, unlike with theClass_Terminate event, we have no way to determine exactly when the garbage collector will call theFinalize method. We can only be sure that it will be called at some time after the last reference to theobject is released. Any delay is due to the fact that the .NET Framework uses a system called

reference-tracing garbage collection, which periodically releases unused resources.Finalize is a Protected method. That is, it can be called from a class and its derived classes, but it isnot callable from outside the class, including by clients of the class. (In fact, since the Finalizedestructor is automatically called by the garbage collector, a class should never call its own Finalizemethod directly.) If a class' Finalize method is present, then it should explicitly call its base class'Finalize method as well. Hence, the general syntax and format of the Finalize method is:

Overrides Protected Sub Finalize( )' Cleanup code goes hereMyBase.FinalizeEnd Sub

The benefits of garbage collection are that it is automatic and it ensures that unused resources arealways released without any specific interaction on the part of the programmer. However, it has thedisadvantages that garbage collection cannot be initiated directly by application code and someresources may remain in use longer than necessary. Thus, in simple terms, we cannot destroy objectson cue.We should note that not all resources are managed by the Common Language Runtime. Theseresources, such as Windows handles and database connections, are thus not subject to garbagecollection without specifically including code to release the resources within the Finalize method. But,as we have seen, this approach does not allow us or clients of our class to release resources on

86

demand. For this purpose, the Base Class Library defines a second destructor called Dispose. Itsgeneral syntax and usage is:

Class classname

Implements IDisposablePublic Sub Dispose( ) Implements IDisposable.Dispose' cleanup code goes here' call child objects' Dispose methods, if necessary, hereEnd Sub' Other class codeEnd Class

Note that classes that support this callable destructor must implement the IDisposable interface—hence the Implements statement just shown. IDisposable has just one member, the Disposemethod.It is important to note that it is necessary to inform any clients of the class that they must call thismethod specifically in order to release resources. (The technical term for this is the manual approach!)

3.4 Inheritance

Perhaps the best way to describe inheritance as it is used in VB .NET is to begin with an example.The classes in a given application often have relationships to one another. Consider, for instance, ourEmployee information application. The Employee objects in the class CEmployee represent thegeneral aspects common to all employees—name, address, salary, and so on.Of course, the executives of the company will have different perquisites than, say, the secretaries. Soit is reasonable to define additional classes named CExecutive and CSecretary, each with propertiesand methods of its own. On the other hand, an executive is also an employee, and there is no reasonto define different Name properties in the two cases. This would be inefficient and wasteful.This situation is precisely what inheritance is designed for. First, we define the CEmployee class,which implements a Salary property and an IncSalary method:

' Employee classPublic Class CEmployee' Salary property is read/writePrivate mdecSalary As DecimalProperty Salary( ) As DecimalGetSalary = mdecSalaryEnd GetSetmdecSalary = ValueEnd SetEnd PropertyPublic Overridable Sub IncSalary(ByVal sngPercent As Single)mdecSalary = mdecSalary * (1 + CDec(sngPercent))End SubEnd Class

Next, we define the CExecutive class:

' Executive Class

87

Public Class CExecutiveInherits CEmployee' Calculate salary increase based on 5% car allowance as wellOverrides Sub IncSalary(ByVal sngPercent As Single)Me.Salary = Me.Salary * CDec(1.05 + sngPercent)End SubEnd Class

There are two things to note here. First, the line:

Inherits CEmployee

indicates that the CExecutive class inherits the members of the CEmployee class. Put another way, anobject of type CExecutive is also an object of type CEmployee. Thus, if we define an object of typeCExecutive:

Dim ceo As New CExecutive

then we can invoke the Salary property, as in:

ceo.Salary = 1000000

Second, the keyword Overrides in the IncSalary method means that the implementation of IncSalaryin CExecutive is called instead of the implementation in CEmployee. Thus, the code:

ceo.IncSalary

raises the salary of the CExecutive object ceo based on a car allowance. Note also the presence ofthe Overridable keyword in the definition of IncSalary in the CEmployee class, which specifies thatthe class inheriting from a base class is allowed to override the method of the base class.Next, we define the CSecretary class, which also inherits from CEmployee but implements a differentsalary increase for secretary objects:

' Secretary ClassPublic Class CSecretaryInherits CEmployee' Secretaries get a 2% overtime allowanceOverrides Sub IncSalary(ByVal sngPercent As Single)Me.Salary = Me.Salary * CDec(1.02 + sngPercent)End SubEnd Class

We can now write code to exercise these classes:

' Define new objectsDim ThePresident As New CExecutive( )Dim MySecretary As New CSecretary( )' Set the salariesThePresident.Salary = 1000000MySecretary.Salary = 30000' Set Employee to President and inc salaryDebug.Writeline("Pres before: " & CStr(ThePresident.Salary))ThePresident.IncSalary(0.4)Debug.WriteLine("Pres after: " & CStr(ThePresident.Salary))

88

Debug.Writeline("Sec before: " & CStr(MySecretary.Salary))MySecretary.IncSalary(0.3)Debug.Writeline("Sec after: " & CStr(MySecretary.Salary))

The output in this case is:

Pres before: 1000000Pres after: 1450000Sec before: 30000Sec after: 39600

The notion of inheritance is quite simple, as put forth in Microsoft's documentation:If Class B inherits from Class A, then any object of Class B is also an object of Class A and soincludes the public properties and methods (that is, the public interface) of Class A. In this case, ClassA is called the base class and Class B is called the derived class. On the other hand, in general, thederived class can override the implementation of a member of the base class for its own use.We have seen in the previous example that inheritance is implemented using the Inherits keyword.

3.4.1 Permission to Inherit

There are two keywords used in the base class definition that affect the ability to inherit from a baseclass:

NotInheritable

When this is used to define a class, as in:

Public NotInheritable Class InterfaceExample

the class cannot be used as a base class.

MustInherit

When this is used to define a class, as in:

Public MustInherit Class InterfaceExample

objects of this class cannot be created directly. Objects of a derived class can be created,however. In other words, MustInherit classes can be used as base classes and only asbase classes.

3.4.2 Overriding

There are several keywords that control whether a derived class can override an implementation in thebase class. These keywords are used in the declaration of the member in question, rather than in theclass definition:

Overridable

Allows but does not require a member to be overridden. Note that the default for a Public

member is NotOverridable. Here is an example:

Public Overridable Sub IncSalary( )

89

NotOverridable

Prohibits overriding of the member. This is the default for Public members of a class.

MustOverride

Must be overridden. When this keyword is used, the member definition is restricted to just thedeclaration line, with no implementation and no End Sub or End Function line. For example:

Public MustOverride Sub IncSalary( )

Note also that when a class module contains a MustOverride member, then the class itselfmust be declared as MustInherit.

Overrides

Unlike the other modifiers, this modifier belongs in the derived class and indicates that themodified member is overriding a base class member. For example:

Overrides Sub IncSalary( )

3.4.3 Rules of Inheritance

In many object-oriented languages, such as C++, a class can inherit directly from more than one baseclass. This is referred to as multiple inheritance . VB .NET does not support multiple inheritance, andso a class can inherit directly from at most one other class. Thus, code such as the following is notpermitted:

' Executive ClassPublic Class CExecutive 'INVALIDInherits CEmployeeInherits CWorker. . .End Class

On the other hand, Class C can inherit from Class B, which, in turn, can inherit from Class A, thusforming an inheritance hierarchy. Note also that a class can implement multiple interfaces through the

Interface keyword. We discuss this issue later in this chapter.

3.4.4 MyBase, MyClass, and Me

The keyword MyBase provides a reference to the base class from within a derived class. If you want tocall a member of the base class from within a derived class, you can use the syntax:

MyBase.MemberName

where MemberName is the name of the member. This will resolve any ambiguity if the derived classalso has a member of the same name.The MyBase keyword can be used to call the constructor of the base class in order to instantiate amember of that class, as in:

MyBase.New()

Note that MyBase cannot be used to call Private class members.

90

Visual Basic looks for the most immediate version in parent classes of the procedure in question. Thus,if Class C derives from Class B, which derives from Class A, a call in Class C to:

MyBase.AProc

first looks in Class B for a matching procedure named AProc. If none is found, then VB looks in ClassA for a matching procedure. (By matching, we mean a method with the same argument signature.)The keyword MyClass provides a reference to the class in which the keyword is used. It is similar tothe Me keyword, except when used to call a method. To illustrate the difference, consider a classnamed Class1 and a derived class named Class1Derived. Note that each class has an IncSalarymethod:

Public Class Class1Public Overridable Function IncSalary(ByVal sSalary As Single) _As SingleIncSalary = sSalary * CSng(1.1)End FunctionPublic Sub ShowIncSalary(ByVal sSalary As Single)MsgBox(Me.IncSalary(sSalary))MsgBox(MyClass.IncSalary(sSalary))End SubEnd ClassPublic Class Class1DerivedInherits Class1Public Overrides Function IncSalary(ByVal sSalary As Single) _As SingleIncSalary = sSalary * CSng(1.2)End FunctionEnd Class

Now consider the following code, placed in a form module:

Dim c1 As New Class1( )Dim c2 As New Class1Derived( )Dim c1var As Class1c1var = c1c1var.IncSalary(10000) ' Shows 11000, 11000c1var = c2c1var.IncSalary(10000) ' Shows 12000, 11000

The first call to IncSalary is made using a variable of type Class1 that refers to an object of typeClass1. In this case, both of the following calls:

Me.IncSalaryMyClass.IncSalary

return the same value, because they both call IncSalary in the base class Class1.

91

However, in the second case, the variable of type Class1 holds a reference to an object of the derivedclass, Class1Derived. In this case, Me refers to an object of type Class1Derived, whereas MyClass

still refers to the base class Class1 wherein the keyword MyClass appears. Thus,

Me.IncSalary

returns 12000 whereas the following:

MyClass.IncSalary

returns 10000.

3.5 Interfaces, Abstract Members, and Classes

We have alluded to the fact that a class may implement all, some, or none of the members of theinterfaces that it defines. Any interface member that does not have an implementation is referred to asan abstract member. The purpose of an abstract member is to provide a member signature (a

template, if you will) that can be implemented by one or more derived classes, generally in differentways.Let us clarify this with an example. Recall from our discussion of inheritance that the CEmployee classdefines and implements an IncSalary method that increments the salary of an employee. Recall alsothat the CExecutive and CSecretary derived classes override the implementation of the IncSalarymethod in the base class CEmployee.Suppose that, in a more complete employee model, there is a derived class for every type ofemployee. Moreover, each of these derived classes overrides the implementation of the IncSalarymethod in the base class CEmployee. In this case, the implementation of IncSalary in the base classwill never need to be called! So why bother to give the member an implementation that will never beused?Instead, we can simply provide an empty IncSalary method, as shown here:

' Employee classPublic Class CEmployee. . .Public Overridable Sub IncSalary(ByVal sngPercent As Single)End SubEnd Class

Alternatively, if we want to require that all derived classes implement the IncSalary method, we canuse the MustOverride keyword, as shown here:

' Employee classPublic MustInherit Class CEmployee. . .Public MustOverride Sub IncSalary(ByVal sngPercent As Single)End Class

As mentioned earlier, when using MustOverride, there is no End Sub statement associated with themethod. Note also that when using the MustOverride keyword, Microsoft requires that the class be

92

declared with the MustInherit keyword. This specifies that we cannot create objects of typeCEmployee.In each of the previous cases, the IncSalary member of the base class CEmployee is an abstractmember.Any class that contains at least one abstract member is termed an abstract class. (Thus, theCEmployee class as defined earlier is an abstract class.) This terminology comes from the fact that itis not possible to create an object from an abstract class because at least one of the object's methodswould not have an implementation.There are also situations where we might want to define a class in which all members are abstract. Inother words, this is a class that only defines an interface. We might refer to such a class as a pureabstract class, although this terminology is not standard.For example, imagine a Shape class called CShape that is designed to model the general propertiesand actions of geometric shapes (ellipses, rectangles, trapezoids, etc.). All shapes need a Drawmethod, but the implementation of the method varies depending on the type of shape—circles aredrawn quite differently than rectangles, for example. Similarly, we want to include methods calledRotate, Translate, and Reflect, but, as with the Draw method, each of these methods require adifferent implementation based on the type of shape.Thus, we can define the CShape class in either of the following ways:

Public Class Class2Public Overridable Sub Draw( )End SubPublic Overridable Sub Rotate(ByVal sngDegrees As Single)End SubPublic Overridable Sub Translate(ByVal x As Integer, _ByVal y As Integer)End SubPublic Overridable Sub Reflect(ByVal iSlope As Integer, _ByVal iIntercept As Integer)End SubEnd Class

or:

Public MustInherit Class CShapePublic MustOverride Sub Draw( )Public MustOverride Sub Rotate(ByVal sngDegrees As Single)Public MustOverride Sub Translate(ByVal x As Integer, _ByVal y As Integer)Public MustOverride Sub Reflect(ByVal iSlope As Integer, _ByVal iIntercept As Integer)End Class

Now we can define derived classes such as CRectangle, CEllipse, CPolygon, and so on. Each ofthese derived classes will (or must, in the latter case) implement the members of the base classCShape. (We won't go into the details of such an implementation here, since it is not relevant to ourdiscussion.)

93

3.5.1 Interfaces Revisited

We have seen that interfaces can be defined in class modules. VB .NET also supports an additionalmethod of defining an interface, using the Interface keyword. The following example defines theIShape interface:

Public Interface IShapeSub Draw( )Sub Rotate(ByVal sngDegrees As Single)Sub Translate(ByVal x As Integer, ByVal y As Integer)Sub Reflect(ByVal iSlope As Integer, _ByVal iIntercept As Integer)End Interface

Note that we cannot implement any of the members of an interface defined using the Interface

keyword, that is, not within the module in which the interface is defined. However, we can implementthe interface using an ordinary class module. Note the use of the Implements statement (which wasalso available in VB 6, but could be applied only to external interfaces):

Public Class CRectangle' Implement the interface IShapeImplements IShapePublic Overridable Sub Draw( ) Implements IShape.Draw' code to implement Draw for rectanglesEnd SubPublic Overridable Sub Spin( ) Implements IShape.Rotate' code to implement Rotate for rectanglesEnd SubEnd Class

Note also the use of the Implements keyword in each function that implements an interface member.This keyword allows us to give the implementing function any name—it does not need to match thename of the method (see the Spin method earlier in this section, which implements the IShape

interface's Rotate method). However, it is probably less confusing (and better programming practice)to use the same name.The main advantage of using the Implements keyword approach to defining an interface is that asingle class can implement multiple interfaces, whereas VB .NET does not permit a single class toinherit directly from multiple base classes. On the other hand, the main disadvantage of the

Interface keyword approach is that no implementation is possible in the module that defines theinterface. Thus, all interface members must be implemented in every class that implements theinterface. This can mean code repetition if an interface member has the same implementation in morethan one implementing class.

3.6 Polymorphism and Overloading

Fortunately, we don't need to go into the details of polymorphism and overloading, which is just as well,because they tend to be both confusing and ambiguous. For instance, some computer scientists saythat overloading is a form of polymorphism, whereas others say it is not. We will discuss only thoseissues that are directly relevant to the .NET Framework.

3.6.1 Overloading

94

Overloading refers to an item being used in more than one way. Operator names are often overloaded.For instance, the plus sign (+) refers to addition of integers, addition of singles, addition of doubles,and concatenation of strings. Thus, the plus symbol (+) is overloaded. It's a good thing, too; otherwise,we would need separate symbols for adding integers, singles, and doubles.Function names can also be overloaded. For instance, the absolute value function, Abs, can take aninteger parameter, a single parameter, or a double parameter. Because the name Abs representsseveral different functions, it is overloaded. In fact, if you look at the documentation for the Absmember of the Math class (in the system namespace of the Base Class Library), you will find thefollowing declarations, showing the different functions using the Abs name:

Overloads Public Shared Function Abs(Decimal) As DecimalOverloads Public Shared Function Abs(Double) As DoubleOverloads Public Shared Function Abs(Integer) As ShortOverloads Public Shared Function Abs(Integer) As IntegerOverloads Public Shared Function Abs(Long) As LongOverloads Public Shared Function Abs(SByte) As SByteOverloads Public Shared Function Abs(Single) As Single

Note the use of the Overloads keyword, which tells VB that this function is overloaded.Specifically, a function name is overloaded when two defined functions use the same name but havedifferent argument signatures. For instance, consider a function that retrieves a current accountbalance. The account could be identified either by the person's name or by the account number. Thus,we might define two functions, each called GetBalance:

Overloads Function GetBalance(sCustName As String) As DecimalOverloads Function GetBalance(sAccountNumber As Long) As Decimal

Note also that VB .NET permits function overloading only because the argument signatures of the twofunctions are different, so that no ambiguity can arise. The function calls:

GetBalance("John Smith")GetBalance(123456)

are resolved by the compiler without difficulty, based on the data type of the argument. This type ofoverloading is often referred to as overloading the function GetBalance. On the other hand, there aretwo different functions here, so it seems more appropriate to say that the function name is beingoverloaded. Overloading is very common and not exclusive to object-oriented programming.

3.6.2 Polymorphism

The term polymorphism means having or passing through many different forms. In the .NETFramework, polymorphism is tied directly to inheritance. Again, let us consider our Employee example.The function IncSalary is defined in three classes: the base class CEmployee and the derived classesCExecutive and CSecretary. Thus, the IncSalary function takes on three forms. This is polymorphism,VB .NET style.In case you are interested, many computer scientists would not consider this to be polymorphism.They would argue that the function IncSalary takes on only one form. It is the implementation thatdiffers, not the function. They would refer to the situation described here for IncSalary as functionoverloading. The main point here is that there is a lot of confusion as to how Microsoft and others usethe terms overloading and polymorphism, so you should be on guard when reading documentation.

95

3.7 Scope and Accessibility in Class Modules

The notion of scope in class modules is more involved than it is in standard modules. As far as localvariables (block-level and procedure-level) are concerned, there is no difference—we have blockscope and procedure-level scope.However, variables declared in the Declarations section of a class module can be assigned one of thefollowing access modifiers:

· Public

· Private

· Friend

· Protected

· Protected Friend

(For standard modules, only Public, Private, and Friend are allowed.)Note that class modules themselves can be declared with any one of the three access modifiers:

Public, Private, or Friend (Protected is not allowed). When a class module declarationspecifies one of these access modifiers, this simply restricts all of its members to that level of access,unless a member's access is further restricted by the access modifier on the member declaration itself.For instance, if the class has Friend access, no member can have Public access. (Put another way,the Public access is overridden by the Friend class access.)On the other hand, all four access modifiers apply to members of the class module?that is, to variable,constant, enum, and procedure declarations within the class module.The complications come because there are actually three types of access to a class member, andthese generally have different scopes. To clarify, let's make the following definitions, which are notstandard but descriptive. For example, consider a variable declaration in the Declaration section of aclass module named Class1:

AccessModifier classvariable As Integer

This variable can be accessed in the following ways:

Direct access

Refers to accessing the member without any qualification, as in:

classvariable = 100

When attempting to access a variable using direct access (that is, without qualification), thevariable's scope takes one of three forms:

· The declaring class only

· The declaring class and its derived classes within the declaring project only

· The declaring class and its derived classes, in any project that holds a reference tothe declaring project

Class/object access

Refers to accessing the member through qualification, either with the class name or the nameof an object of that class.

96

As we have discussed, if a member variable is declared using the Shared keyword, then it isshared by all objects in the class. More accurately, the member exists independently of anyobject of the class. In this case, the member can be accessed (within its scope) throughqualification by the class name, as in:

Class1.classvariable = 100

Note that the member can also be accessed through qualification by an object name, but thishas the same effect as access through qualification by the class name—there is only one copyof the member.If the member is declared without using the Shared keyword, then class/object access refersto accessibility through qualification by the name of an existing object, as in:

Dim c As New Class1c.classvariable = 100

The scope for class/object access can be one of the following:

· The declaring class only

· The declaring project

· The declaring project and any external software component that holds a reference tothe declaring project

Table 3-2 describes the effects of the various access modifiers.

Table 3-2. Access modifiers in class modules

Direct-access scope Class/object scope

Private Declaring class Declaring classProtected All derived classes Declaring classFriend Derived in-project classes Declaring projectProtected Friend All derived classes Declaring projectPublic All derived classes All projectsUnfortunately, it does not seem possible to make a simple statement about the effect of the accessmodifiers Friend and Protected independently. It would have been much clearer to have separatesets of access modifiers for direct-access scope and class/object scope, instead of intertwining theconcepts as shown in Table 3-2. Oh well.

97

Chapter 4. The .NET Framework: General Concepts

In this chapter, we discuss some of the main concepts in the .NET Framework. This is intended as ageneral overview, just to give you the "lay of the .NET land," so to speak. For more information, seeThuan Thai and Hoang Q. Lam's .NET Framework Essentials (O'Reilly, 2001).

4.1 Namespaces

The notion of a namespace plays a fundamental role in the .NET Framework. In general, a

namespace is a logical grouping of types for the purpose of identification. For example, imagine that ina certain business there is an executive named John Smith, a secretary named John Smith, and acustodian named John Smith.In this case, the name John Smith is ambiguous. When the paymaster stands on a table and calls outthe names of people to receive their pay checks, the executive John Smith won't be happy if he rushesto the table when the paymaster calls out his name and the envelope contains the custodian JohnSmith's pay check.To resolve the naming ambiguity, the business can simply define three namespaces: Executive,Secretarial, and Custodial. Now the three individuals can be unambiguously referred to by their fullyqualified names:

· Executive.John Smith

· Secretarial.John Smith

· Custodial.John SmithThe .NET Framework Class Library (FCL), which we look at in more detail in Chapter 5, consists ofseveral thousand classes and other types (such as interfaces, structures, and enumerations) that aredivided into over 90 namespaces. These namespaces provide basic system services, such as:

· Basic and advanced data types and exception handling (the System namespace)

· Data access (the System.Data namespace)

· User-interface elements for standard Windows applications (the System.Windows.Formsnamespace)

· User-interface elements for web applications (the System.Web.UI namespace)In fact, the VB .NET language itself is implemented as a set of classes belonging to theMicrosoft.VisualBasic namespace. (The C# and JScript languages are also implemented as a set ofclasses in corresponding namespaces.)For information on accessing the members of a namespace, see Section 4.5 later in this chapter.Namespaces are not necessarily unique to the Framework Class Library; you can also create yourown namespaces by using the Namespace statement at the beginning of a code file.

4.2 Common Language Runtime (CLR), Managed Code, and ManagedData

The Common Language Runtime (CLR) is an environment that manages code execution and providesapplication-development services. Compilers such as VB .NET expose the CLR's functionality toenable developers to create applications. Code that is created under this environment is called

managed code . Note that COM components are not managed code, although they (as well as otherunmanaged code) can be used in applications that are built under the CLR.

98

The output of a compiler includes metadata, which is information that describes the objects that arepart of an application, such as:

· Data types and their dependencies

· Objects and their members

· References to required components

· Information (including versioning information) about components and resources that wereused to build the applicationMetadata is used by the CLR to do such things as:

· Manage memory allocations

· Locate and load class instances

· Manage object references and perform garbage collection

· Resolve method invocations

· Generate native code

· Make sure that the application has the correct versions of necessary components andresources

· Enforce securityThe metadata in a compiled software component makes the component self-describing. This impliesthat components, even those written in another language, can interact with the given componentdirectly.Objects that are managed by the CLR are referred to as managed data. (It is also possible to useunmanaged data in applications.)

4.3 Managed Execution

Managed execution is the name given for the process of creating applications under the .NETFramework. The steps involved are as follows:1. Write code using one or more .NET compilers. Note that for software components to beuseable by components that are written in other languages, these components must be writtenusing only language features that are part of the Common Language Specification (CLS).2. Compile the code. The compiler translates source code to Microsoft Intermediate Language(MSIL) and generates the necessary metadata for the application.3. Run the code. When code is executed, the MSIL is compiled into native code (which is CPUspecificcode that runs on the same computer architecture as the compiler) by a Just In Time

(JIT) compiler. If required, the JIT checks the code for type safety. If the type-safety check fails,an exception is thrown.Code that cannot access invalid memory addresses or perform other illegal operations that may resultin an application crash is called type-safe code. Code that is verified to be type-safe by the JIT iscalled verifiably type-safe code. Due to limitations in the verification process, code can be type-safeand yet not be verifiably type-safe.

4.4 Assemblies

The purpose of an assembly is to specify a logical unit, or building block, for .NET applications that

encapsulate certain properties.The term assembly refers to both a logical construct and a set of physical files. To draw an analogy onthe logical side, we might use the term neighborhood to refer to a zip code, a neighborhood name, anda list of street addresses. On the physical side, a neighborhood consists of the actual houses that are

99

located at those addresses. Thus, we can speak of physically moving (i.e., deploying) theneighborhood.A .NET application consists of one or more assemblies. Logically speaking, an assembly is just a setof specifications. In particular:

· An assembly specifies the (MSIL) code that is associated with the assembly. This code lies ina Portable Executable (PE) file. (PE files are the traditional file types for Microsoft's code, butthe format is extended for .NET applications.)

· An assembly specifies security permissions for itself, if any.

· An assembly specifies a list of data types and provides scoping for those types. Every datatype in a .NET application must specify the assembly to which it belongs. The scopingprovided by an assembly means that different types may have the same name, as long asthey belong to different assemblies and can therefore be distinguished by means of theassembly to which they belong. Microsoft refers to this by saying that an assembly providesatype boundary.

· An assembly specifies rules for resolving external types and external references, includingreferences to other assemblies. In this way, assemblies form a reference scope boundary.Included in this information are any version dependencies for the external references.

· An assembly specifies which of its parts are exposed outside the assembly and which areprivate to the assembly itself.In addition to these specifications listed, an assembly is an object (or logical unit) that possessescertain properties:

· An assembly has version properties. This includes a major and minor version number, as wellas a revision and build number. Indeed, an assembly is the smallest unit that has versioningproperties. Put another way, all elements of an assembly (types and resources) are versionedas a unit—they are assigned the version numbers of the assembly to which they belong. Inother words, an assembly is a versioning unit.

· An assembly forms a deployment unit. More specifically, at any given time, a .NET applicationonly needs access to the assemblies that specify the code under execution. Other assembliesthat make up the application need not be present if the code they specify is not currentlyneeded for execution. These assemblies can be retrieved upon demand, so that thedownloading of applications can be more efficient.Finally, we note that multiple versions of a single assembly can be run at the same time, on the samesystem, or even in the same process. This is referred to as side-by-side execution.The specifications in an assembly are collectively referred to as the assembly's manifest. The data inthe manifest is also called metadata. Specifically, the manifest contains:

· The name of the assembly

· Version information for the assembly

· Security information for the assembly

· A list of all files that are part of the assembly

· Type reference information for the types specified in the assembly

· A list of other assemblies that are referenced by the assembly

· Custom information, such as a user-friendly assembly title, description, and productinformation (company name, copyright information, and so on)Physically, an assembly consists of one or more files—files that contain code, as well as resources,such as bitmaps. The assembly's manifest can be a separate file or part of another file in the assembly.

100

4.5 Assemblies and VB .NET

To a VB .NET programmer, an assembly is similar to a traditional DLL or EXE file, except that itcontains additional information, such as reference and type information (which in COM was oftencontained in a separate OLB or TLB file, called a type library). When a VB .NET application iscompiled, the compiler creates an assembly for the target EXE or DLL.In the .NET environment, namespaces are part of assemblies. An assembly can contain manynamespaces, and namespaces can be nested.For instance, the System namespace is the fundamental namespace in the .NET environment. This isnot the time to go into details, but one example will be useful. The System namespace identifies theArray class (Microsoft likes to say that the namespace contains classes.) One of the members of theArray class is the Copy method, which copies a portion of one array to another array. Thus, we canwrite code such as the following:

Imports System ' Optional since System is always importedDim array1( ) As Integer = {1, 2, 3, 4}Dim array2(3) As IntegerArray.Copy(array1, array2, 3)

To use an existing assembly in a VB .NET project, you must do two things:

· Add a reference to the assembly to your project. There are two exceptions to this rule,however. A reference to the assembly containing the System namespace (mscorlib.dll) isadded automatically, as is a reference to the assembly containing the language being used(for VB .NET, this is Microsoft.VisualBasic.dll).

· Access the member or members of the namespace, as described later in this section.To access a member of a namespace, you can use its fully qualified name. For example, to create aninstance of the Timers class, which is found in the System.Timers namespace, you can use a codefragment like the following:

Dim oTimer As New System.Timers.Timer(2000)

Since using fully qualified names tends to be relatively cumbersome, you can include an Imports

statement at the beginning of a code file, before any references to variables or classes. Its syntax is:

Imports [aliasname = ] namespace

where aliasname is an optional alias for the namespace, and namespace is its fully qualified name.For example, if you import the System.Timers namespace as follows:

Imports System.Timers

you do not have to qualify a reference to the Timer class, which can be instantiated as follows:

Dim oTimer As New Timer(2000)

In the event that there is a naming conflict (either two namespaces have identically named types, or anamed type conflicts with a name in your project), you can specify an alias for the namespace, asfollows:

Imports TI = System.Timers

and then instantiate a Timer object as follows:

101

Dim oTimer As New TI.Timer(2000)

If you're using the Visual Basic command-line compiler, you haveto explicitly import the Microsoft.VisualBasic namespace, or yourcode will not compile. If you're using Visual Studio, VB's languageelements are accessed automatically without your having toimport the namespace.

102103

Chapter 5. The .NET Framework Class Library

VB .NET is about classes, classes, and more classes. Even something as simple as a string isimplemented in a class (the String class of the System namespace). As we mentioned in Chapter 4,the .NET Framework defines an extensive network of classes and namespaces called the FrameworkClass Library (FCL). This consists of a set of namespaces called the Base Class Library (BCL) thatprovide basic system services (like the System namespace, whose classes define .NET data types,provide exception handling, and handle garbage collection, among other things). It also includesadditional namespaces, such as System.Data, System.Windows.Forms, and System.Web.UI, whichprovide application services. In total, there are over 90 namespaces containing several thousandclasses, interfaces, structures, enumerations, and other items (such as delegates) in the .NETFramework Class Library.The System namespace is at the top of the namespace hierarchy, and the Object class is at the top ofthe object hierarchy. All types in the .NET Framework Class Library derive from the Object class.The .NET Framework Class Library is sufficiently extensive to require an entire book for its description.In this chapter, we provide just a brief introduction and some examples. This should prepare you todive into the Microsoft Class Library documentation. Note also that the reference portion of this book,

Chapter 8, documents selected language elements from the Base Class Library that seemparticularly useful to VB programmers. For more on which classes are included in the referencesection, see its introduction.Before becoming intimidated by the size of the Framework Class Library, we should also keep in mindthat VB .NET provides wrappers for much of the Base Class Library in particular, so we can often justcall a VB function rather than resort to accessing the classes in the Base Class Library directly. Moregenerally, while the class library does have much to offer a VB programmer and should not be ignored,it can be studied and used on an "as needed" basis.Let us illustrate a simple case in which the BCL has something to offer. We mentioned in Chapter 2

that the built-in VB data types are wrappers for a corresponding BCL class (for reference types) orstructure (for value types). However, the Visual Basic language typically does not implement all of themembers of the BCL class. For instance, if we want to verify that a user has entered a number that lieswithin the range of type Integer, we can use code such as the following:

Dim s As StringDim i As Integers = InputBox("Enter an integer")If IsNumeric(s)If (CDbl(s) >= i.MinValue) And (CDbl(s) <= i.MaxValue) Theni = CInt(s)ElseDebug.WriteLine("Invalid number")End IfElseDebug.WriteLine("Non-numeric value")End If

Because the VB Integer data type is a wrapper for the BCL's Int32 structure, the MinValue andMaxValue properties of the Int32 data type are accessible to the Integer variable i. Incidentally,because the MaxValue and MinValue members are shared, we could also have written:

If IsNumeric(s)If (CDbl(s) >= Integer.MinValue) _And (CDbl(s) <= Integer.MaxValue) Then

104

which, in my opinion, is more readable.In order to prevent a compiler error when compiling this code with Option Strict On, we've convertedthe String s to a Double before comparing its value with the Integer class's MinValue and MaxValueproperties. This is because a Double is the least restrictive numeric data type, and we want to be surethat the numeric equivalent of the String s is within the range of a more restrictive numeric (integer)data type.

5.1 The System Namespace

The System namespace contains classes for such broad ranging things as:

· Data types

· Data type conversions

· Method-parameter manipulation

· Events and event handlers

· Mathematics

· Program invocation

· Application-environment management

5.1.1 Data Type Conversion

To illustrate data type conversion, the System namespace defines a class called Convert. If you checkthe documentation, you'll find that one of the methods of the Convert class is ToBoolean. Thedocumentation lists the following for ToBoolean:

Overloads Public Shared Function ToBoolean(String) As BooleanOverloads Public Shared Function ToBoolean(Double) As BooleanOverloads Public Shared Function ToBoolean(Single) As BooleanOverloads Public Shared Function ToBoolean(Char) As BooleanOverloads Public Shared Function ToBoolean(Byte) As BooleanOverloads Public Shared Function ToBoolean(Object) As BooleanOverloads Public Shared Function ToBoolean(Boolean) As BooleanOverloads Public Shared Function ToBoolean(Long) As BooleanOverloads Public Shared Function ToBoolean(Integer) As Boolean

As you can see, there are many ToBoolean functions?each one with a different argument signature?totake care of converting various data types to Boolean.Now, just for exercise, we can write:

Dim s As StringDim b As Booleans = "false"b = System.Convert.ToBoolean(s)msgbox(b)

Because the System namespace is always available (or if we are programming outside of VisualStudio, we can import it using the Imports statement), we can omit the System qualifier and write:

b = Convert.ToBoolean(s)

Of course, we can also use the built-in VB .NET function CBool.

105

The Convert class contains methods for converting data to the standard Visual Basic data types, aswell as to the data types supported by the .NET Framework but not wrapped by Visual Basic, such asthe unsigned-integer data types. The most important of these methods are shown in Table 5-1.

Table 5-1. Members of the System.Convert class

Method Description

ToBoolean Converts a value to a BooleanToByte Converts a value to a ByteToChar Converts a value to a CharToDateTime Converts a value to DateTime (Date in Visual Basic)ToDecimal Converts a value to DecimalToDouble Converts a value to DoubleToInt16 Converts a value to Int16 (Short in Visual Basic)ToInt32 Converts a value to Int32 (Integer in Visual Basic)ToInt64 Converts a value to Int64 (Long in Visual Basic)ToSByte Converts a value to SByte, the unsigned-byte data type in the BCLToSingle Converts a value to SingleToString Converts a value to StringToUInt16 Converts a value to UInt16, an unsigned 16-bit integerToUInt32 Converts a value to UInt32, an unsigned 32-bit integerToUInt64 Converts a value to UInt64, an unsigned 64-bit integer

5.1.2 The Array Class

The Array class contains useful methods for dealing with arrays. For instance, the Array object has aSort method (at last) that sorts the elements of an array. Here is an example:

Sub sortArray( )Dim i As IntegerDim intArray( ) As Integer = {9, 8, 12, 4, 5}For i = 0 To 4console.WriteLine(CStr(intArray(i)))NextArray.Sort(intarray)Console.WriteLine("Sorted:")For i = 0 To 4console.WriteLine(intArray(i))NextEnd Sub

The output is:

981245Sorted:458912

106

Some of the more important methods of the Array class are shown in Table 5-2.

Table 5-2. Some members of the System.Array class

Method Description

BinarySearch Searches a sorted one-dimensional array for a valueIndexOf Returns the first occurrence of a particular value in a one-dimensional arrayLastIndexOf Returns the last occurrence of a particular value in a one-dimensional arrayReverse Reverses the order of the elements in a one-dimensional array or a portion of a onedimensionalarraySort Sorts a one-dimensional array

5.1.3 The Math Class

The Math class has a number of mathematical-function methods (such as trigonometric functions), aswell as some more useful methods, such as Max and Min. Therefore, we can just write:

MsgBox(Math.Max(4,7))

Table 5-3 shows the members of the Math class.

Table 5-3. The members of the Math class

Topic Description

Abs function Absolute valueAcos function ArccosineAsin function ArcsineAtan function Arctangent; returns the angle whose tangent is a specified numberAtan2 function Arctangent; returns the angle whose tangent is the quotient of two specifiednumbersCeiling function Returns the smallest integer greater than or equal to the argument numberCos function CosineCosh function Hyperbolic cosineE field The natural number eExp function Exponential functionFloor function Returns the largest integer less than or equal to the argument numberIEEERemainderfunction Returns the remainder after dividing x by yLog function Natural (base e) logarithmLog10 function Common (base 10) logarithmMax function MaximumMin function MinimumMod operator Returns the modulus, that is, the remainder when number1 is divided bynumber2Pi field Pi, the ratio of the circumference of a circle to its diameterPow function Generalized exponential functionRandomize statement Initializes the random number generatorRnd function Returns a random numberRound function Rounds a given number to a specified number of decimal places

107

Sign function Determines the sign of a numberSin function SineSinh function Hyperbolic sineSqrt function Square rootTan function TangentTanh function Hyperbolic tangent

5.1.4 The String Class

The String class implements a collection of methods for string manipulation, including methods forlocating substrings, concatenation, replacement, padding, trimming, and so on. One interestingmethod is Insert, which inserts a new string into an existing string.The VB .NET String data type is equivalent to the System.String class, so we can apply the methodsof System.String directly to VB strings, as in:

Dim s As String = "To be to be"msgbox(s.Insert(6, "or not "))

This displays the message "To be or not to be." Table 5-4 shows the members of the String class.

Table 5-4. The members of the String class

Topic Description

Asc, AscW functionsReturns an Integer representing the character code for the firstcharacter of the string passed to it. All other characters in the stringare ignored.Chr, ChrW functions Returns the character represented by the character code.Filter function Produces an array of matching values from an array of sourcevalues that either match or do not match a given filter string.Format function Allows you to use either predefined or user-defined formats to createvarious ways to output string, numeric, and date/time data.FormatCurrency,FormatNumber, FormatPercentfunctionsUsed to format currency, numbers, and percentages.FormatDateTime function Formats a date or time expression based on the computer's regionalsettings.GetChar function Returns the Char that is at a given position index within a givenstring.InStr function Finds the starting position of one string within another.InstrRev function Determines the starting position of a substring within a string bysearching from the end of the string to its beginning.Join function Concatenates an array of values into a delimited string using aspecified delimiter.LCase function Converts a string to lowercase.Left function Returns a string containing the leftmost length characters of string.Len function Counts the number of characters within a string or the size of agiven variable.Like operatorIf string matches pattern, results in True; otherwise, results in

False.LTrim function The Me operator represents the current instance of a class fromwithin the class module. (Since a form is a class, this includes forms

108

as well.)Mid function Returns a substring of a specified length from a given string.Mid statement Replaces section of a string with characters from another string.Replace function Replaces a given number of instances of a specified substring inanother string.Right function Returns a string containing the rightmost length characters of string.RTrim function Removes any trailing spaces from stringexp.Space function Creates a string containing number spaces.Split function Parses a single string containing delimited values into an array.StrComp function Determines whether two strings are equal and, if not, which of thetwo strings has the greater value.StrConv function Performs special conversions on a string.StrDup function Returns a string that consists of the first character of stringduplicated a number of times.StrReverse functionReturns a string that is the reverse of the string passed to it. Forexample, if the string "and" is passed to it as an argument,StrReverse returns the string "dna."Trim function Removes both leading and trailing spaces from a given string.UCase function Converts a string to uppercase.

5.2 Other Namespaces

Nested just below the System namespace are a number of second-level namespaces, which containsuch classes as:

System.CodeDOM

Contains classes representing the elements and structure of a source code document.

System.Collections

Contains interfaces and classes that define various collections of objects, such as lists,queues, arrays, hashtables, and dictionaries.

System.ComponentModel

Contains classes that are used to implement the runtime and design-time behavior ofcomponents and controls.

System.Configuration

Contains classes that allow the creation of custom installers for software components.

System.Data

Consists mostly of the classes that constitute the ADO.NET architecture and are used fordatabase connectivity.

System.Diagnostics

Contains classes that allow debugging of applications and code tracing.

System.DirectoryServices

109

Contains classes that provide access to the Active Directory from managed code.

System.Drawing

Contains classes that provide access to GDI+ basic graphics functionality. (More advancedfunctionality is provided in the System.Drawing.Drawing2D, System.Drawing.Imaging, andSystem.Drawing.Text namespaces.)

System.IO

Contains classes that allow synchronous and asynchronous reading from and writing to datastreams and files.

System.Net

Contains classes that provide a simple programming interface to many of the commonnetwork protocols, such as FTP and HTTP. (The System.Net.Sockets namespace provideslower-level network access control.)

System.Reflection

Contains classes and interfaces that provide a managed view of loaded types, methods, andfields, with the ability to create and invoke types dynamically.

System.Resources

Contains classes for managing resources (culture-specific resources and resource files).

System.Security

Contains classes that provide access to the underlying structure of the .NET Frameworksecurity system.

System.ServiceProcess

Contains classes that allow us to install and run services. (Services are long-runningexecutables that run without a user interface.)

System.Text

Contains classes representing ASCII, Unicode, UTF-7, and UTF-8 character encodings, aswell as abstract base classes for converting blocks of characters to and from blocks of bytes,and more.

System.Text.RegularExpressions

Contains classes that provide access to the .NET Framework regular expression engine.

System.Threading

Provides classes and interfaces that enable multithreaded programming.

System.Timers

110

Contains classes that provide the Timer component, which allows you to raise an event on aspecified interval.

System.Web and related namespaces

Contain classes and interfaces that enable browser/server communication and that allow youto develop ASP.NET applications and web services.

System.Windows.Forms

Contains classes for creating Windows-based applications that take full advantage of the richuser-interface features available in the Microsoft Windows operating system. In thisnamespace, you will find the Form class and many other controls that can be added to formsto create user interfaces.

System.Xml

Contains classes that provide standards-based support for processing XML.Let's take a look at some of these other classes in the BCL.

5.2.1 System.Collections

This namespace contains classes for implementing a variety of collection types, such as stacks andqueues. As you may know, a queue is a first-in, first-out data structure. The following code illustratesthe use of the Queue class:

Dim s As StringDim q As New Collections.Queue( )q.Enqueue("To")q.Enqueue("be")q.Enqueue("or")q.Enqueue("not")Do While q.Count > 0s = s & " " & CStr(q.Dequeue)Loopmsgbox(s)

The output is "To be or not."

5.2.2 System.Data

System.Data and its nested namespaces, notably System.Data.OleDb and System.Data.SqlClient,provide data access using ADO.NET. The OleDb and SqlClient namespaces are responsible fordefining data providers that can connect to a data source, retrieve data from a data source, write databack to a data source, and execute commands against the data source. The most important class ineach of these namespaces is a data adapter class (in the OleDb namespace, it's theOleDbDataAdapter class; in the SqlClient namespace, it's the SqlDataAdapter class) which isresponsible for retrieving data from a data source and writing it to a dataset. A dataset in turn is acollection of related data that's disconnected from its original data source.

ADO.NET is not the same thing as ADO, nor is ADO.NET a newversion of ADO. Instead, ADO (or ActiveX Data Objects) is aCOM-based object model for data access. ADO.NET is an entirely

111

new model for data access that is based on the disconnecteddataset.

5.2.3 System.IO

The System.IO namespace contains classes that provide a variety of input/output functionality, suchas:

· Manipulating directories (Directory class) and files (File class)

· Monitoring changes in directories and files (FileSystemWatcher class)

· Reading and writing single bytes, mulitbyte blocks, or characters to and from streams

· Reading and writing characters to and from strings (StringReader and StringWriter)

· Writing and reading data types and objects to and from streams (BinaryWriter andBinaryReader)

· Providing random access to files (FileStream)It appears that, for VB programmers, the System.IO namespace and its classes are intended to takethe place of the FileSystemObject object model that is part of the Microsoft Scripting Runtime. TheSystem.IO namespace is certainly much more extensive. The File and Directory classes duplicate thefunctionality of the FileSystemObject. For more on these classes, see their entries in this book'sreference section.

5.2.4 System.Text.RegularExpressions

The System.Text.RegularExpressions namespace contains classes that provide access to the .NETFramework's regular expression engine. This is not the place to go into details about regularexpressions, but we can make a few comments.In its simplest form, a regular expression is a text string that represents a pattern that other stringsmay or may not match. In this way, regular expressions form a very powerful method of stringmatching. In more complicated forms, a regular expression is a kind of programming statement. Forinstance, the expression:

s/ab*c/def

says to match the given string against the regular expression ab*c (strings that start with ab and endwith c). If a match exists, then replace the given string with the string def. Here are some simpleregular expressions for pattern matching:

Single character

This is matched only by itself.

Dot (. )

This is matched by any character except the newline character.

[ string of characters]

This matches any single character that belongs to the string of characters. For example, [abc]

matches the single character a, b, or c. A dash can also be used in the character list, forinstance, [0-9] matches any single digit. We can also write [0-9a-z] to match any singledigit or any single lowercase character, and [a-zA-Z] to match any single lower- oruppercase character.

112

The ^ symbol can be used to negate the match. For instance, [^0-9] matches any character

except a digit.

Special match abbreviations

\d matches any single digit; \D matches any single nondigit.

\w is equivalent to [a-zA-Z_], thus matching any letter or underscore; \W is the negation of

\w.

Asterisk (* )

The occurrence of an asterisk within a regular expression gives a match if and only if there arezero or more repeated instances of the single character preceding the asterisk. For example,the regular expression \da*\d is matched by any string beginning with a single digit,continuing with zero or more as and ending with a single digit, as with 01 or 0aaa1.

Plus sign (+ )

The occurrence of a plus sign within a regular expression gives a match if and only if there areone or more repeated instances of the single character preceding the plus sign. For example,the regular expression \da+\d is matched by any string beginning with a single digit,continuing with one or more as and ending with a single digit, as with 0aaa1 (but not 01).

Question mark (? )

The occurrence of a question mark within a regular expression gives a match if and only ifthere are zero or one instances of the single character preceding the question mark. Forexample, the regular expression \da?\d is matched by any string beginning with a single digit,continuing with zero or one as and ending with a single digit, as with 01 and 0a1.

General multiplier

The occurrence of the substring {x,y}, where x and y are nonnegative integers within aregular expression, gives a match if and only if there are at least x but at most y instances ofthe single character preceding the opening bracket. For example, the regular expression

\da{5,10}\d is matched by any string beginning with a single digit, continuing with at least 5but at most 10 as and ending with a single digit, as with 0aaaaaa1.Note that you can leave out one of x or y. Thus, {x,} means "at least x," and {,y} means"at most y."The System.Text.RegularExpressions namespace has a Regex class, whose objects representregular expressions. Here is a simple example of the use of the Regex class:

' Define a new Regex object with pattern \da{3,5}\dDim rx As New System.Text.RegularExpressions.Regex("\da{3,5}\d")' Do some matchingMsgBox(rx.IsMatch("0a1")) ' Displays FalseMsgBox(rx.IsMatch("0aaa1")) ' Displays True

The System.Text.RegularExpressions namespace contains classes for string replacement as well, butwe do not go into these matters in this brief introduction.

113

5.2.5 System.Windows.Forms

This namespace is the mother of all namespaces for creating Windows applications. To quote thedocumentation:The System.Windows.Forms namespace contains classes for creating Windows-based applicationsthat take full advantage of the rich user interface features available in the Microsoft Windows operatingsystem. In this namespace you will find the Form class and many other controls that can be added toforms to create user interfaces.In fact, each new form added to a VB .NET project contains the line:

Imports System.Windows.Forms

Fortunately, Visual Studio provides the functionality of the System.Windows.Forms namespace to usas VB programmers, so we don't need to program directly against this namespace.

114115

Chapter 6. Delegates and Events

In this chapter, we discuss delegates and events, two additional .NET framework topics that areimportant to VB programmers.

6.1 Delegates

In a never-ending effort to deny VB programmers the right to use pointers, Microsoft has implementeda feature called delegates that, according to the documentation, provide a safe alternative to functionpointers.As you may know, a pointer variable (or pointer) is simply a variable whose value is interpreted by thecompiler as a memory address. The address to which the pointer points is the target of the pointer,and we say that the pointer variable points to that target address. If the target address is a variable ofdata type Integer, for example, then we say that the pointer is of type Integer or is an Integer pointer.Thus, the type of a pointer is the type of the target variable. (We have seen that, as reference types,variables of type Object and String are both pointers; i.e., their values point to the address of the datain memory.)A pointer can also point to a function, that is, contain the address of a function. Even though a functionis not a variable, it does have a physical location in memory and so can be the target of a pointer.(Actually, it's reasonable to think of a function as a type of variable, but that is another story.) In thiscase, we have a function pointer.Function pointers are very useful in certain situations for calling or specifying functions. This iscommonly done in the C++ programming language, where function pointers are supported directly.One area in which function pointers are used is in the context of callback functions. To illustrate, if wewant to enumerate all of the fonts on a given system, the Windows API provides a function called

EnumFontFamiliesEx, defined as follows:

Public Declare Function EnumFontFamiliesEx Lib "gdi32" _Alias "EnumFontFamiliesExA" ( _ByVal hdc As Long, _lpLogFont As LOGFONT, _ByVal lpEnumFontProc As Long, _ByVal lParam As Long, _ByVal dw As Long) _As Long

The third parameter requires the address of a function we must declare, called a callback function.The reason for this term is that Windows will call our callback function for each font in the system,passing information about the font in the parameters of the function. According to the documentation,the callback function must have a particular form:

Public Function EnumFontFamExProc(ByVal lpelfe As Long, _ByVal lpntme As Long, _ByVal FontType As Long,ByRef lParam As Long) As Long

The point here is that to use EnumFontFamiliesEx, we need to pass the address of a function as oneof the parameters.As you may know, this is done in VB using the AddressOf operator. In earlier versions of VB, thisoperator is described as follows:

116

A unary operator that causes the address of the procedure it precedes to be passed to an APIprocedure that expects a function pointer at that position in the argument list.Put another way, the AddressOf operator is implemented in VB 6 for the express purpose of passingfunction addresses to API functions.In VB .NET, the AddressOf operator returns a delegate, which is, as the documentation states:A unary operator that creates a procedure delegate instance that references the specific procedure.So let us discuss delegates. We begin with a rather unhelpful definition: a delegate is an object of aclass derived from either the Delegate class or the MulticastDelegate class. These two classes areabstract, so no objects of these classes can be created. Nevertheless, other classes can be derivedfrom these classes, and objects can be created from these derived classes.In VB .NET, delegates can be used to call methods of objects or to supply callback functions. Inaddition, VB .NET uses delegates to bind event handlers to events. Fortunately, VB .NET alsosupplies tools (such as the AddHandler method) to automate this process, so we don't need to usedelegates directly for this purpose.A delegate object inherits a number of properties and methods from the Delegate or MulticastDelegateclass. In particular, a delegate object has:

· A Target property that references the object or objects whose method or methods are to becalled.

· A Method property that returns a MethodInfo object that describes the method or methodsassociated with the delegate.

· An Invoke method that invokes the target method or methods.By now you have probably guessed that there are two delegate classes because delegates derivedfrom the Delegate class can only call a single method, whereas delegates derived fromMulticastDelegate can call multiple methods.

6.1.1 Using a Delegate to Call a Method

To call a method using a delegate, we call the Invoke method of the delegate. To illustrate, considerthe class module with a simple method:

Public Class Class1Public Sub AMethod(ByVal s As String)Msgbox(s)End SubEnd Class

Now, in a module with a Windows Form (referred to as a form module in earlier versions of VB), wedeclare a (single cast) delegate with the same parameters as the target method we wish to call:

Delegate Sub ADelegate(ByVal s As String)

The following code then uses the delegate to call the AMethod of Class1:

Protected Sub Form1_Click(ByVal sender As Object, _ByVal e As System.EventArgs) _Handles MyBase.Click' Object of type Class1 _Dim obj As New Class1( )

117

' Declare a new delegateDim delg As ADelegate' Define the delegate, passing the address' of the object's methoddelg = New ADelegate(AddressOf obj.AMethod)' Now call the method using the delegate's Invoke methoddelg.Invoke("test")End Sub

Note that the documentation describes the delegate constructor as taking two parameters, as in:

delg = New ADelegate(TargetObject, PointerToMethodOfObject)

However, Visual Basic is not capable of handling the second parameter, so VB supports the specialsyntax:

delg = New ADelegate(AddressOf obj.AMethod)

We point this out only to warn you about the documentation on the delegate class constructor.

6.1.2 Using a Delegate as a Function Pointer

The following example illustrates the use of a delegate in the context of a callback function. In thisexample, we want to create a generic sort function for sorting integer arrays. The function uses thebubble sort algorithm for sorting, but it's generic in the sense that one of its parameters is a comparefunction that is used to do the comparison of adjacent integers. Thus, by varying the externalcomparison function, we can change the type of sorting (ascending, descending, or some othermethod) without changing the main sort function. The compare function is a callback function, since itis a function we supply that is called by the main sort function. (In this case, the callback function is notsupplying us with information, as in the font enumeration case described earlier. Instead, it is called tohelp the sort function do its sorting.)First, we declare a delegate. As part of the declaration of a delegate, we must specify the signature ofthe method that is associated with the delegate, which, in our case, is the compare function. Since thecompare function should take two (adjacent) integers and return True if and only if we need to swapthe integers in the bubble sort algorithm, we declare the delegate as follows:

' Returns True if need to swapDelegate Function CompareFunc(ByVal x As Integer, _ByVal y As Integer) _As Boolean

Here are two alternative target methods for the delegate—one for an ascending sort and one for adescending sort:

Function SortAscending(ByVal x As Integer, ByVal y As Integer) As BooleanIf y < x ThenSortAscending = TrueEnd IfEnd FunctionFunction SortDescending(ByVal x As Integer, _ByVal y As Integer) As BooleanIf y > x Then

118

SortDescending = TrueEnd IfEnd Function

Now we can define the sort routine. Note the call to the Invoke method of the delegate:

Sub BubbleSort(ByVal CompareMethod As CompareFunc, _ByVal IntArray( ) As Integer)Dim i, j, temp As IntegerFor i = 0 To Ubound(IntArray)For j = i + 1 To Ubound(IntArray)If CompareMethod.Invoke(IntArray(i), IntArray(j)) ThenTemp = IntArray(j)IntArray(j) = IntArray(i)IntArray(i) = TempEnd IfNext jNext iEnd Sub

Here is some code to exercise this example:

Protected Sub Button1_Click(ByVal sender As Object, _ByVal e As System.EventArgs)Dim i As IntegerDim iArray() As Integer = New Integer( ) {6, 2, 4, 9}BubbleSort(AddressOf SortAscending, iArray)For i = 0 To 3Debug.WriteLine(CStr(iArray(i)))NextDebug.WriteLineBubbleSort(AddressOf SortDescending, iArray)For i = 0 To 3Debug.WriteLine(CStr(iArray(i)))NextEnd Sub

The output is, as you would expect:

24699642

6.2 Events and Event Binding

An event is an action that occurs. This action can take place on the part of the user of an application(such as when the user clicks a command button), on the part of application code (such as when achange is made to a record in a recordset), or on the part of the operating system (such as a timerevent). When an event occurs, we say that the event is raised, or fired.Each event has a source. This is the object to which the action is applied, such as the button that wasclicked. The source is responsible for alerting the operating system that an event has occurred. It does

119

so by sending an event notification message, generally to its parent or container window. For thisreason, Microsoft refers to the event source as the sender.An event often has an event argument, which is simply data that pertains to the event. For instance,the press of a keyboard key generates an event that includes event arguments describing the keycodeof the key pressed and information on the state of modifier keys (the Shift, Alt, and Ctrl keys). Theevent arguments are part of the message sent by the event source.An event handler is a procedure (or method) that is executed as a result of event notification. Theprocess of declaring an event handler for an event is called binding the procedure to the event.

6.2.1 Control-Related Events

Most controls have a large number of built-in events associated with them. For instance, the textboxcontrol has events associated with changing the text in the textbox, hitting a key while the textbox hasthe focus, clicking on the textbox with the mouse, dragging the mouse over the textbox, and more.The VB IDE can be used to insert an empty event handler, complete with the proper event parameters,for any built-in control. The procedure is simply to select the control, then click the Events button in theProperties window. This displays a list of built-in events for the control. Selecting one of these eventscauses the VB IDE to insert an empty event handler for that event into the code editor window.Note that each control has a default event. For instance, the default event for the command button isthe Click event. As a shortcut, we can get the VB IDE to insert an empty event handler for the defaultevent simply by double clicking the control. For instance, double clicking a command button producesthe following code:

Private Sub button1_Click(ByVal sender As System.Object, _ByVal e As System.EventArgs) _Handles button1.ClickEnd Sub

The sender variable is the source of the event. The second parameter is an object whose propertiesdescribe the event arguments.As another example, double clicking a Windows form causes the VB IDE to insert the following emptyevent handler:

Protected Sub Form1_Click(ByVal sender As Object, _ByVal e As System.EventArgs)End Sub

6.2.2 WithEvents

To define a custom event in a class module, we can use the WithEvents keyword. To illustrate witha very simple example, suppose we create the class module shown here:

Public Class Class1' Declare an eventPublic Event AnEvent(ByVal EventParam As Integer)' Method to raise the eventPublic Sub RaiseTheEvent(ByVal iEventNumber As Integer)RaiseEvent AnEvent(iEventNumber)End Sub

120

End Class

In a class module with a Windows form, we declare a variable of type Class1 using the WithEvents

keyword to hook the class' events:

Public WithEvents ev As Class1

This automatically causes the VB IDE to add the variable name ev to the left-hand drop-down listabove the code window. When we select this variable, the right-hand drop-down list displays theevents for this class. In this case, the list contains only the ev_AnEvent event. Selecting this eventplaces an empty event shell in the code editor window (to which we have added a single line of code):

Public Sub ev_AnEvent(ByVal EventParam As System.Integer) _Handles ev.AnEventMsgBox("Event raised: " & EventParam)End Sub

Finally, in a button click event, we can place code to implement our simple example:

Protected Sub Button1_Click(ByVal sender As Object, _ByVal e As System.EventArgs) _Handles Button1.Click' Define a new Class1 instanceev = New Class1( )' Raise the eventev.RaiseTheEvent(7)End Sub

We should note that the WithEvents keyword approach to event handling has one slight drawback.Namely, we cannot use the New keyword with WithEvents, as in:

Public WithEvents ev As New Class1

Thus, the object must be instantiated separately from the variable declaration, as we did in theprevious example.

6.2.3 AddHandler

The AddHandler statement can be used to bind an event handler to a built-in or custom event usingcode. This makes it possible to bind several event handlers to a single event. To illustrate, proceed asfollows. Add the default event handler for a form's Click event:

Protected Sub Form1_Click(ByVal sender As Object, _ByVal e As System.EventArgs)msgbox("Default Click Event")End Sub

Next, add another procedure with the same signature as the default event handler:

Protected Sub Form1Click(ByVal sender As Object, _ByVal e As System.EventArgs)msgbox("Custom Click Event")End Sub

Finally, to bind the function Form1Click to the Click event, we use the AddHandler statement:

121

AddHandler Form1.Click, AddressOf Me.Form1Click

This is actually shorthand for:

AddHandler Form1.Click, New EventHandler(AddressOf Me.Form1Click)

In general, the AddHandler statement has the following syntax:

AddHandler NameOfEventSender, AddressOf NameOfEventHandler

122123

Chapter 7. Error Handling in VB .NET

In this chapter, we take a concise look at error-handling techniques in VB .NET. Note that the terms

exception and error are used synonymously throughout the VB .NET documentation, and so we usethem interchangeably in this chapter.VB .NET supports the On Error Goto style of error handling, which is supported by earlier versions ofVisual Basic (with some new wrinkles). This type of error handling is referred to as unstructured errorhandling. However, unlike earlier versions of Visual Basic, VB .NET also supports the structuredexception handling technique familiar to C++ programmers, which is now the preferred method of errorhandling in VB .NET.

7.1 Error Detection and Error Handling

Let us begin by clarifying some terminology. We agree to say that handling an error means respondingto a detected error. Thus, there is a clear distinction between error detecting and error handling. Thereason for this distinction is that these processes can take place at different times and in differentlocations within the code of an application. We also agree to refer to the procedure (or module) inwhich an error occurs as the offending procedure (or module).There are two types of errors that can occur in a running program. (We will not discuss compile-time orsyntax errors.) A runtime error occurs when Visual Basic attempts to perform an operation that isimpossible to perform, such as opening a file that does not exist or dividing by 0. Visual Basicautomatically takes care of error detection for runtime errors because it has no other choice. On theother hand, proper error handling of runtime errors is up to the programmer, for otherwise Visual Basicitself handles the error by presenting an error message and terminating the application, which is not agood solution to the problem.A logical error is often defined as the production of an unexpected result. It might be better to define itas the production of an unexpected and incorrect result (although even this is still somewhatambiguous). For instance, consider a function that returns the IQ for an individual based on a set of IQtest scores. If the individual is very smart, we might expect an IQ in the range of 120 or more. A resultof 100 might be unexpected, but it is not necessarily an error. On the other hand, if the function returnsan IQ of -350, that is a logical error.Visual Basic (or, for that matter, any other language) does not provide error detection for logical errors,because to Visual Basic, no error has occurred. However, a logical error may subsequently result in aruntime error, which Visual Basic will certainly recognize. For instance, code that is intended toretrieve a positive integer for later use in an integer variable may instead retrieve 0. This is a logicalerror. But if that integer is later used as a denominator in some other part of the application, we cansurely expect a runtime error.Thus, it is up to the programmer to anticipate logical errors and provide both error detection and errorhandling. From this perspective, logical errors are far more serious and much more difficult to deal withthan runtime errors. After all, a runtime error won't be completely overlooked—at least Visual Basic willdo something about it, even if that consists only of presenting an error message to the user andterminating the application.The problem with an overlooked logical error is that it may give the user specious information (that is,invalid information that looks valid). This is no doubt the most insidious behavior a program canproduce. If we are lucky, a logical error will generate a runtime error at some later time, but we maystill have great difficulty determining the location of the logical error from the runtime error message.

124

7.2 Runtime Error Handling

As we have mentioned, VB currently supports both unstructured and structured error handling. Let usfirst look at unstructured error handling.

7.2.1 Unstructured Error Handling

Error-handling techniques that revolve around the various On Error statements are referred to as

unstructured error-handling techniques. These techniques generally use the Err object and the VisualBasic call stack.

7.2.1.1 The Err object

Visual Basic's built-in error object, called Err, is one of the main tools for unstructured error handling.This object has several properties and methods, as shown in Tables 7-1 and 7-2, respectively.

Table 7-1. Properties of the Err object

Property Description

Description A short string describing the error.HelpContext The context ID for a help topic associated with the error.HelpFile The fully qualified filename of the associated help file, if any.LastDLLErrorThe return code from a call made to a function in an external DLL. Note, however, thatthis property may change value at any time, so it is wise to store the current value in avariable immediately upon return from the DLL call. Note also that even if the DLL callresulted in an error, this is not considered an error by VB. (VB has no way of knowingthe meaning of return values from external functions, after all.)Number This is the error number of the error.SourceA string that specifies the object that generated the error. When the error is generatedwithin your application, the Source property is the project's name, which is more or lessuseless. (It would have been nice to get the name of the offending procedure.)However, when the error is generated by an external COM component, the Sourceproperty returns the programmatic ID of that component, which has the form

application.objectname, as in Excel.Application, for example.

Table 7-2. Methods of the Err object

Method Description

ClearClears the values of all properties of the Err object. Its syntax is:

Err().Clear( )

Note that the Clear method is called implicitly when any of the following statements isexecuted: a Resume statement of any type; an Exit Sub, Exit Function, or ExitProperty statement; or any On Error statement.RaiseCauses Visual Basic to generate a runtime error and sets the properties of the Err object tothe values given by the parameters of the Raise method. Its syntax is:

Err.Raise(Number, Source, Description, _HelpFile, HelpContext)

where all but the first named argument is optional. Each parameter corresponds to theproperty of the same name.

125

7.2.1.2 Dealing with runtime errors

Visual Basic detects a runtime error as soon as it occurs, sets the properties of the Err object, anddirects the flow of execution to a location that the programmer has specified by the most recent OnError line. This location can be one of the following:

· The line of code immediately following the line that caused the error.

· Another location within the offending procedure.

· The procedure that called the offending procedure, if there is one. If not, VB issues an errormessage itself and terminates the application.Let us take a closer look at each of these possibilities.

7.2.1.2.1 In-line error handling

Code execution will be "redirected" to the line following the offending line of code (that is, executionwill continue immediately following the offending line) if the most recent preceding On Error

statement is:

On Error Resume Next

This is referred to as in-line error handling. Here is an example that involves renaming a file. Note thetypical use of a Select Case statement to handle the error based on the value of Err.Number.Incidentally, one way to obtain error numbers is to deliberately invoke a particular error and breakexecution (with a breakpoint) to examine Err.Number:

Dim sOldName, sNewName As StringOn Error Resume Next' Ask for an existing file namesOldName = InputBox("Enter the file name to rename")' Ask for new namesNewName = InputBox("Enter the new file name")' Rename fileRename("c:\" & sOldName, "c:\" & sNewName)' Deal with errorIf Err( ).Number = 53 Then' File not found errorMsgBox("File " & sOldName & " not found")Exit SubElse' All other errorsMsgBox(Err().Number & ": " & Err( ).Description)Exit SubEnd If

7.2.1.2.2 Centralized error handling

While in-line error handling does have its uses, there is much to be said for centralizing error handlingwithin a procedure. (This often improves readability and makes code maintenance easier.) We candirect code execution to a central error handler using the code:

On Error Goto label

126

This is outlined in the following code shell:

Sub Example( )On Error Goto ErrHandler'' If run-time error occurs here'' Visual Basic directs execution to ErrHandlerExit SubErrHandler:'' Code can be placed here to handle errors'' or pass them up the calls list.'' We have knowledge of Err().Number, Err( ).Description,'' and Err( ).Source.End Sub

Once the On Error Goto label line is executed, we say that the error handler beginning at the label

ErrHandler is active.Once code execution is directed to the error handler, there are several possibilities for dealing with theerror. The most common possibility is simply to handle the error in the active error handler, perhaps bydisplaying an error message asking the user to take corrective action.Another common (and useful) approach is passing information about an error to the calling procedurewith parameters or with the return value of the offending function. For instance, if a function isdesigned to rename a file, the function might return an integer error code indicating the success orfailure of the operation. This is quite common among the Win32 API functions. In particular, the errorcode might be 0 for success, -1 if the file does not exist, -2 if the new filename is invalid, and so on.A third possibility is to pass the error to the calling procedure by invoking the Err.Raise method withinthe active error handler, as in:

Err.Raise(Err.Number, Err.Source, Err.Description, _Err.HelpFile, Err.HelpContext)

This triggers the calling procedure's error handler (or more precisely, the next enabled error handler inthe calls list). This process is called regenerating or reraising the error.Note that it is possible to deactivate an active error handler using the line:

On Error Goto 0

If there is no active error handler, then VB reacts to errors just as though no error handler existed inthe procedure. We describe this situation in the next section.

7.2.1.2.3 No enabled error-handler

If there is no enabled error handler in the offending procedure, either because there is no

OnErrorstatement in the procedure or because error handling has been disabled with an On ErrorGoto 0 statement, then Visual Basic automatically sends the error to the calling procedure's errorhandler. If the calling procedure has no error handler, the error continues up the calls list until itreaches an enabled error handler. If none is found, then Visual Basic handles the error by displayingan error message and terminating the application.

127

7.2.2 Structured Exception Handling

Structured exception handling uses a TryCatchFinally structure to handle errors. As we willsee, VB .NET's structured exception handling is a much more object-oriented approach, involvingobjects of the Exception class and its derived classes.

7.2.2.1 TryCatchFinally

The syntax of the TryCatchFinally construct is given here:

Try

tryStatements

[Catch1 [exception [As type]] [When expression]catchStatements1[Exit Try]Catch2 [exception [As type]] [When expression]catchStatements2

[Exit Try]. . .Catchn [exception [As type]] [When expression]catchStatementsn][Exit Try][Finally

finallyStatements]End Try

The tryStatements (which are required) constitute the Try block and are the statements that aremonitored for errors by VB. Within the Try block, we say that error handling is active.The Catch blocks (of which there can be more than one) contain code that is executed in response toVB "catching" a particular type of error within the Try block. Thus, the Catch blocks consist of theerror handlers for the Try block.The phrases exception [As type] and [When expression] are referred to as filters in theVB .NET documentation. In the former case, exception is either a variable of type Exception, whichis the base class that "catches" all exceptions, or a variable of one of Exception's derived classes. (Weprovide a list of these classes a bit later.) For instance, the variable declared as:

Catch e As Exception

will catch (that is, handle) any exception. The variable declared as:

Catch e As ArgumentNullException

catches (handles) any exception of class ArgumentNullException. In short, type is the name of one ofthe exception classes.The When filter is typically used with user-defined errors. For instance, the code in the following Try

block raises an error if the user does not enter a number. The Catch block catches this error:

TryDim sInput As StringsInput = Inputbox("Enter a number.")

128

If Not IsNumeric(sInput) ThenErr.Raise(1)End IfCatch When Err.Number = 1Msgbox("Error1")End Try

Note that code such as:

Dim x As IntegerTryx = 5Catch When x = 5MsgBox(x)End Try

does not work (that is, the Catch statements are never executed) because no error was generated.The Exit Try statement is used to break out of any portion of a TryCatchFinally block. Theoptional finallyStatements code block is executed regardless of whether an error occurs (or iscaught), unless an Exit Try statement is executed. This final code can be used for cleanup in theevent of an error. (By placing an Exit Try at the end of the Try block, the finallyStatements arenot executed if no error occurs.)As with unstructured error handling, VB may pass an error up the call stack when using structurederror handling. This happens in the following situations:

· If an error occurs within a Try block that is not handled by an existing Catch block

· If an error occurs outside any Try block (provided, of course, that no On Error-style errorhandlers are active).

7.2.2.2 Exception classes

The System namespace contains the Exception class, which is the base class for a substantialcollection of derived exception classes, listed as follows. Note that the indentation indicates classinheritance. For example, EntryPointNotFoundException (the fifth from the last entry in the list) inheritsfrom TypeLoadException.

ExceptionApplicationExceptionSystemExceptionAccessExceptionFieldAccessExceptionMethodAccessExceptionMissingMemberExceptionMissingFieldExceptionMissingMethodExceptionAppDomainUnloadedExceptionAppDomainUnloadInProgressExceptionArgumentExceptionArgumentNullExceptionArgumentOutOfRangeExceptionDuplicateWaitObjectExceptionArithmeticExceptionDivideByZeroExceptionNotFiniteNumberExceptionOverflowException

129

ArrayTypeMismatchExceptionBadImageFormatExceptionCannotUnloadAppDomainExceptionContextMarshalExceptionCoreExceptionExecutionEngineExceptionIndexOutOfRangeExceptionStackOverflowExceptionExecutionEngineExceptionFormatExceptionInvalidCastExceptionInvalidOperationExceptionMulticastNotSupportedExceptionNotImplementedExceptionNotSupportedExceptionPlatformNotSupportedExceptionNullReferenceExceptionOutOfMemoryExceptionRankExceptionServicedComponentExceptionTypeInitializationExceptionTypeLoadExceptionEntryPointNotFoundExceptionTypeUnloadedExceptionUnauthorizedAccessExceptionWeakReferenceExceptionURIFormatException

As Microsoft states: "Most of the exception classes that inherit from Exception do not implementadditional members or provide additional functionality." Thus, it is simply the class name thatdistinguishes one type of exception from another. The properties and methods applied to an exceptionobject are inherited from the Exception base class.When writing Catch blocks, we always face the question of whether to simply trap the genericexception class, as in:

Sub test( )Try Catch e As Exception End TryEnd Sub

or whether to trap specific exception classes. Of course, the time to trap specific exception classes iswhen we want to handle errors differently based on their class. For instance, this may take the form ofissuing different custom error messages for different exception types.Also, there are occasions when we may want to take advantage of members of a particular exceptionclass that are not implemented in the Exception base class. For instance, the ArgumentExceptionclass has a ParamName property that returns the name of the parameter that causes the exception.Now, if we simply trap the generic Exception class, as in the following code:

Sub test( )TryDim s, d As Strings = "c:\temp.txt"' Attempt to copy a file to a nonvalid target

130

FileCopy(s, d)Catch e As ExceptionMsgBox(e.Message)End TryEnd Sub

then we cannot take advantage of the ParamName property. On the other hand, if we specifically trapthe ArgumentException class, as in the following code:

Sub test1( )TryDim s, d As Strings = "c:\temp.txt"' Attempt to copy a file to a nonvalid targetFileCopy(s, d)Catch e As ArgumentExceptionMsgBox(e.Message & " Parameter: " & e.ParamName)End TryEnd Sub

then we can retrieve the name of the offending parameter.Now let us take a look at some of the members of the Exception class:

Message property

A string containing an error message.

Source property

A string that describes the application or object that threw the exception.

StackTrace property

A string that contains the stack trace immediately before the exception was thrown. Weprovide an example of this in a moment.

TargetSite property

A string that gives the method that threw the exception.

ToString method

A string that returns the fully qualified name of the exception, possibly the error message, thename of the inner exception, and the stack trace. Its syntax is simply:

ToString( )

The best way to get a feel for these members is with an example. Consider the following code, whichconsists of three subroutines. The first subroutine, Exception0, contains a TryCatch statement. Inthe Try code block, the subroutine Exception0 calls the subroutine Exception1, which simply calls

Exception2.

Sub Exception0( )Dim s As StringTryException1( )

131

Catch e As Exceptions = "Message: " & e.Messages = s & ControlChars.CrLf & "Source: " & e.Sources = s & ControlChars.CrLf & "Stack: " & e.StackTraces = s & ControlChars.CrLf & "Target: " & e.TargetSite.Names = s & ControlChars.CrLf & "ToString: " & e.ToStringdebug.writeline(s)End TryEnd SubSub Exception1( )Exception2( )End SubSub Exception2( )Throw New ArgumentNullException( )End Sub

In Exception2, there is a single line of code that executes the Throw statement, which throws anexception. This is similar to raising an error with the Err.Raise method. However, as you can see bythe New keyword, the Throw statement actually creates an object of one of the exception types.The output from the call to Exception0 is:

Message: argument can't be nullSource:Stack: at WindowsApplication3.Form1.Exception2( )in C:\VBNET\Form1.vb:line 68at WindowsApplication3.Form1.Exception1( )in C:\VBNET\Form1.vb:line 66at WindowsApplication3.Form1.Exception0( )in C:\VBNET\Form1.vb:line 53Target: Exception2ToString: System.ArgumentNullException: argument can't be nullat WindowsApplication3.Form1.Exception2( )in C:\VBNET\Form1.vb:line 68at WindowsApplication3.Form1.Exception1( )in C:\VBNET\Form1.vb:line 66at WindowsApplication3.Form1.Exception0( )inC:\VBNET\Form1.vb:line 53

7.3 Dealing with Logical Errors

Since Visual Basic makes the handling of runtime errors a relatively straightforward process, it seemsreasonable to try to mimic this process for logical errors.

7.3.1 Detecting Logical Errors

To detect a logical error, we place error-detection code immediately following the potential offender.For instance, consider the following procedure shell for getting a sequence of positive integers fromthe user, starting with the number of integers:

Public Sub GetSomeData( )Dim DataCt As IntegerDataCt = CInt(InputBox("Enter number of items."))

132

' Code here to get the individual data values End Sub

The proper place for error-detecting code is immediately following the InputBox function, where wecan check for a nonpositive integer:

Public Sub GetSomeData( )Dim DataCt As IntegerDataCt = CInt(InputBox("Enter number of items."))' Check for errorIf DataCt < = 0 then' something hereEnd If' Code here to get the individual data values End Sub

Note that the alternative to immediate detection of logical errors is to place the error-detecting codejust prior to using the value of DataCt, but this is both dangerous and inefficient. It is dangerous sincewe might forget to place the code, and it is inefficient since we may use DataCt in a variety oflocations in the program, each of which would require error-detecting code.

7.3.2 Where to Handle a Logical Error

Once a logical error is detected, we have three choices as to where to handle that error.

7.3.2.1 Handling the error on the spot

A logical error can be handled at the location where it was detected. Here is an example:

Public Sub GetSomeData( )TryAgain:DataCt = CInt(InputBox("Enter number of items."))' Check for errorIf DataCt < = 0 thenIf MsgBox("Number must be a positive integer." & _" Try again or cancel.", vbQuestion+vbOKCancel) _= vbOK thenGoto TryAgainElseExit SubEnd IfEnd If'' Code here to get the individual data values End Sub

Handling a logical error on the spot may be appropriate when the required code is short. It is alsoappropriate in Property procedures, which often amount to little more than a single line that sets aprivate instance variable, preceded by data validation, which is essentially logical-error detection.

7.3.2.2 Handling the error in the offending procedure's error handler

We can duplicate the procedure that Visual Basic uses for runtime errors simply by raising our ownruntime error. Here is an example using structured exception handling:

TryDim DataCt As Integer = CInt(InputBox("Enter number of items."))' Check for error

133

If DataCt <= 0 Then' Throw an exceptionThrow New Exception("Must enter a positive number.")End IfCatch ex As ExceptionMsgBox(ex.Message)End Try

Note that the Exception class constructor (in one of its overloaded forms) is:

Overloads Public Sub New(String)

where String is the error message to be associated with the error.Here is an example of error raising using unstructured error handling:

Public Sub GetSomeData( )On Error Goto ErrGetSomeDataDataCt = CInt(InputBox("Enter number of items."))' Check for errorIf DataCt < = 0 then' Raise an errorErr( ).Raise Number:= ErrBadDataCtEnd If' Code here to get the individual data values Exit Sub' Error-handlerErrGetSomeData:Select Case Err( ).NumberCase ErrBadDataCt'' Deal with this error by displaying'' message and getting help from userCase Else'' Deal with other errorsEnd SelectExit SubEnd Sub

7.3.2.3 Passing the error to the calling procedure

As with runtime errors, passing the error to the calling procedure can be done in a parameter of theoffending procedure or as the return value of the offending function. Also, the calling procedure's errorhandler can be called by throwing (or raising) an error.

7.4 Error Constants

To raise our own errors using the Err.Raise method, we need error numbers that do not conflict withthose used by Visual Basic. The Visual Basic documentation says that error numbers in the range

vbObjectError to vbObjectError + 65535, where vbObjectError is a built-in constant whosevalue is the signed integer -2147220991 (or &H80040000 as an unsigned hexadecimal integer), aredesigned to signal an error generated by an object.

134

It further says that error numbers below vbObjectError + 512 may conflict with values reserved forOLE, so these numbers are verboten. Thus, we are left with numbers in the range vbObjectError +512 to vbObjectError + 65535, which should be plenty.Many programmers like to assign symbolic constants to error numbers, since it tends to improvereadability and cut down on the need for comments. For instance, we could add the line:

Public Const ErrBadDataCt = vbObjectError + 1024

in a standard module.

135

Part II: Reference

This section consists only of one very long chapter (Chapter 8), which contains an alphabeticreference to VB .NET language elements.The chapter documents the following:

· Statements, such as AddHandler or StructureEnd Structure.

· Procedures, such as AppActivate or Rename. These were statements in previousversions of Visual Basic, but now they are methods of one class or another within theMicrosoft.VisualBasic namespace. The official documentation describes them asfunctions, but since they don't return a value, we've chosen to describe them asprocedures.

· Functions, such as Format or IsReference.

· Compiler directives, such as #Const or #If.

· Visual Basic classes and their members. The two intrinsic objects available in VisualBasic are the Collection object and the Err object.

· Selected classes in the .NET Framework Class Library, along with their members.Documentation of the Framework Class Library, however, is highly selective; we'vechosen classes and their members either because they replace language elementsthat were present in VB 6, or because they provide much needed functionality thatsupplements existing language elements.When you're looking for a particular language element but don't quite remember what it's called, analphabetic reference is of little value. For this reason, we've included Appendix B.Finally, two language elements are covered in the appendixes rather than in Part II. With a fewexceptions (notably, Like and Is) that are documented in Part II, Visual Basic operators arecovered in Appendix C. And Visual Basic constants and enumerations are listed in Appendix D.

Chapter 8. The Language Reference

This long chapter documents VB .NET language elements. To help you speed the process of findingthe right element to perform a particular task, you can use Appendix B to determine what languageelements are available for the purpose you require. If you're using Visual Studio .NET, you can alsomake use of its Object Browser to browse the Microsoft.VisualBasic namespace.In documenting the VB .NET language, we've tried to provide a consistent and uniform treatment ofparticular types of language elements. These language elements are:

Functions

The entry for each function provides the standard information that you'd expect for a function:its syntax, parameters (if it has any), return value, and description. In addition, we list rules forusing the function (see Rules at a Glance), discuss tips and tricks related to the function(see Programming Tips and Gotchas), frequently provide examples, and list relatedlanguage elements.In addition, each VB .NET function is in fact a method, since it is a member of a particularclass in the Microsoft.VisualBasic namespace. In each case, we've listed the class to whichthe function belongs.For the first time, Visual Basic supports both named and positional arguments for all functions,procedures, and methods, with just a few exceptions. Functions, procedures, or methods thataccept parameter arrays as arguments don't accept named arguments if the ParamArray

136

parameter is present. And "functions" that are actually resolved by the compiler at compiletime (the conversion functions fall into this category) do not accept named arguments. To seehow named arguments work, let's look at the syntax of the Mid function:

Mid(Str As String, Start As Integer, Length As Integer)

Using positional arguments, you might call the function as follows:

iPos = Mid(strName, 12, 10)

The same function call using named arguments might appear as follows:

iPos = Mid(start:=12, str:=strName, length:=10)

Since named arguments are nearly universally accepted, we only note when you can't usenamed arguments with a particular function. The name of each argument is provided in thefunction's syntax statement.Finally, we've noted any differences between the operation of the function under previousversions of Visual Basic and under VB .NET.

Procedures

Procedures are really functions that don't return a value to the caller. Consequently, except forthe absence of a return value, the same information is presented for procedures as forfunctions.Procedures are interesting as a separate language category. Under previous versions ofVisual Basic, they were statements. With the rationalization and streamlining of Visual Basicfor its .NET version, they were moved into classes in the Microsoft.VisualBasic namespaceand became procedures. The official documentation describes them as functions, althoughthey do not return a value.

Statements

Visual Basic statements are not class members, don't support named arguments, and don'treturn a value. Aside from these three items, the same information is presented for statementsas for procedures and functions.

Directives

Visual Basic directives are really statements that provide instructions to the VB .NET compileror to a .NET development environment like Visual Studio. Like statements, they are not classmembers, don't support named arguments, and don't return a value. In general, the sameinformation is presented for directives as for statements.

Classes and Objects

Entries for classes and objects identify the namespace to which the class belongs (somethingthat is particularly important in the case of the Framework Class Library) and indicate whetherthe class is creatable. If a class is createable, a new instance of that class can be created byusing the New keyword, as in:

Dim colStates As New Collection

137

In some cases, the entry for the class or object also includes a summary listing of the class'members, along with their syntax and a brief description.

Class Members (Properties, Methods, and Events)

When the members of a class seem to be particularly interesting or important, we've devotedseparate entries to each. These contain the same items of information as functions.

#Const DirectiveSyntax

#Const constantname = expressionconstantname

Use: RequiredData Type: String literalName of the constant

expression

Use: RequiredData Type: LiteralAny combination of literal values, other conditional compilation constants defined with the

#Const directive, and arithmetic or logical operators except Is

Description

Defines a conditional compiler constant.By using compiler constants to create code blocks that are included in the compiled application onlywhen a particular condition is met, you can create more than one version of the application using thesame source code. This is a two-step process:

· Defining the conditional compiler constant. This step is optional; conditional compilerconstants that are not explicitly defined by the #Const directive, but are referenced in code,default to a value of Nothing.

· Evaluating the constant in the conditional compiler #IfThen statement block.A conditional compiler constant can be assigned any string, numeric, or logical value returned by anexpression. However, the expression itself can only consist of literals, operators other than Is, andanother conditional compiler constant.When the constant is evaluated, the code within the conditional compiler #IfThen block iscompiled as part of the application only when the expression using the conditional compiler constantevaluates to True.

138

Rules at a Glance

· Conditional compiler constants are evaluated by the conditional compiler #IfThen

statement block.

· You can use any arithmetic or logical operator in the expression except Is.

· You cannot use other constants defined with the standard Const statement in the expression.

· You cannot use intrinsic functions or variables in expression.

· Constants defined with #Const can only be used in conditional code blocks.

· You can place the #Const directive anywhere within a source file. If placed outside of allmodules, the defined constant is visible throughout the source file, but is not visible to anyother source files in the project. If placed in a module, the scope of the constant is that module.If placed in a procedure, the scope is that procedure and all called procedures.

· The #Const directive must be the first statement on a line of code. It can be followed only bya comment. Note that the colon, which is used to combine two complete sets of statementsonto a single line, cannot be used on lines that contain #Const.

Programming Tips and Gotchas

· Conditional compiler constants help you debug your code, as well as provide a way to createmore than one version of your application. You can include code that only operates when runin debug mode. The code can be left in your final version and does not compile unless runningin the debugger. Therefore, you don't need to keep adding and removing debugging code.

· Conditional compiler constants may be defined in terms of other conditional compilerconstants. For example, the following code fragment works as expected:

· #Const Flag1 = 1

· #Const Flag2 = 1#Const Flags = Flag1 + Flag2

· A conditional compiler constant can be defined at the command line using the /define or /d

switch.

· It is important to remember that the constant defined by #Const is evaluated at compile timeand therefore does not return information about the system on which the application is running.For example, the intent of the following code fragment is to test for a sound card and, if one ispresent, to include code to take advantage of the system's enhanced sound capabilities:

· If waveOutGetNumDevs > 0 Then

· #Const ccSoundEnabled = True

· Endif

·

· #If ccSoundEnabled Then

· ' Include code for sound-enabled systems

· #Else

· ' Include code for systems without a sound card#End If

· However, the code does not work as expected, since it includes or excludes the codesupporting a sound card based on the state of the machine on which the program is compiled,rather than the machine on which the application is run.

See Also

#IfThen#Else Directive

139

#IfThen#Else DirectiveSyntax

#If expression Thenstatements[#ElseIf furtherexpression Then[elseifstatements]][#Else[elsestatements]]#End If

expression

Use: RequiredAn expression made up of literals, operators, and conditional compiler constants that willevaluate to True or False

statements

Use: RequiredOne or more lines of code or compiler directives, which is executed if expressionevaluatesto True

furtherexpression

Use: OptionalAn expression made up of literals, operators, and conditional compiler constants that willevaluate to True or False. furtherexpression is only evaluated if the precedingexpression evaluates to False

elseifstatements

Use: OptionalOne or more lines of code or compiler directives, which is executed if furtherexpression

evaluates to True

elsestatements

Use: OptionalOne or more lines of code or compiler directives, which are executed if expression or

furtherexpression evaluates to False

Description

Defines a block or blocks of code that are only included in the compiled application when a particularcondition is met, allowing you to create more than one version of the application using the samesource code.

140

Conditionally including a block of code is a two-step process:

· Use the #Const directive to assign a value to a conditional compiler constant.

· Evaluate the conditional compiler constant using the #IfThen#End If statementblock.Only code blocks whose expressions evaluate to True are included in the executable. You can usethe #Else statement to execute code when the #IfThen expression evaluates to False. Youcan also use an #ElseIf statement to evaluate more expressions if previous expressions in thesame block have evaluated to False.Some uses of conditional compilation code are:

· To provide blocks of debugging code that can be left within the source code and switched onand off using a conditional constant. Since debug statements such as Debug.Write have noeffect in compiled executables, they do not need to be included in conditional compilationcode for the purpose of removing them from the final executable.

· To provide blocks of code that can perform different functions based on the build required bythe developer. For example, you may have a sample version of your application that offersless functionality than the full product. This can be achieved using the same source code andwrapping the code for menu options, etc., within conditional compiler directives.

· To provide blocks of code that reference different components depending upon the buildcriteria of the application.

Rules at a Glance

· Unlike the normal IfThen statement, you cannot use a single-line version of the

#IfThen statement.

· All expressions are evaluated using Option Compare Text, regardless of the setting of

Option Compare.

· If a conditional compiler constant is undefined, comparing it to Nothing, 0, False, or anempty string ("") returns True.

Example

#Const ccVersion = 2.5Private oTest as ObjectSub GetCorrectObject( )#If ccVersion = 2.5 ThenSet oTest = New MyObject.MyClass#ElseSet oTest = New MyOtherObject.MyClass#End IfEnd Sub

Programming Tips and Gotchas

· You can negate the evaluation of the expression in the #IfThen or #ElseIfThen

statements by placing the Not operator before the expression. For example, #If NotccVersion = 5 Then forces the code after this line to compile in all situations where

ccVersion does not equal 5.

· Conditional compilation helps you debug your code, as well as provides a way to create morethan one version of your application. You can include code that will only operate when run in

141

debug mode. The code can be left in your final version and will not compile unless running inthe debugger; therefore, you don't need to keep adding and removing code.

See Also

#Const Directive

#Region#End Region DirectiveSyntax

#Region "identifier_string"' code goes here#End Region

identifier_string

Use: RequiredData Type: String literalThe title of the code block (or region)

Description

Marks a block of code as an expandable and collapsible region or code block in the VisualStudio .NET editor

Rules at a Glance

· Code blocks delineated with the #Region#End Region directive are collapsed by default.

· identifier_string serves as the title to identify the region when it is collapsed.

· Code blocks defined by other directives (such as #If) must be entirely contained within the

#Region#End Region block.

Abs FunctionClass

System.Math

Syntax

Math.Abs(value)

value

Use: Required

142

Any valid numeric expressionA number whose absolute value is to be returned

Return Value

The absolute value of value. The data type is the same as that of the argument passed to thefunction.

Description

Returns the absolute value of value. If value is an uninitialized variable, the return value is 0

Rules at a Glance

· Only numeric values can be passed to the Abs function.

· This is a Shared member of the Math class, so it can be used without creating any objects.

Example

In this example, the LineLength function is used to determine the length of a line on the screen. If theline runs from left to right, X1 is less than X2, and the expression X2 - X1 returns the length of the line.If, however, the line runs from right to left, X1 is greater than X2, and a negative line length is returned.As you know, in most circumstances it does not matter which way a line is pointing; all you want toknow is how long it is. Using the Abs function allows you to return the same figure whether theunderlying figure is negative or positive:

Function LineLength(X2 as Integer) as IntegerDim X1 As IntegerX1 = 100LineLength = Math.Abs(X2 - X1)End Function

Programming Tips and Gotchas

Because the Abs function can only accept numeric values, you may want to check the value you passto Abs using the IsNumeric function to avoid generating an error. This is illustrated in the followingcode snippet:

If IsNumeric(sExtent) ThenMath.Abs(sExtent) End If

VB .NET/VB 6 Differences

In VB 6, Abs is an intrinsic VB function. In the .NET platform, it is a member of the Math class in the

System namespace, and so it is not part of the VB .NET language.

See Also

143

Sign Function

Acos FunctionClass

System.Math

Syntax

Math.Acos(d)

d

Use: RequiredData Type: Double or any valid numeric expressionA cosine, which is a number greater than or equal to -1 and less than or equal to 1

Return Value

A Double between 0 and pi that is the arccosine of d in radians

Description

Returns the arccosine of d in radians

Rules at a Glance

· If d is out of range (less than -1 or greater than 1), Acos returns NaN.

· This is a Shared member, so it can be used without creating any objects.

Programming Tips and Gotchas

To convert from radians to degrees, multiply by 180/pi.

VB .NET/VB 6 Differences

The Acos function did not exist in VB 6.

See Also

Asin Function, Atan Function, Atan2 Function

AddHandler Statement

144

Syntax

AddHandler NameOfEventSender, AddressOf NameOfEventHandlerNameOfEventSender

Use: RequiredType: String literalThe name of a class or object instance and its event, such as Button1.Click

NameOfEventHandler

Use: RequiredType: String literalThe name of a subroutine that is to serve as the event handler for NameOfEventSender

Description

Binds an event handler to a built-in or custom event. This makes it possible to bind several eventhandlers to a single event.

· EventNameOfSender takes the form class.event or object.event.

· You can stop handling events defined by the AddHandler statement by calling the

RemoveHandler statement.

Example

For an illustration, see Section 6.2.3 in Chapter 6.

Programming Tips and Gotchas

The WithEvents keyword can be used to receive event notification for the lifetime of an object. Incontrast, AddHandler and RemoveHandler can be used to dynamically add and remove eventnotification at runtime.

AddressOf OperatorSyntax

AddressOf procedurenameprocedurename

Use: RequiredThe name of a procedure that is referenced by the procedure delegate

Description

145

The AddressOf operator returns a procedure delegate instance that references a specific procedure.The AddressOf operator is used in the following situations:

· If a parameter to a procedure (a VB procedure or a Win32 API function) requires a functionpointer (the address of a function), then we can pass the expression:

AddressOf functionname

· where functionname is the name of the function. This function is called a callback function.

· AddressOf is also used to create delegate objects, as in:

delg = New ADelegate(AddressOf obj.AMethod)

· AddressOf is used to bind event handlers to events through the AddHandler statement:

AddHandler Form1.Click, AddressOf Me.Form1Click

Examples of all three applications of AddressOf can be found in Section 6.1 in Chapter 6.

VB .NET/VB 6 Differences

In VB 6, the AddressOf operator can only be used in a call to a Windows API function. Moreover, theargument passed to AddressOf must be the name of a procedure in a standard code module.However, in VB .NET these restrictions no longer apply.

AppActivate ProcedureClass

Microsoft.VisualBasic.Interaction

Syntax

[Interaction.]AppActivate(title)

title

Use: RequiredData Type: String or IntegerThe name of the application as currently shown in the application window title bar. This canalso be the task ID returned from the Shell function.

Description

Activates a window based on its caption

Rules at a Glance

146

· AppActivate performs a case-insensitive search on all top-level windows for a windowcaption that matches title. If an exact match is found, the window is activated. If no matchis found, then the window captions are searched for a prefix match (title matches thebeginning of the window caption). For example, the title "Microsoft Word" matches

"Microsoft Word - MyDocument.doc". If a prefix match is found, the window is activated.Note that if multiple prefix matches are found, there is no way to predict which matchingwindow will be activated.

· The window state (Maximized, Minimized, or Normal) of the activated application is notaffected by AppActivate.

· If a matching application cannot be found, an exception of type System.ArgumentException israised, and runtime error 5, "Invalid procedure call or argument," is generated.

Example

Private Sub Button2_Click(ByVal sender As System.Object, _ByVal e As System.EventArgs) _Handles Button2.ClickDim bVoid As BooleanbVoid = ActivateAnApp("Microsoft Excel")End SubFunction ActivateAnApp(vAppTitle As String) As BooleanOn Error GoTo Activate_ErrActivateAnApp = FalseAppActivate(vAppTitle)ActivateAnApp = TrueExit FunctionActivate_Err:MsgBox ("Application " & vAppTitle & _" could not be activated")End Function

Programming Tips and Gotchas

· AppActivate searches only top-level windows.

· You can also use the task ID returned by the Shell function with the AppActivate statement,as this simple example demonstrates:

· Option Explicit

· Private vAppID

·

· Private Sub Button1_Click(ByVal sender As System.Object, _

· ByVal e As System.EventArgs) _

· Handles Button1.Click

· vAppID = Shell("C:\Program Files\InternetExplorer\IEXPLORE.EXE")

· End Sub

· Private Sub Button2_Click( ByVal sender As System.Object, _

· ByVal e As System.EventArgs) _

· Handles Button2.Click

· AppActivate vAppIDEnd Sub

147

· AppActivate is very difficult to use with applications whose application titles change toreflect the state or context of the application. Microsoft Outlook illustrates an excellentexample of this problem. If the user has Outlook in the Calendar section, the title bar reads"Calendar - Microsoft Outlook," whereas if in the Inbox section, the title bar reads "Inbox -Microsoft Outlook." In situations such as this, we must resort to other techniques, such asusing Win32 API methods, to enumerate all windows and check the captions directly.

· AppActivate is often used to give the focus to a particular window before keystrokes aresent to it using the SendKeys statement, which sends keystrokes to the active window only.

VB .NET/VB 6 Differences

In VB 6, AppActivate has a second optional parameter, wait, a Boolean that determines whetherthe application calling AppActivate must have the focus for the window indicated by title to beactivated. In VB .NET, wait is not supported.

See Also

Shell Function

Application ClassNamespace

System.Windows.Forms

Createable

No

Description

The Application object provides a diverse range of functionality, including support for multithreadedprogramming, access to the system registry, and support for subclassing (intercepting messages sentto application windows). It also includes a variety of informational functions, such as properties toretrieve the company name, to retrieve the application's executable path, and to retrieve theapplication's name and version.Application objects can be created as follows:

Dim obj As Application

However, because all of the Application object's members are shared, you do not need to instantiatethe Application object to access its properties and methods. Hence, you can retrieve the executablepath of your application, for instance, with the code fragment:

Dim sPath As String = Application.ExecutablePath

Application class members marked with a plus sign (+) are discussed in detail in their own entries.

Public Shared Properties

148

AllowQuitCommonAppDataPathCommonAppDataRegistryCompanyName +CurrentCultureCurrentInputLanguageExecutablePath +LocalUserAppDataPathMessageLoopProductName +ProductVersion +SafeTopLevelCaptionFormatStartupPathUserAppDataPathUserAppDataRegistry

Public Shared Methods

AddMessageFilterDoEvents +ExitExitThreadOleRequiredOnThreadExceptionRemoveMessageFilterRun

Public Shared Events

ApplicationExitIdleThreadExceptionThreadExit

See Also

Application.CompanyName Property, Application.DoEvents Method,

Application.ExecutablePath Property, Application.ProductName Property,

Application.ProductVersion Property

Application.CompanyName PropertyClass

System.Windows.Forms.Application

Syntax

Application.CompanyName( )

Return Value

A String containing the company name for the application

149

Description

Gets the company name for the application. This is a read-only property.The value of the CompanyName property can be defined by including the <AssemblyCompany>

attribute in the AssemblyInfo file for the application. Its syntax is:

<Assembly: AssemblyCompany("sCompany")>

where sCompany is a string literal containing the company name.

See Also

Application Class, Application.ProductName Property, Application.ProductVersion Property

Application.DoEvents MethodClass

System.Windows.Forms.Application

Syntax

Application.DoEvents( )

Description

Allows the operating system to process events and messages waiting in the message queue.For example, you can allow a user to click a Cancel button while a processor-intensive operation isexecuting. In this case, without DoEvents, the click event is not processed until after the operation hadcompleted. With DoEvents, Windows allocates time for the Cancel button's Click event to fire and theevent handler to execute.

Example

The following example uses a form with two command buttons to illustrate DoEvents. Suppose theuser clicks CommandButton1. Then the Do loop in the click event executes indefinitely. However, if theuser clicks CommandButton2, its click event is processed when the DoEvents statement inCommandButton1_Click is executed. This sets the Boolean flag to False, which terminates the Do

loop.

Option ExplicitPrivate lngCtr As LongPrivate blnFlag As BooleanPrivate Sub Button1_Click( ByVal sender As System.Object, _ByVal e As System.EventArgs) _Handles Button1.Click

150

blnFlag = TrueDo While blnFlaglngCtr = lngCtr + 1DoEvents( )LoopMsgBox("Loop interrupted after " & lngCtr & _" iterations.")End SubPrivate Sub CommandButton2_Click( )blnFlag = FalseEnd Sub

Programming Tips and Gotchas

· While DoEvents can be indispensable for increasing the responsiveness of your application, itshould at the same time be used judiciously, since it entails an enormous performance penalty.For example, the following table compares the number of seconds required for a simple

ForNext loop to iterate one million times when DoEvents isn't called, on the one hand,and when it's called on each iteration of the loop, on the other.Without DoEvents 0.01 secondsWith DoEvents 49.26 seconds

· If most of a procedure's processing occurs inside of a loop, one way to avoid too many calls toDoEvents is to call it conditionally every ten, hundred, or thousand iterations of the loop. Forexample, the following code calls DoEvents every thousand iterations:

· Dim lCtr As Long

· For lCtr = 0 To 1000000

· If (lCtr Mod 1000) = 0 Then

· DoEvents

· End IfNext

· DoEvents should not be used in any event procedure or callback routine that is invokedautomatically by the operating system. Doing so causes re-entrance problems. (The event orroutine may be called again during the processing of the DoEvents method.) For the samereason, DoEvents should not be used in in-process COM objects created with Visual Basic.

See Also

Application Class

Application.ExecutablePath PropertyClass

System.Windows.Forms.Application

151

Syntax

Application.ExecutablePath( )

Return Value

A String containing the complete path of the executable file for the application

Description

Gets the complete path of the executable file for the application. This is a read-only property.

VB .NET/VB 6 Differences

The ExecutablePath property in the .NET Framework corresponds to the App.Path property in VB 6.

See Also

Application Class

Application.ProductName PropertyClass

System.Windows.Forms.Application

Syntax

Application.ProductName( )

Return Value

A String containing the product name of the application

Description

Gets the product name of the application. This is a read-only property.The value of the ProductName property can be defined by including the <AssemblyProduct>

attribute in the application's AssemblyInfo file. Its syntax is:

<Assembly: AssemblyProduct("sProduct")>

where sProduct is a string literal containing the product name.

VB .NET/VB 6 Differences

The ProductName property in the .NET Framework corresponds to the App.ProductName property inVB 6.

152

See Also

Application Class, Application.CompanyName Property, Application.ProductVersionProperty

Application.ProductVersion PropertyClass

System.Windows.Forms.Application

Syntax

Application.ProductVersion( )

Return Value

A String containing the product version of the application

Description

Gets the product version of the application. This is a read-only property. The product version typicallyhas the form:

MajorVersionNumber.MinorVersionNumber.BuildNumber.PrivatePartNumber

Its default value is "1.0.*", which indicates that Visual Studio maintains default build and revisionnumbers.The value of the ProductVersion property can be defined by including the <AssemblyVersion>

attribute in the application's AssemblyInfo file. Its syntax is:

<Assembly: AssemblyVersion("maj.min.bld.rev")>

where maj is the major version number, min is the minor version number, bld is the build number,and rev is the revision number.

VB .NET/VB 6 Differences

The ProductVersion property in the .NET Framework corresponds to the App.Major, App.Minor, andApp.Revision properties in VB 6.

See Also

Application Class, Application.CompanyName Property, Application.ProductName Property

Array Class

153

Namespace

System

Createable

Yes

Description

An Array object (that is, an instance of the Array class) that represents an array.Arrays defined in VB .NET are Array objects, so they support the members of the Array class. Arrayclass members marked with a plus sign (+) are discussed in detail in their own entries.

Public Instance Properties

IsFixedSizeIsReadOnlyIsSynchronizedLengthRankSyncRoot

Public Shared Methods

BinarySearch +ClearCopy +CreateInstanceIndexOf +LastIndexOf +Reverse +Sort +

Public Instance Methods

CloneCopyToEqualsGetEnumeratorGetHashCodeGetLengthGetLowerBoundGetTypeGetUpperBoundGetValueInitializeSetValueToString

Array.BinarySearch Method

154

Class

System.Array

Syntax

Array.BinarySearch(array, value, [comparer])

Array.BinarySearch(array, index, length, value, [comparer])

array

Use: RequiredData Type: Any arrayThe one-dimensional array to be searched

value

Use: Required in first overloaded functionData Type: AnyThe value to search for in arrayindex

Use: Required in second overloaded versionData Type: IntegerThe array element at which the search is to start

length

Use: Required in second overloaded versionData Type: IntegerThe number of array elements to be searched

comparer

Use: OptionalData Type: IComparer

A BCL or user-defined class implementing the IComparer interface that determines how twoitems are compared for equality.

Return Value

An Integer representing the zero-based ordinal position of the element matching value

155

Description

This method provides a quick way to search for a value in a sorted one-dimensional array, returningthe smallest index whose element is that value. It uses a binary search algorithm, which tends to takelog2(n) comparisons to find an item in an array of length n. For example, if n = 100,000, the number ofcomparisons is on the order of 17.To illustrate, if arr is an array of names in alphabetical order, then the code:

Array.BinarySearch(arr, "steve")

returns the smallest index with element "steve." If no such element exists, BinarySearch returns thenegative number whose bitwise complement is the index of the first element that is larger than "steve."

Rules at a Glance

· The array must be a one-dimensional array sorted in ascending order.

· If value is not found in the array, the method returns a negative number, which is the bitwisecomplement of the index of the first element that is larger than value. To extract this value,you can use the Not operator, as in the following code fragment:

· iResult = Array.BinarySearch(lArr, lSearch)

· if iResult >= 0 Then

· MsgBox(iResult)

· Else

· MsgBox(iResult & vbcrlf & Not iResult)End If

· By default, the System.Collections.Comparer class is used to compare value with themembers of array. This means that string comparisons are case sensitive.

Programming Tips and Gotchas

· If an array contains Boolean values, the method fails to correctly identify the position of thefirst False value in the array.

· In addition to the Comparer class, you can also pass an instance of theSystem.Collections.CaseInsensitiveComparer class as the comparer argument. It providesfor case-insensitive comparisons. For example:

· Dim sArr( ) As String = {"Alaska", "ALASKA", "Michigan","MICHIGAN", _

· "New York", "NEW YORK"}

· Dim sSearch As String

· Dim lResult As Long

· Dim oComp As New CaseInsensitiveComparer

·

· sSearch = "MICHIGAN"iResult = Array.BinarySearch(sArr, sSearch, oComp)

In this case, because of the case-insensitive comparison, the value of lResult is 2.

See Also

Array.IndexOf Method, Array.LastIndexOf Method, Array.Sort Method

156

Array.Copy MethodClass

System.Array

Syntax

Array.Copy(sourceArray, destinationArray, length)Array.Copy(sourceArray, sourceIndex, destinationArray, _destinationIndex, length)

sourceArray

Use: RequiredData Type: Any arrayThe array to be copied

sourceIndex

Use: Required in second overloaded versionData Type: IntegerThe index in sourceArray at which copying begins

destinationArray

Use: RequiredData Type: Any arrayThe target array

destinationIndex

Use: Required in second overloaded versionData Type: IntegerThe index in destinationArray where the first element is to be copied

length

Use: RequiredData Type: IntegerThe number of elements to copy

157

Return Value

None

Description

Makes a copy of all or part of an array.Since arrays are reference types, when we set one array variable equal to another, we are justassigning a new reference to the same array. For instance, consider the following code:

Dim a( ) As Integer = {1, 2, 3}Dim b( ) As Integer' Array assignmentb = a' Change bb(0) = 10' Check aMsgBox(a(0)) 'Displays 10

The fact that changing b(0) also changes a(0) shows that a and b point to the same array.

Rules at a Glance

· Using the first syntax, you can copy a range of values from the beginning of sourceArray tothe beginning of destinationArray. Using the second syntax, you can copy a range ofvalues from anywhere in destinationArray to anywhere in targetArray.

· sourceArray and destinationArray must have the same number of dimensions.

· length is the total number of elements to be copied. If sArr1 is a two-dimensional array, forexample, the statement:

Array.Copy(sArr1, 0, sArr2, 0, 3)

· copies the values from sArr(0,0), sArr(0,1), and sArr(1,0) to sArr2.

· To copy all elements, you can supply UBound(sourceArray) + 1 as an argument to

length.

· If sourceArray and destinationArray are the same, and destinationIndex lieswithin the range of values being copied (that is, if the source and target ranges overlap), nodata will be lost. The method behaves as if it copies length elements from sourceArray toa temporary buffer, then copies from the temporary buffer to destinationArray.

Example

Dim a( ) As Integer = {1, 2, 3}Dim c( ) As Integer' Array copyReDim c(UBound(a) + 1)Array.Copy(a, c, UBound(a) + 1)'Change cc(0) = 20'Check aMsgBox(a(0)) 'Displays 1

VB .NET/VB 6 Differences

158

Since arrays were not a reference type in VB 6, you could simply create a copy of an existing arraythrough assignment, thus eliminating the need for a Copy method.

Array.IndexOf MethodClass

System.Array

Syntax

Array.IndexOf(Array, Value[, startIndex[, count]])

Array

Use: RequiredData Type: Any arrayThe array to be searched

Value

Use: RequiredData Type: AnyThe object that is searched for

startIndex

Use: OptionalData Type: IntegerThe index at which to start the search

count

Use: OptionalData Type: IntegerThe number of items to search

Return Value

The index of the first occurrence of Value in Array, or -1

Description

159

Returns an Integer representing the index of the first occurrence of object in Array

Rules at a Glance

· Array must be a one-dimensional array.

· By default, the IndexOf method searches for Value from the beginning to the end of Array.

· If startIndex is provided without count, IndexOf searches from startIndex to the lastelement of Array.

· If both startIndex and count are provided, the method searches count elements startingat startIndex. In other words, it searches from array(startIndex) to

array(startIndex + count - 1).

· If startIndex is present and is outside of the range of the elements in array, the methodreturns -1.

· If count is present and startIndex + count - 1 exceeds the total number of elements in

array, the method call generates an ArgumentOutOfRangeException exception.

Example

The following code searches for a value in an Integer array:

Dim i As IntegerDim a(99999) As IntegerFor i = 0 To 99999a(i) = CInt(Rnd( ) * 100000)NextMsgBox(Array.IndexOf(a, 36500))

You can also specify the starting index for the search, as well as the number of elements to search.For example:

Array.IndexOf(array:=a, value:=136500, startIndex:=100, _count:=1000)

Array.LastIndexOf MethodClass

System.Array

Syntax

Array.LastIndexOf(Array, Value[, startIndex, count])

Array

Use: RequiredData Type: Any arrayThe array to be searched

Value

160

Use: RequiredData Type: AnyThe object that is searched for

startIndex

Use: OptionalData Type: IntegerThe index at which to start the search

count

Use: OptionalData Type: IntegerThe number of elements to search

Return Value

An Integer containing the index of the last occurrence of Object in Array

Description

Returns the index of the last occurrence of Object in Array

Rules at a Glance

· Array must be a one-dimensional array.

· The LastIndexOf method has the same syntax as the IndexOf method and works the sameway as IndexOf, except that it searches from the end of the array and returns the largest indexof a matching element.

· By default, the LastIndexOf method searches for Value from the end to the beginning of

Array.

· If startIndex is provided without count, LastIndexOf searches from startIndex to thefirst element of Array.

· If both startIndex and count are provided, the method searches count elementsbackward starting at startIndex. In other words, it searches from array(startIndex) to

array(startIndex - count + 1).

· If startIndex is present and is outside of the range of the elements in array, the methodreturns -1.

· If count is present and startIndex < count - 1, the method call generates anArgumentOutOfRangeException exception.

Example

The following code searches for a value in an Integer array:

Dim i As IntegerDim a(100000) As Integer

161

For i = 0 To 99999a(i) = CInt(Rnd( ) * 100000)NextMsgBox(Array.LastIndexOf(a, 36500))

You can also specify the starting index for the search, as well as the number of elements to search.For example:

Array.LastIndexOf(array:=a, value:=136500, startIndex:=100, _count:=50)

See Also

Array.IndexOf Method

Array.Reverse MethodClass

System.Array

Syntax

Array.Reverse(array[, startindex, endindex])

array

Use: RequiredData Type: Any arrayThe array to be reversed

startIndex

Use: OptionalData Type: IntegerThe index at which to start the reversal process

endIndex

Use: OptionalData Type: IntegerThe index at which to end the reversal process

Return Value

None

162

Description

Reverses a portion of or all of the elements of an array.

Example

Dim a( ) As Integer = {1, 2, 3, 4, 5}Dim i As Integerarray.Reverse(a, 1, 3)For i = 0 To 4debug.Write(a(i))Next

This code prints the sequence 14325, which is the original array 12345 with the middle section fromindex 1 to index 3 reversed.

Array.Sort MethodClass

System.Array

Syntax

Array.Sort(array)Array.Sort(array, comparer)Array.Sort(array, index, length)Array.Sort(array, index, length, comparer)Array.Sort(keys, items)Array.Sort(keys, items, comparer)Array.Sort(keys, items, index, length)Array.Sort(keys, items, index, length, comparer)

array

Use: RequiredData Type: Any arrayThe array of objects to be sorted

keys

Use: RequiredData Type: Any arrayThe array of keys to use for sorting. This array is also sorted.

items

Use: Required

163

Data Type: Any arrayA parallel array of values to be sorted in the order of keys, their corresponding keys

index

Use: RequiredData Type: IntegerThe index at which to start the sort

length

Use: RequiredData Type: IntegerThe index at which to end the reversal process

comparer

Use: RequiredData Type: IComparer interfaceAn object implementing the IComparer interface to be used for sorting. If Nothing, then the

IComparable implementation of each element (in the case of arrays of keys) or value type(in the case of arrays).

Return Value

None

Description

Sorts a portion of, or sorts an entire one-dimensional array, with an optionally specified key array andan optionally specified IComparer interface

Example

Sub sortArray( )Dim i As IntegerDim intArray( ) As Integer = {9, 8, 12, 4, 5}For i = 0 To 4console.WriteLine(CStr(intArray(i)))NextSystem.Array.Sort(intarray)Console.WriteLine("Sorted:")For i = 0 To 4console.WriteLine(CStr(intArray(i)))NextEnd Sub

The output is:

164

981245Sorted:458912

Asc, AscW FunctionsClass

Microsoft.VisualBasic.Strings

Syntax

Asc(string)AscW(str)

string, str

Use: RequiredData Type: String or CharAny expression that evaluates to a nonempty string

Return Value

An Integer that represents the character code of the first character of the string. The range for thereturned value is 0 - 255 on nonDBCS systems, but -32768 to 32767 on DBCS systems.

Description

Returns an Integer representing the character code for the first character of the string passed to it. Allother characters in the string are ignored

Rules at a Glance

· The string expression passed to the function must contain at least one character or a runtimeerror is generated.

· Only the first character of the string is evaluated by Asc or AscW.

Example

Dim sChars As StringDim iCharCode As IntegersChars = TextBox1.TextIf Len(sChars) > 0 Then

165

iCharCode = Asc(sChars)If iCharCode >= 97 And iChar <= 122 ThenMsgBox "The first character must be uppercase"End IfEnd If

Programming Tips and Gotchas

· Check that the string you are passing to the function contains at least one character using the

Len function, as the following example shows:

· If Len(sMyString) > 0 Then

· iCharCode = Asc(sMyString)

· Else

· MsgBox("Cannot process a zero-length string")End If

· Use Asc within your data-validation routines to determine such conditions as whether the firstcharacter is upper- or lowercase and whether it is alphabetic or numeric, as the followingexample demonstrates:

· Private Sub Button1_Click( ByVal sender As System.Object, _

· ByVal e As System.EventArgs) _

· Handles Button1.Click

·

· Dim sTest As String

· Dim iChar As Integer

·

· sTest = TextBox1.Text

·

· If Len(sTest) > 0 Then

· iChar = Asc(sTest)

· If iChar >= 65 And iChar <= 90 Then

· MsgBox "The first character is UPPERCASE"

· ElseIf iChar >= 97 And iChar <= 122 Then

· MsgBox "The first character is lowercase"

· Else

· MsgBox "The first character isn't alphabetical"

· End If

· Else

· MsgBox "Please enter something in the text box"

· End If

·

End Sub

· Use the Asc function and the related Chr function to create rudimentary encryption methods.Once you have obtained the character code for a particular character, you can performcalculations on this code to come up with a different number and then convert this to acharacter using the Chr function. To decrypt your string, simply reverse the calculation. Youmay want to avoid character codes less than 20, however, since these can be interpreted asspecial nonprinting characters and cause undesirable effects if displayed or printed.

· Private Sub CommandButton2_Click( )

·

· Dim MyEncryptedString, MyDecryptedString As String

· Dim MyName As String = "Paul Lomax"

· Dim i As Integer

·

166

· For i = 1 To Len(MyName)

· MyEncryptedString = MyEncryptedString & _

· Chr(Asc(Mid(MyName, i, 1)) + 25)

· Next i

·

· MsgBox("Hello, my name is " & MyEncryptedString)

·

· For i = 1 To Len(MyName)

· MyDecryptedString &= Chr(Asc(Mid(MyEncryptedString, i, 1)) - 25)Next i

·

· MsgBox("Hello, my name is " & MyDecryptedString)End Sub

See Also

Chr, ChrW Functions

Asin FunctionClass

System.Math

Syntax

Math.Asin(d)

d

Use: RequiredData Type: Double or any valid numeric expressionA number representing a sine, which can range from -1 to 1

Return Value

A Double between -pi/2 and pi/2 that is the arcsine of d in radians

Description

Returns the arcsine of d, in radians

Rules at a Glance

· If d is out of range, the function returns NaN.

· This is a Shared member, so it can be used without creating any objects.

Programming Tips and Gotchas

167

To convert from radians to degrees, multiply by 180/pi.

VB .NET/VB 6 Differences

The Asin function did not exist in VB 6.

See Also

Acos Function, Atan Function, Atan2 Function

Atan FunctionClass

System.Math

Syntax

Math.Atan(d)

d

Use: RequiredData Type: Double or any valid numeric expressionA number representing a tangent

Return Value

A Double that is the arctangent in radians of d, in the range -pi/2 to pi/2

Description

Takes the ratio of two sides of a right triangle (d) and returns the corresponding angle in radians. Theratio is the length of the side opposite the angle divided by the length of the side adjacent to the angle.

Rules at a Glance

· If d is out of range, the function returns NaN.

· This is a Shared member, so it can be used without creating any objects.

Example

Private Sub Main( )Dim dblSideAdj As DoubleDim dblSideOpp As DoubleDim dblRatio As DoubleDim dblAtangent As DoubledblSideAdj = 50.25

168

dblSideOpp = 75.5dblRatio = dblSideOpp / dblSideAdjdblAtangent = Math.Atan(dblRatio)'convert from radians to degreesdblDegrees = dblAtangent * (180 / 3.142)MsgBox dblDegrees & " Degrees"End Sub

Programming Tips and Gotchas

· To convert radians to degrees, multiply radians by 180/pi.

· Do not confuse Atan with the cotangent. Atan is the inverse trigonometric function of Tan,whereas the cotangent is the reciprocal of the tangent.

VB .NET/VB 6 Differences

The Atan function corresponds to the VB 6 Atn intrinsic function.

See Also

Acos Function, Asin Function, Atan2 Function

Atan2 FunctionClass

System.Math

Syntax

Math.Atan2(y, x)

x

Use: RequiredData Type: DoubleThe x coordinate of a point

y

Use: RequiredData Type: DoubleThe y coordinate of a point

Return Value

169

A Double that is the arctangent of the ratio x/y, in radians

Description

Returns the angle in the Cartesian plane formed by the x-axis and a vector starting from the origin (0,0)and terminating at the point (x, y). More specifically, the return value q satisfies the following:

· For (x, y) in quadrant 1, 0 < q < pi/2.

· For (x, y) in quadrant 2, pi /2 < q < pi.

· For ( x, y) in quadrant 3, -pi < q < -pi /2.

· For ( x, y) in quadrant 4, -pi /2 < q < 0.

Rules at a Glance

This is a Shared member, so it can be used without creating any objects.

VB .NET/VB 6 Differences

The Atan2 function does not exist in VB 6.

See Also

Acos Function, Asin Function, Atan Function

Beep ProcedureClass

Microsoft.VisualBasic.Interaction

Syntax

Beep

Description

Sounds a tone through the computer's speaker

Example

Private Sub Main( )iVoid = DoSomeLongFunction( )BeepMsgBox "Finished!"End Sub

Programming Tips and Gotchas

170

· We have found the Beep statement to be completely unreliable, and therefore we never use itin applications intended for distribution.

· If you do decide to use the Beep statement, please remember that its overuse will not endearyou to your users!

· The frequency and duration of the tone depends on the computer's hardware. Bear in mindthat on some systems, a mouse click is louder than the beep!

· Since the successful operation of the Beep statement does not require the presence of anymultimedia hardware (such as a sound card, for example), it can be used when a system isnot configured to support sound. For example, if the following is defined in the declarationssection of a code module:

· Declare Function waveOutGetNumDevs Lib "winmm.dll" ( ) As Long

· Declare Function PlaySound Lib "winmm.dll" _

· Alias "PlaySoundA" (ByVal lpszName As String, _

· ByVal hModule As Long, ByVal dwFlags As Long) _

· As Long

·

· Public Const SND_APPLICATION = &H80

· Public Const SND_ASYNC = &H1

· Public Const SND_FILENAME = &H20000

· Public Const SND_NODEFAULT = &H2

·

· Public HasSound As Boolean

·

· Public Function IsSoundSupported( ) As Boolean

· If (waveOutGetNumDevs > 0) Then _

· IsSoundSupported = TrueEnd Function

then the following procedure takes advantage of any existing sound hardware to play a wavefile or simply beeps the built-in PC speaker if no sound hardware is found.

Private Sub Form_Load( ByVal sender As System.Object, _ByVal e As System.EventArgs) _Handles MyBase.LoadDim intCtr As IntegerHasSound = IsSoundSupported( )If HasSound ThenCall PlaySound("c:\windows\media\tada.wav", 0, _SND_FILENAME Or SND_NODEFAULT)ElseFor intCtr = 0 To 3BeepNextEnd IfEnd Sub

Call StatementSyntax

[Call] procedurename[(argumentlist)]procedurename

Use: Required

171

Data Type: N/AThe name of the subroutine being called

argumentlist

Use: OptionalData Type: AnyA comma-delimited list of arguments to pass to the subroutine being called

Description

Passes execution control to a procedure, function, or dynamic-link library (DLL) procedure or function

Rules at a Glance

· Use of the Call keyword is optional.

· Regardless of whether the Call keyword is used, argumentlist, if it is present, must beenclosed in parentheses.

· If you use Call to call a function, the function's return value is discarded.

Example

Call myProcedure(True, iMyInt)Sub myProcedure(blnFlag as Boolean, iNumber as Integer) End Sub

Programming Tips and Gotchas

· To pass a whole array to a procedure, use the array name followed by empty parentheses.

· Some programmers suggest that code is more readable when the Call keyword is used tocall subroutines.

VB .NET/VB 6 Differences

· In VB 6, parentheses had to be omitted if the Call keyword was omitted and

procedurename had more than one argument. In VB .NET, parentheses are requiredwhenever arguments are present.

· In VB 6, if argumentlist consisted of a single argument, enclosing it in parentheses andomitting the Call statement reversed the method by which the argument was passed to thecalled function. Thus, an argument ordinarily called by value would be called by reference,and vice versa. In VB .NET, this confusing behavior is not supported.

· In VB 6, when calling an external routine defined using the Declare statement, you canoverride the default method of passing an argument by specifying the ByVal or ByRef

keywords before the argument. In VB .NET you cannot change whether an argument ispassed by value or by reference in the call to the routine.

See Also

CallByName Function

172

CallByName FunctionClass

Microsoft.VisualBasic.Interaction

Named Arguments

Yes, if Args( ) is omitted

Syntax

CallByName(Object, ProcName, UseCallType, Args( ))

Object

Use: RequiredData Type: ObjectA reference to the object containing the procedure being called.

ProcName

Use: RequiredData Type: StringThe name of the procedure to call.

UseCallType

Use: RequiredData Type: CallType ConstantA constant of the type CallType indicating what type of procedure is being called. CallType

constants are listed in the following table.

Constant Value Description

Method 1 The called procedure is a method.

Get 2 The called procedure retrieves a property value.

Let 4 The called procedure sets the value of a property.

Args

Use: OptionalData Type: AnyA ParamArray argument representing the arguments required by the procedure being called.

173

Return Value

Depends on the return value (if any) of the called procedure

Description

Provides a method for calling a class member by name.Since ProcName is a string expression, rather than the literal name of a routine, it is possible to callroutines dynamically at runtime using a string variable to hold the various procedure names.

Rules at a Glance

· The return type of CallByName is the return type of the called procedure.

· ProcName is not case sensitive.

· UseCallType can either be a numeric value or a constant of the CallType enumeration. Inthe latter case, the enumeration name must be specified along with the constant name, as inCallType.Method.

· Args( ) must be a parameter array. A parameter array is an array used to contain function,procedure, or property arguments that can have a variable number of elements.

Programming Tips and Gotchas

· Since the member to be called is not known at compile time, the performance of CallByName

is inferior to calling members directly by literal name.

· Using CallByName does not necessarily require that Option Strict be set Off.

Example

The following example uses a parameter array to call the Multiply method of a class named Math:

Imports Microsoft.VisualBasicImports SystemModule modMainPublic Sub Main( )Dim oMath As New MathDim dArr( ) As Double = {1,2,3}' Call using ParamArrayMsgBox(CallByName(oMath, "Multiply", CallType.Method, dArr))End SubEnd ModulePublic Class MathPublic Function Multiply(a( ) As Double) As DoubleDim result as double = 1.0Dim intCtr As IntegerDim intIndex As Integer = 0

174

for intIndex = 0 to ubound(a)result = result * a(intIndex)nextMultiply = resultEnd FunctionEnd Class

VB .NET/VB 6 Differences

In VB 6, you don't have to specify VbCallType as the name of the enumeration to access itsconstants. In VB .NET, you must specify CallType as the name of the enumeration to access itsconstants.

See Also

Call Statement

CBool FunctionNamed Arguments

No

Syntax

CBool(expression)

expression

Use: RequiredData Type: String or NumericAny numeric expression or a string representation of a numeric value

Return Value

expression converted to Boolean data type (True or False)

Description

Casts expression as a Boolean data type

Rules at a Glance

When a numeric value is converted to Boolean, any nonzero value is converted to True, and zero isconverted to False.

175

If the expression to be converted is a string, the string must be capable of being evaluated as anumber, or it must be "True" or "False". Any other string generates a runtime error. For example,

CBool("one") results in a type mismatch error, whereas CBool("1") is converted to True, and

CBool("True") is converted to True.

Programming Tips and Gotchas

· You can check the validity of the expression prior to using the CBool function by using the

IsNumeric function.

· Like most of the conversion functions, CBool is not actually a function in theMicrosoft.VisualBasic namespace. Instead, it is similar to a Visual C++ macro; the compilertranslates the function call into inline code.

CByte FunctionNamed Arguments

No

Syntax

CByte(expression)

expression

Use: RequiredData Type: Numeric or StringA string or numeric expression that evaluates to a number between 0 and 255

Return Value

expression converted to Byte data type

Description

Converts expression to a Byte data type

Rules at a Glance

· If the expression to be converted is a string, the string must be capable of conversion to anumeric expression; this can be checked using the IsNumeric function.

· If expression evaluates to less than 0 or more than 255, a runtime error is generated.

· If the value of expression is not a whole number, CByte rounds the number prior toconversion.

Example

If IsNumeric(sMyNumber) ThenIf val(sMyNumber) >= 0 and val(sMyNumber) <= 255 ThenBytMyNumber = CByte(sMyNumber)

176

End IfEnd If

Programming Tips and Gotchas

· Check that the value you pass to CByte is neither negative nor greater than 255.

· Use IsNumeric to ensure that the value passed to CByte can be converted to a numericexpression.

· When using CByte to convert floating point numbers, fractional values up to but notincluding .5 are rounded down, while values above but not including .5 are rounded up. Valueswhose fractional component is exactly equal to .5 are rounded up if their integral component isodd and down if their integral component is even.

· The CByte function converts an expression to an unsigned byte data type. To convert

expression to a signed byte data type, create an instance of the SByte class and call itsParse method.

· Like most of the conversion functions, CByte is not actually a function in theMicrosoft.VisualBasic namespace. Instead, it is similar to a Visual C++ macro; the compilertranslates the function call into inline code.

CChar FunctionNamed Arguments

No

Syntax

CChar(expression)

expression

Use: RequiredData Type: StringAny string expression

Return Value

A value of type Char

Description

Converts the first character in a string expression to a Char data type

Rules at a Glance

CChar extracts the first character of expression and converts it to a Char data type.

Example

MsgBox(CChar("abc")) ' Displays aMsgBox(CChar("56")) ' Displays 5

177

Programming Tips and Gotchas

· If you wish to convert a numeric code to its corresponding Char data type, use the ChrW

function.

· Like most of the conversion functions, CChar is not actually a function in theMicrosoft.VisualBasic namespace. Instead, it is similar to a Visual C++ macro; the compilertranslates the function call into inline code.

See Also

Chr, ChrW Functions

CDate FunctionNamed Arguments

No

Syntax

CDate(expression)

expression

Use: RequiredData Type: String or NumericAny valid representation of a date and time

Return Value

expression converted into a Date data type.

Description

Converts expression to a Date data type.The format of expression—the order of day, month, and year—is determined by the locale setting ofthe local computer. To be certain a date is recognized correctly by CDate, the month, day, and yearelements of expression must be in the same sequence as the local computer's regional settings;otherwise, the CDate function has no idea, for example, that 4 was supposed to be the fourth day ofthe month, not the month of April.

Rules at a Glance

· You can use any of the date delimiters specified in your computer's regional settings; for mostsystems, this includes ,, /, -, and .

· The earliest date that can be handled by the Date data type is 01/01/100. The latest date thatcan be handled by the Date data type is 12/31/9999.

178

Programming Tips and Gotchas

· Use the IsDate function to determine if expression can be converted to a date or time.

· If you pass an empty string to CDate, an error is generated.

· A modicum of intelligence has been built into the CDate function. It can determine the day andmonth from a string, regardless of their position in the string; this applies only where the daynumber is larger than 12, which automatically distinguishes it from the number of the month.For example, if the string "30/12/97" is passed into the CDate function on a system expectinga date format of mm/dd/yy, CDate sees that 30 is too large to represent a month and thustreats it as the day. This can lead to problems because if we accidentally pass a string such as"30/12/97" instead of the intended "3/12/97," then VB does not issue an error message!

· If we pass a string whose year specification is less than three characters in length, then VBinterprets the year as belonging to the twenty-first century. For instance, the string "1/1/1" isinterpreted as "1/1/2001."

· If you do not specify a year, the CDate function uses the year from the current date on yourcomputer.

· Like most conversion functions, CDate is not actually a function in the Microsoft.VisualBasicnamespace. Instead, it is similar to a Visual C++ macro; the compiler translates the functioncall into inline code.

CDbl FunctionNamed Arguments

No

Syntax

CDbl(expression)

expression

Use: RequiredData Type: Numeric or String-1.79769313486232E308 to -4.94065645841247E-324 for negative values, and4.94065645841247E-324 to 1.79769313486232E308 for positive values

Return Value

expression cast as a Double data type.

Description

Converts expression to a Double data type

Rules at a Glance

· If the value of expression is outside the range of the double data type, an overflow error isgenerated.

179

· expression must evaluate to a numeric value; otherwise, a type-mismatch error isgenerated.

Example

Dim dblMyNumber as DoubleIf IsNumeric(sMyNumber) thendblMyNumber = CDbl(sMyNumber)End If

Programming Tips and Gotchas

· When converting a string representation of a number to a numeric value, the data typeconversion functions, such as CDbl, are preferable to the older function, Val. This is becausethe data type conversion functions take account of the system's regional settings, whereas Val

recognizes only the period as a decimal separator. For example, if a user inputs a value of6,231,532.11, CDbl correctly converts it to a double with a value of 6231532.11, while Val

returns a value of 6.

· Use IsNumeric to test whether expression evaluates to a number.

· Like most conversion functions, CDbl is not actually a function in the Microsoft.VisualBasicnamespace. Instead, it is similar to a Visual C++ macro; the compiler translates the functioncall into inline code.

See Also

CSng Function

CDec FunctionNamed Arguments

No

Syntax

CDec(expression)

expression

Use: RequiredData Type: Numeric or StringThe range is +/-79,228,162,514,264,337,593,543,950,335 for numbers with no decimal places.The range is +/-7.9228162514264337593543950335 for numbers with up to 28 decimalplaces. The smallest possible nonzero number is 0.0000000000000000000000000001.

Return Value

expression cast as a Decimal type

Description

180

This function casts expression as a Decimal value.

Rules at a Glance

· If the value of expression is outside the range of the Decimal data type, an overflow error isgenerated.

· expression must evaluate to a numeric value; otherwise a type-mismatch error is generated.To prevent this, it can be tested beforehand with the IsNumeric function.

Example

Dim decMyNumber As DecimalIf IsNumeric(sMyNumber) thendecMyNumber = CDec(sMyNumber)End If

Programming Tips and Gotchas

· The Decimal data type replaces the VB 6 Currency data type and is appropriate for very large,very small, or very high precision numbers.

· Use IsNumeric to test whether expression evaluates to a number.

· When converting a string representation of a number to a numeric, you should use the datatype conversion functions—such as CDec—instead of Val, because the data type conversionfunctions take account of the system's regional settings. In particular, the CDec functionrecognizes the thousands separator if it is encountered in the string representation of anumber. For example, if the user inputs the value 1,827,209.6654, CDec converts it to a thedecimal value 1827209.6654, while Val converts it to a Double value of 1.

· Like most of the conversion functions, CDec is not actually a function in theMicrosoft.VisualBasic namespace. Instead, it is similar to a Visual C++ macro; the compilertranslates the function call into inline code.

Ceiling FunctionClass

System.Math

Syntax

Math.Ceiling(a)

a

Use: RequiredData Type: Double

Return Value

A Double containing the smallest integer greater than or equal to the argument a.

Description

181

Returns the smallest integer greater than or equal to the argument a.

Example

Console.WriteLine(Math.Ceiling(12.1)) ' Returns 13Console.WriteLine(Math.Ceiling(12.5)) ' Returns 13Console.WriteLine(Math.Ceiling(-12.5)) ' Returns -12Console.WriteLine(Math.Ceiling(-12.8)) ' Returns -12

Rules at a Glance

· Because this function can only accept numeric values, you may want to check the value youpass using the IsNumeric function to prevent generating an error.

· This is a Shared member, so it can be used without creating any objects.

VB .NET/VB 6 Differences

The Ceiling function is new to the .NET Framework.

See Also

Floor Function

ChDir ProcedureClass

Microsoft.VisualBasic.FileSystem

Syntax

ChDir(path)

path

Use: RequiredData Type: StringThe path of the directory to set as the new default directory

Description

Changes the current working (default) directory.

Rules at a Glance

· path can be an absolute or relative reference.

· Changing the default directory does not change the default drive; it only changes a particulardrive's default directory.

182

Example

ChDir("c:\program files\my folder\")ChDir("..") 'c:\program files is now the default directory.

Programming Tips and Gotchas

· The single dot (".") represents the current directory and the double dot ("..") representsthe parent of the current directory. If the root directory is the current directory, the statement:

ChDir("..")

· does not change the current directory and does not produce a syntax error.

· If path is not found, or a FileNotFoundExeception exception, 76, "Path not found," isgenerated. However, if path refers to another machine on the network, error 75, "Path/Fileaccess error," is generated.

· Although you can use a network path such as \\NTSERV1\d$\TestDir\ to change the currentdirectory on the network admin share \\NTSERV1\d$, you can't access this drive using

ChDrive without having the drive mapped to a drive letter, which makes using network pathswith ChDir a little pointless!

· Use CurDir to determine the current directory for a particular drive.

VB .NET/VB 6 Differences

In VB .NET, ChDir is implemented as a procedure (a method of the FileSystem class). In VB 6, it isimplemented as a statement. As a result, the VB .NET version requires parentheses around the path

argument.

See Also

ChDrive Procedure, CurDir Function

ChDrive ProcedureClass

Microsoft.VisualBasic.FileSystem

Syntax

ChDrive(drive)

drive

Use: RequiredData Type: String or CharThe letter of the drive (A-Z) to set as the new default drive

Description

183

Changes the current working (default) disk drive

Rules at a Glance

· If a zero-length string is supplied, the drive is not changed.

· If driveletter consists of more than one character, only the first character is used todetermine the drive.

Example

The following example demonstrates a utility function that uses ChDrive to determine if a given drive isavailable. By centralizing the test, this reduces the amount of coding required each time you need touse ChDrive.

Private Function IsAvailableDrive(sDrive As String) _As Boolean'if an error occurs goto to the next line of codeOn Error Resume NextDim sCurDrv As String'get the letter of the current drivesCurDrv = Left$(CurDir, 1)'attempt to change the driveChDrive(sDrive)'did an error occur?If Err.Number = 0 Then'no - this drive is OK to useIsAvailableDrive = TrueElse'yes - don't use this driveIsAvailableDrive = FalseEnd If'set the drive back to what it wasChDrive(sCurDrv)End Function

The following code snippet shows how this function could be implemented within your application:

If IsAvailableDrive(sDrv) ThenChDrive(sDrv)ElseMsgBox ("Cannot use Drive " & sDrv & ":\")End If

Programming Tips and Gotchas

· The current directory is unaffected by the ChDrive procedure.

· Since ChDrive only processes the first letter of the drive string, it's not possible to supply apiped name as a network drive name (for example, \\NTServer\). Instead, the machine onwhich your program runs must have a drive letter mapped to the network resource usingExplorer or other network commands. If drive is specified as a UNC path, the function raiseserror number 5, "Invalid procedure call or argument," or generates an ArgumentExceptionexception.

184

· If drive is invalid, the function returns error number 68, "Device unavailable," or generates anIOException exception.

· To determine which drive is current, call the CurDir function with no arguments. Then use the

Left function to extract its first character, as the following code fragment illustrates:

Dim sDrive As String = Left(CurDir( ), 1)

VB .NET/VB 6 Differences

In VB .NET, ChDrive is implemented as a procedure (a method of the FileSystem class). In VB 6, it isimplemented as a statement. As a result, the VB .NET version requires parentheses around the

drive argument.

See Also

ChDir Procedure, CurDir Function

Choose FunctionClass

Microsoft.VisualBasic.Interaction

Named Arguments

No

Syntax

Choose(index, item_1[,item_2, [, item_n]])

index

Use: RequiredData Type: SingleAn expression that evaluates to the (1-based) index of the object to choose from the list

item_1-item_n

Use: RequiredData Type: AnyA comma-delimited list of values from which to choose, or a ParamArray containing valuesfrom which to choose

Return Value

The object chosen from the list.

185

Description

Programmatically selects an object from a predefined list of objects (which are passed as parametersto the function) based on its ordinal position in the list. Using Choose is a simpler alternative topopulating an array with fixed values.

Rules at a Glance

· The list of items is based from 1, rather than the more usual VB default base of 0.

· Because the list consists of objects, you can mix data types within the list; you are not forcedto use the same data type for each item in the list. For example, item_1 can be a string, while

item_2 can be a long integer, and item_3 can be a floating point number.

· If the rounded value of index does not correspond to an item in the list, the function returns anull string.

Programming Tips and Gotchas

· If index is not a whole number, it is rounded before being used.

· It is important to note that all items in the list are evaluated. Thus, if we use functions orexpressions as parameters, all of the functions are called or all of the expressions areevaluated.

· By providing item_1 through item_n in the form of a ParamArray, the list of values can beexpanded or contracted programmatically at runtime.

· You can save memory and create more efficient and self-documenting code by using the

Choose function instead of creating an array and populating it with fixed values each time theprogram executes. As the following example illustrates, you can turn several lines of code intoone:

· Dim vMyArray(3)

· vMyArray(1) = "This"

· vMyarray(2) = "That"

· vMyArray(3) = "The Other"

·

· Sub chooseFromArray(iIndex as Integer)

· vResult = vMyArray(iIndex)

· End Sub

·

· Sub chooseFromChoose(sglIndex as Single)

· vResult = Choose(sglIndex, "This", "That", "The Other")End Sub

VB .NET/VB 6 Differences

· In VB 6, item_1 through item_n must only take the form of a comma-delimited list. InVB .NET, these arguments can also take the form of an array. This allows the list of choices tobe modified dynamically at runtime.

· In VB 6, idx must be greater than .5 and less than .5 plus the number of items in the list, or aruntime error results. In VB .NET, if idx is out of range, the function returns a null string.

See Also

Switch Function

Chr, ChrW Functions

186

Class

Microsoft.VisualBasic.Strings

Syntax

Chr(charcode)ChrW(charcode)

charcode

Use: RequiredData Type: IntegerAn expression that evaluates to a Unicode character code

Return Value

A Char that contains the character represented by charcode

Description

Returns the character represented by the charcode

Programming Tips and Gotchas

· Use Chr(34) to embed quotation marks inside a string, as shown in the following example:

· sSQL = "SELECT * FROM myTable _where myColumn = " & Chr(34) & sValue & Chr(34)

· The following table lists some of the more commonly used character codes that are suppliedin the call to the Chr function:

Code Constant Description

0 vbNullChar For C/C++ string functions, the null character required toterminate standard strings8 vbBack A backspace character9 vbTab A tab character10 vbLf A linefeed character13 vbCr A carriage return character34 ControlChars.Quote A quotation mark

VB .NET/VB 6 Differences

· The ChrB function is no longer supported.

· The VB 6 version of the Chr function returns a String; the VB .NET version returns a Char.

See Also

187

Asc, AscW Functions

CInt FunctionNamed Arguments

No

Syntax

CInt(expression)

expression

Use: RequiredData Type: Numeric or StringThe range of expression is -2,147,483,648 to 2,147,483,647; fractions are rounded.

Return Value

expression cast as an Integer

Description

Converts expression to an Integer; any fractional portion of expression is rounded.

Rules at a Glance

· expression must evaluate to a numeric value; otherwise, a type-mismatch error isgenerated.

· If the value of expression is outside the range of the Integer data type, an overflow error isgenerated.

· When the fractional part of expression is exactly .5, CInt always rounds it to the nearesteven number. For example, .5 rounds to 0, and 1.5 rounds to 2.

Example

Dim iMyNumber as IntegerIf IsNumeric(sMyNumber) theniMyNumber = CInt(sMyNumber)End If

Programming Tips and Gotchas

· When converting a string representation of a number to a numeric data type, you should usethe data type conversion functions—such as CInt—instead of Val, because the data typeconversion functions take into account the system's regional settings. In particular, CInt

recognizes the thousands separator if it's present in expression, whereas Val does not. Forexample, if expression is 1,234, then CInt successfully converts it to the integer value 1234,while Val converts it to 1.

188

· Use IsNumeric to test whether expression evaluates to a number before performing theconversion.

· CInt differs from the Fix and Int functions, which truncate, rather than round, the fractional partof a number. Also, Fix and Int always return the same type of value as was passed in.

· CInt converts an expression to a signed 32-bit integer. To convert an expression to anunsigned 32-bit integer, create an instance of the UInt32 structure, and call its Parse method.

· Like most of the conversion functions, CInt is not actually a function in theMicrosoft.VisualBasic namespace. Instead, it is similar to a Visual C++ macro; the compilertranslates the function call into inline code.

VB .NET/VB 6 Differences

The VB .NET CInt function actually corresponds to the VB 6 CLng function, since both return 32-bitintegers.

See Also

CLng Function, CShort Function

Class StatementSyntax

[accessmodifier] [inheritability] ClassNamestatements

End Class

accessmodifier

Use: OptionalType: KeywordThe possible values of accessmodifier are Public, Private, and Friend. For moreinformation, see Section 3.7 in Chapter 3.

inheritability

Use: OptionalType: KeywordOne of the keywords, MustInherit or NotInheritable, must be used. MustInherit

specifies that objects of this class cannot be created, but that objects of derived classes canbe created. NotInheritable specifies that this class cannot be used as a base class.

ClassName

Use: RequiredType: String literalThis is the name of the class.

189

Description

Defines a class and delimits the statements that define that class' variables, properties, and methods.For a detailed discussion with examples, see Chapter 3.

Rules at a Glance

· ClassName follows standard Visual Basic variable-naming conventions.

· Within a class code block, members are declared as Public, Private, Protected,

Friend, or Protected Friend. The Dim keyword is equivalent to Private when used inclass modules (but it is equivalent to Public in structures). Property declarations areautomatically Public.

· The ClassEnd Class construct can include the following elements:

Private variable or procedure declarations

These items are accessible within the class, but do not have scope outside of the class.

Public variable or procedure declarations

Public variables are public properties of the class; Public procedures are public methodsof the class.

Property declarations

These are the public properties of the class. Default properties can be declared by using the

Default keyword.

· To define a custom constructor within a class module, define a subroutine called New. Notethat the New subroutine (like any other procedure) can be overloaded.

· To define a destructor within a class module, define a function called Destruct. Destructorscannot be overloaded.

· To create an object of a class, use syntax such as:

· Dim oObj As CClassoObj = New CClass(arguments_for_constructor)

or:

Dim oObj = New CClass(arguments_for_constructor)

or:

Dim oObj As CClass = New CClass(arguments_for_constructor)

Programming Tips and Gotchas

· A property defined as a simple public variable cannot be designated the class' default member.

· According to accepted object-oriented programming practices, public properties should bedefined using the Property statement, since this allows the value of a property to bemodified in a controlled and predictable way. It allows you to validate data and allows yourprogram to know when a property value is being changed. Because this is not possible usingsimple public variables, defining a public variable that is accessible outside of the class isconsidered poor programming practice.

· The Me or MyClass keywords can be used within the ClassEnd Class construct toreference the class.

190

VB .NET/VB 6 Differences

The ClassEnd Class construct is new to VB .NET. In VB 6, each class was defined in its ownclass module, which corresponded to a separate CLS file.

See Also

Property Statement, StructureEnd Structure Statement

Clipboard ClassNamespace

System.Windows.Forms

Createable

No

Description

The Clipboard object represents the Windows Clipboard, an object that allows data to be sharedacross processes. The members of the Clipboard class allow data to be placed in and retrieved fromthe Clipboard.The Clipboard object can be created as follows:

Dim obj As Clipboard

However, because the Clipboard object's members are shared, you do not need to instantiate theClipboard object to access its properties and methods. Hence, you can place data on the Clipboard,for instance, with the following code fragment:

Clipboard.SetDataObject(strData)

Application class members marked with a plus sign (+) are discussed in detail in their own entries.

Public Shared Methods

GetDataObject +SetDataObject +

See Also

Clipboard.GetDataObject Method, Clipboard.SetDataObject Method

Clipboard.GetDataObject Method

191

Class

System.Windows.Forms.Clipboard

Syntax

Clipboard.GetDataObject( )

Return value

An IDataObject object that represents the data currently on the clipboard

Description

Retrieves data from the Clipboard

Rules at a Glance

· If the Clipboard contains no data, the GetDataObject method returns Nothing.

· Once you have an IDataObject object, you can use the members of the IDataObject classto get information about the Clipboard data, as shown in the following example. The relevantIDataObject members for Clipboard manipulation in VB are GetData, GetDataPresent, andGetFormats.

Example

The following example extracts the text that is currently on the Clipboard:

' Declare IDataObject variable and get clipboard IDataObjectDim di As IDataObject = Clipboard.GetDataObjectDim obj As Object' Fire GetData method of IDataObject object to get clipboard dataobj = di.GetData(DataFormats.Text, False)' Show the text, if anyIf obj Is Nothing ThenMsgBox("No text on clipboard.")ElseMsgBox(CStr(obj))End If

VB .NET/VB 6 Differences

While the .NET Base Class Library uses the GetDataObject method to retrieve all data from theClipboard, the Clipboard object in VB 6 included the GetFormat, GetData, and GetText methods toretrieve Clipboard data.

See Also

Clipboard Class, Clipboard.SetDataObject Method, IDataObject Interface

192

Clipboard.SetDataObject MethodClass

System.Windows.Forms.Clipboard

Syntax

SetDataObject(data)

data

Use: RequiredData Type: AnyData to place on the Clipboard

Description

Places data on the Clipboard

Example

The following example places text on the clipboard:

Dim s As String = "donna"clipboard.SetDataObject(s)

VB .NET/VB 6 Differences

While the .NET Base Class Library uses the SetDataObject method to place all data on the Clipboard,the Clipboard object in VB 6 includes two methods, SetData and SetText, depending on the format ofthe data to be placed on the Clipboard.

See Also

Clipboard Class, Clipboard.GetDataObject Method, IDataObject Interface

CLng FunctionNamed Arguments

No

Syntax

CLng(expression)

193

expression

Use: RequiredData Type: Numeric or StringRanges from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807; fractions arerounded.

Return Value

expression cast as a Long data type

Description

Converts expression to a long integer; any fractional element of expression is rounded.

Rules at a Glance

· expression must evaluate to a numeric value; otherwise, a type-mismatch error isgenerated.

· If the value of expression is outside the range of the Long data type, an overflow error isgenerated.

· When the fractional part is exactly .5, CLng always rounds it to the nearest even number. Forexample, .5 rounds to 0, and 1.5 rounds to 2.

Example

Dim lngMyNumber as LongIf IsNumeric(sMyNumber) thenlngMyNumber = CLng(sMyNumber)End If

Programming Tips and Gotchas

· When converting a string representation of a number to a numeric, you should use the datatype conversion functions—such as CLng—instead of Val, because the data type conversionfunction takes into account the system's regional settings. In particular, CLng is able torecognize the thousands separator if it's included in expression, while Val cannot. Forexample, if a user enters a value of 1,098,234 into a textbox, CLng converts it to the longinteger 1098234, but Val converts it to a value of 1.

· Use IsNumeric to test whether expression evaluates to a number.

· CLng differs from the Fix and Int functions, which truncate, rather than round, the fractionalpart of a number. Also, Fix and Int always return the same type of value as was passed in.

· CLng converts an expression to a signed long integer. To convert an expression to anunsigned long integer, create an instance of the UInt64 structure and call its Parse method.

· Like most of the conversion functions, CLng is not actually a function in theMicrosoft.VisualBasic namespace. Instead, it is similar to a Visual C++ macro; the compilertranslates the function call into inline code.

VB .NET/VB 6 Differences

The VB .NET CLng function returns a 64-bit integer, whereas the VB 6 CLng function returns a 32-bitinteger.

194

See Also

CInt Function , CShort Function

CObj FunctionNamed Arguments

No

Syntax

CObj(expression)

expression

Use: RequiredData Type: Any

Return Value

expression cast as an Object data type

Description

Converts any expression that can be interpreted as an object to Object

Rules at a Glance

expression can be any data type, including a strongly typed object, as the following code fragmentillustrates:

Dim oSomeClass As New CSomeClassDim oObj As ObjectoObj = CObj(oSomeClass)

Example

The following code:

Dim obj As Object

obj = CObj("test")

casts the string "test" to type Object and places it in the Object variable obj.

Programming Tips and Gotchas

· The operation of the CObj function is possible because all VB .NET data types are eitherstructures or objects.

195

· Once a data type is converted to type Object, you can display its value by calling its ToStringmethod, as in the following code fragment:

· Dim bFlag As Boolean = True

·

· oObj = CObj(bFlag)MsgBox(oObj.ToString)

· Instead of using the CObj function to convert a strongly typed object to a generic Object datatype, you can also use simple assignment, as the following code fragment illustrates:

· Dim oSomeClass As New CSomeClass

· Dim oObj As ObjectoObj = oSomeClass

· Like most of the conversion functions, CObj is not actually a function in theMicrosoft.VisualBasic namespace. Instead, it is similar to a Visual C++ macro; the compilertranslates the function call into inline code.

VB .NET/VB 6 Differences

The CObj function is new to VB .NET. The closest equivalent in VB 6 is CVar, which converts a datatype to a Variant.

Collection ClassNamespace

Microsoft.VisualBasic

Createable

Yes

Syntax

Dim objectvariable As [New] Collection

objectvariable

Use: RequiredData Type: CollectionThe name of the Collection object

Description

A Collection object allows you to store members of any data type, including object data types or evenother collection objects, and to retrieve them using a unique key.Collection objects allow us to create a form of associative array, which is an array whose members areindexed by something more meaningful than an integer. The real power of a collection comes by usingcollections with class objects. The Collection object is discussed in more detail in Chapter 2.

196

Collection objects are created in exactly the same way as other objects, as in:

Dim obj As New Collection

or:

Dim obj As Collection

obj = New Collection

In the former syntax, the Collection object is created at the time that the obj variable is declared,which may be sooner than you actually need the Collection object. The latter syntax gives you morecontrol over the creation process.

Rules at a Glance

· You can use a Collection object to store data of any data type, including object types and evenother Collection objects.

· The Add method of the Collection object is used to add items to the collection (see theCollection.Add entry).

· Members of a collection can be accessed using either their ordinal number or their key,assuming that one was assigned at the time that the member was added to the collection (seethe Collection.Item entry).

· The first member in a collection is stored at ordinal position 1 (not at 0, as with arrays).

· The Count method returns the number of members in the collection (see the Collection.Countentry).

· The Remove method removes items from a collection (see the Collection.Remove entry).

Example

This example shows how you can nest one collection within another collection. We create 10instances of colSubCollection, each containing two integer values. These colSubCollection

objects are stored in the collection named colMainCollection. The code also shows how to readthe values of colMainCollection and colSubCollection.

Sub testCollection( )'declare objects for the main and sub collections'creating a new instance of the main collection'in the processDim colMainCollection As New CollectionDim colSubCollection As CollectionDim i As IntegerFor i = 1 To 10'create a new instance of the sub collection objectcolSubCollection = New Collection'populate the sub collection with two integer valuescolSubCollection.Add(Item:=i + 6, _Key:="MySixPlusVal")colSubCollection.Add(Item:=i + 3, _Key:="MyThreePlusVal")'now add the sub collection to the main collection'using the count converted to a string as the keycolMainCollection.Add(Item:=colSubCollection, _Key:=CStr(i))'destroy the reference the sub collectioncolSubCollection = NothingNext i

197

MsgBox(colMainCollection.Count)For i = 1 To colMainCollection.Count'use the Item method to obtain a reference to the'subcollectioncolSubCollection = _colMainCollection.Item(CStr(i))'display the values held in the sub collection.Console.WriteLine("6 + " & i & " = " & _colSubCollection.Item("MySixPlusVal"))Console.WriteLine("3 + " & i & " = " & _colSubCollection.Item("MyThreePlusVal"))'destroy the reference to the sub collectioncolSubCollection = NothingNext iEnd Sub

Programming Tips and Gotchas

· A highly efficient method of enumerating the members of a collection is to use the ForEachNext loop, as the following example shows:

· Dim colMyCollection As New Collection

· Dim colSubCollection As Collection

·

· For i = 1 To 10

· Set colSubCollection = New Collection

· colSubCollection.Add Item:=i + 6, _

· Key:="MySixPlusVal"

· colSubCollection.Add Item:=i + 3, _

· Key:="MyThreePlusVal"

· colMyCollection.Add Item:=colSubCollection, _

· Key:=CStr(i)

· Set colSubCollection = Nothing

· Next i

·

· For Each colSubCollection In colMyCollection

· MsgBox colSubCollection.Item("MySixPlusVal")Next

· Interestingly, although most Visual Basic data types are merely wrappers for data types in theBase Class Library, the Collection object is a "native" VB data type that's derived fromSystem.Object and implements the ICollection, IEnumerable, and IList interfaces.This can be seen from the following code fragment:

· Dim oColl As New Collection

· Dim oType As Type, oInt As Type

·

· oType = oColl.GetType( )

· Console.WriteLine("Type: " & oType.ToString)

· Console.WriteLine("Base Type: " & oType.BaseType.ToString)

· Dim oTypes( ) As Type = oType.GetInterfaces

· For Each oInt in oTypes

· Console.WriteLine("Interface: " & oInt.ToString)Next

See Also

198

Collection.Add Method, Collection.Count Property, Collection.Item Method,

Collection.Remove Method, Hashtable Class, Queue Class, Stack Class

Collection.Add MethodClass

Microsoft.VisualBasic.Collection

Syntax

objectvariable.Add item [, key, before, after]objectvariable

Use: RequiredData Type: Collection ObjectThe name of the Collection object to which an item is to be added

item

Use: RequiredData Type: ObjectAn object of any type that specifies the member to add to the collection

key

Use: OptionalData Type: StringA unique string expression that specifies a key string that can be used, instead of a positionalindex, to access a member of the collection

before

Use: OptionalData Type: ObjectThe member to be added placed in the collection before the member identified by the before

argument (more on this in Rules at a Glance)

after

Key: OptionalData Type: Object

199

The member to be added placed in the collection after the member identified by the after

argument (more on this in Rules at a Glance)

Description

Adds an object to a collection

Rules at a Glance

· If you do not specify a before or after value, the member is appended to the end of thecollection (in index order).

· If you do not specify a key value, you cannot access this member using a key, but insteadmust access it either by using its ordinal number or by enumerating all the members of thecollection with the For EachNext construct. Thus, keys are highly recommended.

· The before or after argument can refer to an index or a key. For instance, consider thefollowing code:

· Dim c As New Collection( )

· c.Add("donna", "111")

· c.Add("steve", "222")

· 'c.Add("bill", "333", "222")

· 'c.Add("bill", "333", 2)MsgBox(c.Item(2))

Both of the commented lines of code adds the item "bill" between "donna" and "steve." Thefirst line uses the key to specify the before object, and the second line specifies the ordinalposition of the before object.

· Key values must be unique or an error (runtime error 457, "This key is already associated withan element of this collection") is generated.

· You can specify a before or after position, but not both.

Example

colComposers.Add(Item:="Ludwig von Beethoven" _Key:="Beethoven")

Programming Tips and Gotchas

· Using named parameters helps to self-document your code:

· colMyCollection.Add Item:="VB .NET Language in a Nutshell" _Key:="Title"

· If your key parameter is a value being brought in from outside your program, you must ensurethat each value is always unique. One method for doing this is illustrated in the entry for the

Collection.Item Method.

See Also

Collection Class, Collection.Count Property, Collection.Item Method, Collection.RemoveMethod

Collection.Count Property

200

Class

Microsoft.VisualBasic.Collection

Syntax

objectvariable.Count

objectvariable

Use: RequiredData Type: Collection ObjectObject variable referring to a Collection object

Description

Returns an Integer containing the number of members in the collection.

Rules at a Glance

Collections are 1-based; that is, the index of the first element of a collection is 1. In contrast, arrays are0-based; the index of the first element of an array is 0.

Example

For i = 1 To colMyCollection.CountSet colSubCollection = colMyCollection.Item(CStr(i))MsgBox colSubCollection.Item("Name")Set colSubCollection = NothingNext i

Programming Tips and Gotchas

Because collections are 1-based, you can iterate the members of a collection by using index valuesranging from 1 to the value of objectvariable.Count.

See Also

Collection Class, Collection.Add Method, Collection.Item Method, Collection.RemoveMethod

Collection.Item MethodClass

Microsoft.VisualBasic.Collection

201

Syntax

objectvariable.Item(index)

objectvariable

Use: RequiredData Type: Collection ObjectAn object variable of type Collection

index

Use: RequiredData Type: Integer or StringEither the index (the ordinal position) of the object in the collection, or the unique key namebelonging to the object

Description

Returns the member of the collection for the specified key or ordinal position.

Programming Tips and Gotchas

· When writing wrapper classes for collections, you can make your object model more readableby making the name of the property that wraps the Item method the same as the name of theobject obtained from the collection. For example, if your collection class is called Employeesand is a collection of Employee records, your object model reads much better to have anEmployee Property procedure, as follows:

· Public Property Employee(vKey as Object) As Boolean

· Get

· Employee = mcolEmployees.Item(vKey)

· End Get

· . . .End Property

Note that in the previous Property procedure, the parameter is passed as an object so that theargument can be either a string (the item's key) or an integer (the item's ordinal position).

· There is no Exists method in the Collection object, so you cannot find out in advance if aparticular key exists within the collection. However, you can create an Exists function bycalling the Item method with a given key and returning an appropriate value based on whetheran error occurred, as the following code shows:

· Public Function Exists(ByVal oKey As Object) As Boolean

· Try

· moValue = mCollection.Item(oKey)

· Exists = True

· Catch e As NullReferenceException

· Exists = False

· End TryEnd Function

202

· The Item method is the default member of the Collection object, and since it is parameterized,we do not need to include an explicit call to the Item method. The following two statements, forexample, are identical to one another:

· set objMember = objCollection.Item(6)set objMember = objCollection(6)

See Also

Collection Class, Collection.Add Method, Collection.Count Property, Collection.RemoveMethod

Collection.Remove MethodClass

Microsoft.VisualBasic.Collection

Syntax

objectvariable.Remove (index)

or:

objectvariable.Remove (key)

objectvariable

Use: RequiredData Type: Collection ObjectAn object variable of the Collection type

index

Use: RequiredData Type: IntegerThe ordinal position of the item to remove

key

Use: RequiredData Type: StringThe key of the item to remove

Description

Removes a member from a collection.

203

Example

colMyCollection.Remove ("Name")

Programming Tips and Gotchas

· Members of the collection that follow the removed member are automatically moveddownward by one ordinal position; therefore, no gaps are left in the collection.

· Because the collection is reindexed after each deletion, you should be sure not to delete amember of the collection based on a stored numeric value of index, since this value couldchange. Instead, you should either delete the member by key or retrieve the index value justbefore calling the Remove method.

· If you are deleting multiple members of a collection by numeric index value, you should deletethem backwards—from highest index value to lowest—because the collection is reindexedafter each deletion.

· If you are using a collection as the basis for a class module, or if you are using functions inyour application to wrap and enhance the limited functionality of a collection, you can include aClear method to remove all the members in your collection. The method should be written toremove the member in position 1 until no members are left, as the following codedemonstrates:

· Public Sub Clear( )

·

· Dim i As Integer

·

· For i = 1 To mcolMyCollection.Count

· mcolMyCollection.Remove(1)

· Next i

·

End Sub

· Alternately, you could do the same thing by working from the end of the collection forward, asthe following code illustrates:

· Dim intCtr As Integer

·

· For intCtr = objCollec.Count To 1 Step -1

· objCollec.Remove(intCtr)Next

· When using named arguments, providing an index value with the key:= keyword or providinga key name with the index:= keyword generates a runtime error.

See Also

Collection Class, Collection.Add Method, Collection.Count Property, Collection.Item Method

ColorDialog ClassNamespace

System.Windows.Forms

204

Createable

Yes

Description

Represents a common dialog box for selecting a color.The ColorDialog object has properties for setting the initial appearance and functionality of the colordialog box, a property for returning the color selected by the user, and a method for showing the dialogbox.

Selected ColorDialog Members

The following provides a brief description of the more important members of the ColorDialog class:

AllowFullOpen property

Returns or sets a Boolean value indicating whether the user can use the dialog box to definecustom colors. The default is True.

AnyColor property

Returns or sets a Boolean value indicating whether the dialog box displays all available colors,although in Beta 2 of VB .NET, this property seems to have no effect. The default is False.

Color property

Returns an instance of a Color structure, which contains information about the color selectedby the user. The Color structure, which is a type belonging to the System.Drawing namespace,has a number of members, among which are:

· Over 140 named color properties, from Red, Green, and Blue, to PapayaWhip,MistyRose, and MediumSeagreen. These properties return a Color structure.

· The Name property, which returns the name of the color, or its ARGB value forcustom colors. (The A component is the alpha component of the color, whichdetermines the color's opacity.)

· The R property, G property, and B property, which return a byte specifying the red,green, or blue color component of the RGB color value, respectively.

· The IsKnownColor, IsNamedColor, and IsSystemColor properties, which giveinformation about the color.

CustomColors property

Represents an array of Integers used to set or return the set of custom colors that will beshown in the ColorDialog dialog box.

FullOpen property

Represents a Boolean property that sets or retrieves the value indicating whether the dialogbox is opened with the controls used to create custom visible controls. (The default is False,but the user can always click the Custom Colors button to display the custom colors controls.)

Reset method

205

Resets the dialog box by setting all options and custom colors to their default values andsetting the selected color to black.

SolidColorOnly property

For systems displaying 256 colors or less, if this property is set to True, restricts the dialogbox to solid colors only, that is, to colors that are not composites of other colors.

VB .NET/VB 6 Differences

While the ColorDialog class is implemented in the .NET Base Class Library, VB 6 offered theCommonDialog custom control. Although the two offer similar functionality, their public interfaces arealmost completely different.

Example

The following code asks the user for a color and displays that color:

Dim cd As New ColorDialog( )Dim c As New Color( )If cd.ShowDialog( ) = DialogResult.OK ThenDebug.WriteLine(cd.Color.ToString)Debug.WriteLine(cd.Color.Name)ElseDebug.WriteLine("No color chosen")End If

Note the use of the DialogResult enumeration to check user action on the dialog box. Here is theprecise output if red is selected:

Color [Alpha=255, Red=255, Green=0, Blue=0]ffff0000

Command FunctionClass

Microsoft.VisualBasic.Interaction

Syntax

Command( )

Return Value

A String containing the command-line arguments

Description

Returns the arguments used when launching VB or an application created with VB.

206

Rules at a Glance

· For applications created with VB and compiled into an EXE, Command returns a stringcontaining everything entered after the executable filename.

· If the executable has been launched with no command-line arguments, Command returns anull string.

Example

The following example demonstrates how to parse command-line arguments to set up a series ofoptions in your executable. This example (which is bereft of all error handling) looks for a hyphen or aforward slash in the command-line arguments and assumes that the character following it is acommand-line switch. Given the command-line arguments:

-d:50 -f -g -k

the program displays the following in the Immediate window:

Got option dOption d Parameter = 50Got option fGot option gGot option k

The source code is as follows:

Private Sub ParseCommandLine( )Dim i As IntegerDim s, sChar, sParam As StringDim sPattern As String = "[-/]"For i = 1 To Len(Command)sChar = mid(Command, i, 1)If sChar = "-" or sChar = "/" Thens = Mid(Command, i + 1, 1)Select Case sCase "d"Console.WriteLine("Got option d")sParam = Mid(Command, i + 3, 2)Console.WriteLine("Option d Parameter = " & _sParam)Case "f"Console.WriteLine("Got option f")Case "g"Console.WriteLine("Got option g")Case "k"Console.WriteLine("Got option k")Case "l"Console.WriteLine("Got option l")End SelectEnd IfNext IEnd Sub

Programming Tips and Gotchas

207

· During the development phase, you can pass arguments to your program using the CommandLine Arguments textbox, which can be found on the Property Pages dialog box for the project(right-click the project name in the Solution Explorer window). In particular, the textbox isfound under Start Options in the Debugging subnode of the Configuration Properties node.

· To handle command-line arguments, you must write a routine similar to the one shown earlierto parse the string returned by Command, since the function only returns a single stringcontaining all input after the name of the executable file.

· Command-line arguments are ideal for specifying various options on unattended applications.

Const StatementSyntax

[accessmodifier] Const constantname [As type] = constantvalueaccessmodifier

Use: OptionalType: KeywordOne of the keywords Public, Private, Protected, Friend, or Protected Friend. Formore information, see Section 3.7 in Chapter 3.

constantname

Use: RequiredType: String LiteralThe name of the constant.

type

Use: OptionalType: KeywordThe data type; it can be Byte, Boolean, Char, Short, Integer, Long, Single, Double,

Decimal, Date, or String, as well as any of the data types defined in the Base ClassLibrary.

constantvalue

Use: RequiredData Type: Numeric or StringA literal, constant, or any combination of literals and constants that includes arithmetic orlogical operators, except Is.

Description

208

Associates a constant value with a name. This feature is provided to make code more readable. Thename is referred to as a symbolic constant.

Rules at a Glance

· The rules for constantname are the same for those of any variable: the name can be up to255 characters in length and can contain any alphanumeric character, although it must startwith an alphabetic character. In addition, the name can include almost any other characterexcept a period or any of the data type definition characters ($, &, %, !).

· The constantvalue expression cannot include any of the built-in functions or objects,although it can be a combination of absolute values and operators. The expression can alsoinclude previously defined constants. For example:

· Private Const CONST_ONE = 1

· Private Const CONST_TWO = 2Private Const CONST_THREE = CONST_ONE + CONST_TWO

· Scoping rules are the same as for variables. For more on scope, see Chapter 3.

· If Option Strict is on, the data type of the constant must be defined by using the As type

clause.

Example

Private Const MY_CONSTANT = 3.1417

Programming Tips and Gotchas

· Your code may be more readable if you take advantage of the fact that VB allows lengthyconstant (and variable) names. This allows you to choose these names in a more meaningfulway.

· If you are building a large application with many different modules, you may find your codeeasier to maintain if you create a single separate code module to hold your Public constants.

· If two or more constants are related, you should define them as members of an enumerationusing the Enum statement.

See Also

Enum Statement

Cos FunctionClass

System.Math

Syntax

Math.Cos(d)

d

Use: RequiredData Type: Double or numeric expression

209

An angle in radians

Return Value

A Double data type denoting the cosine of an angle

Description

Takes an angle specified in radians and returns a ratio representing the length of the side adjacent tothe angle divided by the length of the hypotenuse

Rules at a Glance

· The cosine returned by the function is between -1 and 1.

· This is a Shared member, so it can be used without creating any objects.

Example

Dim dblCosine as DoubledblCosine = Math.Cos(dblRadians)

Programming Tips and Gotchas

· To convert degrees to radians, multiply degrees by pi/180.

· To convert radians to degrees, multiply radians by 180/pi.

VB .NET/VB 6 Differences

In VB 6, Cos was an intrinsic VB function. In the .NET platform, it is a member of the Math class in theSystem namespace, and so it is not part of the VB .NET language.

See Also

Cosh Function, Sin Function, Tan Function

Cosh FunctionClass

System.Math

Syntax

Math.Cosh(value)

value

Use: RequiredData Type: Double or numeric expression

210

An angle in radians

Return Value

A Double denoting the hyperbolic cosine of the angle.

Description

Returns the hyperbolic cosine of an angle.

Rules at a Glance

This is a Shared member, so it can be used without creating any objects.

VB .NET/VB 6 Differences

The Cosh function is new to the .NET platform; it did not exist in VB 6.

See Also

Cos Function, Sinh Function, Tanh Function

CreateObject FunctionClass

Microsoft.VisualBasic.Interaction

Named Arguments

No

Syntax

objectvariable = CreateObject(progid [, servername])

objectvariable

Use: RequiredData Type: ObjectA variable to hold the reference to the instantiated object

progid

Use: RequiredData Type: String

211

The programmatic identifier (or ProgID) of the class of the object to create

servername

Use: OptionalData Type: StringThe name of the server on which the object resides

Return Value

A reference to a COM or ActiveX object.

Description

Creates an instance of an OLE Automation (ActiveX) object.Prior to calling the methods, functions, or properties of a COM or ActiveX object, you are required tocreate an instance of that object. Once an object is created, reference it in code using the objectvariable you defined.

Rules at a Glance

· If your project does not include a reference to the object, you must declare the object variabletype as Object; this allows the variable to reference any type of object.

· If an instance of the ActiveX object is already running, CreateObject may start a new instancewhen it creates an object of the required type.

· CreateObject can only be used to create instances of COM (or ActiveX) objects; it cannot beused to instantiate .NET components.

Example

The following routine defines a generic Object variable, as well as an Excel application object. It thenuses the Timer function to compare the performance of the code fragment that uses late binding toinstantiate the Excel application object with the one that uses early binding. (For a discussion of lateand early binding, see the second item under Programming Tips and Gotchas.)

Private Sub TestBinding( )Dim dblTime As DoubleDim strMsg As String' Calculate time for late bindingdblTime = Timer( )Dim objExcelLate As ObjectobjExcelLate = CreateObject("excel.application")objExcelLate = NothingstrMsg &= "Late Bound: " & Timer( ) - dblTimestrMsg &= vbCrLf' Calculate time for early bindingdblTime = Timer( )Dim objExcelEarly As Excel.ApplicationobjExcelEarly = Excel.ApplicationobjExcelEarly = Nothing

212

strMsg &= "Early Bound: " & Timer( ) - dblTimeMsgBox (strMsg, vbOKOnly, "Late and Early Binding")End Sub

Programming Tips and Gotchas

· The ProgID is defined in the system registry and usually takes the form library.class or

application.class.

· The Object data type is the most generic of Visual Basic objects. When an object variable hasbeen defined as type Object, CreateObject performs what is termed late binding. This meansthat, because the precise object type is unknown at compile time, the object cannot be boundinto your program when it is compiled. Instead, this binding occurs only at runtime, when theprogram is run on the target system and the CreateObject function is executed. This need todetermine the object type by referencing the relevant interfaces at runtime is time-consumingand results in poor performance. You can vastly improve this performance by utilizing earlybinding. Early binding necessitates adding a reference to the required object to your project.

· The servername parameter permits the specification of the name of the server on which theActiveX object is registered. This means that you could even specify different serversdepending upon prevailing circumstances, as this short example demonstrates:

· Dim sMainServer As String

· Dim sBackUpServer As String

·

· sMainServer = "NTPROD1"

· sBackUpServer = "NTPROD2"

·

· If IsOnline(sMainServer) Then

· CreateObject("Sales.Customer",sMainServer)

· Else

· CreateObject("Sales.Customer",sBackUpServer)End If

· To use a current instance of an already running ActiveX object, use the GetObject function.

· If an object is registered as a single-instance object—i.e., an out-of-process ActiveX EXE—only one instance of the object can be created. Regardless of the number of times

CreateObject is executed, you will obtain a reference to the same instance of the object.

· It is considered good programming practice (and often a necessary one) to tidy up after youhave finished using an object by setting objectvariable to Nothing. This has the effect offreeing the memory taken up by the instance of the object, and, if there are no other "live"references to the object, shutting it down. For example:

objectvariable = Nothing

See Also

GetObject Function

CShort FunctionNamed Arguments

213

No

Syntax

CShort(expression)

expression

Use: RequiredData Type: Numeric or StringThe range of expression is -32,768 to 32,767; fractions are rounded.

Return Value

expression cast as a Short

Description

Converts expression to a Short value; any fractional portion of expression is rounded.

Rules at a Glance

· expression must evaluate to a numeric value; otherwise, a type-mismatch error isgenerated.

· If the value of expression is outside the range of the Short data type, an overflow error isgenerated.

· When the fractional part of expression is exactly .5, CShort always rounds it to the nearesteven number. For example, .5 rounds to 0, and 1.5 rounds to 2.

Example

Dim iMyNumber as ShortIf IsNumeric(sMyNumber) theniMyNumber = CShort(sMyNumber)End If

Programming Tips and Gotchas

· When converting a string representation of a number to a numeric, you should use the datatype conversion functions—such as CShort—instead of Val, because the data type conversionfunctions take into account the system's regional settings. In particular, CShort recognizes thethousands separator if it's present in expression, whereas Val does not. For example, if

expression is 1,234, CShort successfully converts it to the integer value 1234, while Val

converts it to 1.

· Use IsNumeric to test whether expression evaluates to a number before performing theconversion.

· CShort differs from the Fix and Int functions, which truncate, rather than round, the fractionalpart of a number. Also, Fix and Int always return the same type value as was passed in.

· Like most of the conversion functions, CShort is not actually a function in theMicrosoft.VisualBasic namespace. Instead, it is similar to a Visual C++ macro; the compilertranslates the function call into inline code.

VB .NET/VB 6 Differences

214

The CShort function is new to VB .NET. However, it corresponds directly to the VB 6 CInt function,since both return 16-bit integers.

See Also

CInt Function, CLng Function

CSng FunctionNamed Arguments

No

Syntax

CSng(expression)

expression

Use: RequiredData Type: Numeric or StringThe range of expression is -3.402823E38 to -1.401298E-45 for negative values, and1.401298E-45 to 3.402823E38 for positive values.

Return Value

expression cast as a Single data type

Description

Returns a single-precision number

Rules at a Glance

· expression must evaluate to a numeric value; otherwise, a type-mismatch error isgenerated.

· If the value of expression is outside the range of the Double data type, an overflow error isgenerated.

Example

Dim sngMyNumber As SingleIf IsNumeric(sMyNumber) ThensngMyNumber = CSng(sMyNumber)End If

Programming Tips and Gotchas

· You can use IsNumeric to test an expression before passing it to CSng.

215

· When converting a string representation of a number to a numeric, you should use the datatype conversion functions—such as CSng—instead of Val, because the data type conversionfunctions take into account the computer's regional settings. The thousands separator is themost important of these regional settings. For example, if the value of expression is thestring 1,234.987, CSng converts it to 1234.987, while Val incorrectly converts it to 1.

· Like most of the conversion functions, CSng is not actually a function in theMicrosoft.VisualBasic namespace. Instead, it is similar to a Visual C++ macro; the compilertranslates the function call into inline code.

See Also

CDbl Function

CStr FunctionNamed Arguments

No

Syntax

CStr(expression)

expression

Use: RequiredData Type: AnyAny numeric, date, string, or Boolean expression

Return Value

expression converted to a string.

Description

Returns a string representation of expression.

Rules at a Glance

If expression is Boolean, the function returns one of the strings "True" or "False". For anexpression that can be interpreted as a date, the return value is a string representation of that date, inthe short date format of the host computer. For a numeric expression, the return is a stringrepresenting the number.

Example

Dim sMyString as StringsMyString = CStr(100)

Programming Tips and Gotchas

216

· The string representation of Boolean values is either "True" or "False", as opposed to theirunderlying values of 0 and -1.

· Uninitialized numeric data types passed to CStr return "0."

· An uninitialized date variable passed to CStr returns "12:00:00AM."

· Like most of the conversion functions, CStr is not actually a function in theMicrosoft.VisualBasic namespace. Instead, it is similar to a Visual C++ macro; the compilertranslates the function call into inline code.

See Also

Str Function

CType FunctionNamed Arguments

No

Syntax

CType(expression, typename)

expression

Use: RequiredData Type: AnyThe data item to be converted

typename

Use: RequiredType: KeywordThe data type, object type, structure, or interface to which expression is to be converted

Return Value

expression cast as a typename interface, object, structure, or data type.

Description

Converts any expression that can be interpreted as an object to Object.

Rules at a Glance

· expression can be any data, object, structure, or interface type.

217

· typename can be any data type (such as Boolean, Byte, Decimal, Long, Short, String,etc.), structure type, object type, or interface that can be used with the As clause in a Dim

statement.

· If the function fails, or if the converted value of expression is outside the range allowed by

typename, an InvalidCastException exception occurs.

Example

Option Strict OnImports Microsoft.VisualBasicImports SystemInterface IEmployeeProperty Name( ) As StringProperty Salary( ) As DecimalEnd InterfacePublic Class CSalariedImplements IEmployeeDim sName As StringDim decSalary AS DEcimalPublic Property Name( ) As String Implements IEmployee.NameGetName = sNameEnd GetSetsName = ValueEnd SetEnd PropertyPublic Property Salary( ) As Decimal Implements IEmployee.SalaryGetSalary = decSalaryEnd GetSetdecSalary = ValueEnd SetEnd PropertyEnd ClassModule modMainPublic Sub Main( )Dim oSal As New CSalariedDim oSal2 As CSalariedDim oEmp As IEmployeeoSal.Name = "John Doe"oSal.Salary = 30000console.writeline(oSal.Name)oEmp = CType(oSal, IEmployee)console.writeline(oEmp.Name)oSal2 = CType(oEmp, CSalaried)console.writeline(oSal2.name)

218

End SubEnd Module

Programming Tips and Gotchas

· CType can perform the same conversions as the individual conversion functions and raises aruntime error if it is asked to perform a conversion that an individual conversion functioncannot perform. For example, in the function call:

bVal = CType(Var1, Boolean)

Var1 can be any numeric value, any numeric string, or a string whose value is either "True"

or "False".

· Like most of the conversion functions, CType is not actually a function in theMicrosoft.VisualBasic namespace. Instead, it is similar to a Visual C++ macro; the compilertranslates the function call into inline code.

· In part, CType is a "convenience function" that provides the functionality of the entire set ofconversion functions in a single function. Its real significance, however, comes when you wantto convert a derived object to the type of its base class, or when you want to convert an objectto the type of its virtual base class (that is, its interface).

· Upcasting a derived object to its parent object type can be done implicitly. However,downcasting back from the base class type to the derived object type cannot be done implicitlyif Option Strict is On. Instead, CType can be used to perform this conversion. This isillustrated in the example.

VB .NET/VB 6 Differences

The CType function is new to VB .NET.

See Also

CBool Function, CByte Function, CChar Function, CDate Function, CDbl Function, CDecFunction, CInt Function, CLng Function, CObj Function, CShort Function, CSng Function,

CStr Function

CurDir FunctionClass

Microsoft.VisualBasic.FileSystem

Syntax

CurDir[(drive)]

drive

Use: OptionalData Type: String or Char

219

The name of the drive

Return Value

A String containing the current path.

Description

Returns the current directory of a particular drive or the default drive.

Rules at a Glance

· If no drive is specified or if drive is a zero-length string (""), CurDir returns the path for thecurrent drive.

· drive can be the single-letter drive name with or without a colon (i.e., both "C" and "C:" arevalid values for drive).

· If drive is invalid, the function will generate runtime error 68, "Device unavailable."

· Because CurDir can only accept a single-character string, you cannot use network drivenames, share names, or UNC drive names.

Example

Sub TestCurDir( )MsgBox CurDir("C")End Sub

See Also

ChDir Procedure, ChDrive Procedure, MkDir Procedure, RmDir Procedure

DateAdd FunctionClass

Microsoft.VisualBasic.DateAndTime

Syntax

DateAdd(interval, number, datevalue)

interval

Use: RequiredData Type: String or DateInterval enumA String expression (see the first item in Rules at a Glance) or a member of the

DateInterval enumeration (see the second item in Rules at a Glance) that specifies theinterval of time to add.

number

220

Use: RequiredData Type: DoubleAn expression denoting the number of time intervals you want to add (it can be positive ornegative

datevalue

Use: RequiredData Type: Date, or an expression capable of conversion to a dateDate representing the starting date to which the interval is to be added

Return Value

A past or future Date that reflects the result of the addition

Description

Returns a Date representing the result of adding (or subtracting, if number is negative) a givennumber of time periods to or from a given date. For instance, you can calculate the date 178 monthsbefore today's date, or the date and time 12,789 minutes from now.

Rules at a Glance

· interval can be one of the following literal strings:

String Description

yyyy Year

q Quarter

m Month

y Day of year

d Day

w Weekday

ww Week

h Hour

n Minute

s Second

· interval can also be a member of the DateInterval enum:

· Enum DateInterval

· Day

· DayOfYear

· Hour

· Minute

· Month

· Quarter

· Second

· Week

· Weekday

221

· WeekOfYearEnd Enum

· If number is positive, the result will be in the future; if number is negative, the result will be inthe past. (The meaning of "future" and "past" here is relative to datevalue).

· The DateAdd function has a built-in calendar algorithm to prevent it from returning an invaliddate. For example, you can add 10 minutes to 31 December 1999 23:55, and DateAdd

automatically recalculates all elements of the date to return a valid date, in this case 1 January2000 00:05. This includes leap years: the calendar algorithm takes the presence of 29February into account for leap years.

Example

DateAdd(DateInterval.Day, 120, #3/3/2001#) ' Returns 7/1/2001

Programming Tips and Gotchas

· You can check that a date is valid using the IsDate function prior to passing it as a parameterto the function.

· To add a number of days to datevalue, use either the day of the year ("y" or

DateInterval.DayOfYear), the day ("d" or DateInterval.Day), or the weekday ("w" or

DateInterval.Weekday).

· DateAdd generates an error if the result does not lie in the range of dates of the Date datatype.

· If number contains a fractional value, it is rounded to the nearest whole number before beingused in the calculation.

· You can also use the members of the DateTime structure of the BCL to manipulate datesand times.

VB .NET/VB 6 Differences

VB 6 lacks the DateInterval enumeration and therefore only accepts a string as the interval

argument.

See Also

DateDiff Function

DateDiff FunctionClass

Microsoft.VisualBasic.DateAndTime

Syntax

DateDiff(interval, date1, date2[, dayofweek[, weekofyear]])

interval

Use: RequiredData Type: String or DateInterval enum

222

A String expression (see the first item in Rules at a Glance) or a member of the

DateInterval enumeration (see the second item in Rules at a Glance) that specifies theunits of time used to express the difference between date1 and date2date1, date2

Use: RequiredData Type: Date or a literal dateThe starting and ending dates, whose difference is computed as date2-date1dayofweek

Use: OptionalData Type: FirstDayOfWeek enumA member of the FirstDayOfWeek enum

weekofyear

Use: OptionalData Type: FirstWeekOfYear enumA member of the FirstWeekOfYear enum

Return Value

A Long specifying the number of time intervals between the two dates

Description

Calculates the number of time intervals between two dates. For example, you can use the function todetermine how many days there are between 1 January 1980 and 31 May 1998.

Rules at a Glance

· interval can be one of the following literal strings:

String Description

yyyy Year

q Quarter

m Month

y Day of year

d Day

w Weekday

ww Week

h Hour

n Minute

223

s Second

· interval can also be a member of the DateInterval enum:

· Enum DateInterval

· Day

· DayOfYear

· Hour

· Minute

· Month

· Quarter

· Second

· Week

· Weekday

· WeekOfYearEnd Enum

· To calculate the number of days between date1 and date2, you can use either of the

DateInterval constants, DayOfYear or Day, or the string literals "y" or "d".

· When interval is Weekday or "w", DateDiff returns the number of weeks between the twodates. If date1 falls on a Monday, DateDiff counts the number of Mondays until date2. Itcounts date2, but not date1. If interval is Week or "ww", however, DateDiff returns thenumber of calendar weeks between the two dates. It counts the number of Sundays between

date1 and date2. DateDiff counts date2 if it falls on a Sunday, but it doesn't count date1,even if it does fall on a Sunday.

· The DayOfWeek argument affects calculations that use the Week or "w" and Weekday or

"ww" interval settings only.

Example

DateDiff(DateInterval.Day, #1/1/1945#, #3/3/2001#, _FirstDayOfWeek.System, FirstWeekOfYear.System)

Programming Tips and Gotchas

· When working with dates, always check that a date is valid using the IsDate function prior topassing it as a function parameter.

· If date1 or date2 is enclosed in double quotation marks (" ") and you omit the year, thecurrent year is inserted in your code each time the date1 or date2 expression is evaluated.This makes it possible to write code that can be used in different years.

· When comparing December 31 to January 1 of the immediately succeeding year, DateDiff

with interval set equal to Year, or "yyyy" returns 1 even though only a day has elapsed.

· DateDiff considers the four quarters of the year to be January 1-March 31, April 1-June 30,July 1-September 30, and October 1-December 31. Consequently, when determining thenumber of quarters between March 31 and April 1 of the same year, for example, DateDiff

returns 1, even though the latter date is only one day after the former.

· If interval is Month or "m", DateDiff simply counts the difference in the months in whichthe respective dates fall. For example, when determining the number of months betweenJanuary 31 and February 1 of the same year, DateDiff returns 1, even though the latter date isonly one day after the former.

· In calculating the number of hours, minutes, or seconds between two dates, if an explicit timeis not specified, DateDiff provides a default value of midnight (00:00:00).

VB .NET/VB 6 Differences

224

VB 6 lacks the DateInterval enumeration and therefore only accepts a string as the interval

argument.

See Also

DateAdd Function

DatePart FunctionClass

Microsoft.VisualBasic.DateAndTime

Syntax

DatePart(interval, datevalue[,firstdayofweekvalue[, _firstweekofyearvalue]])

interval

Use: RequiredData Type: String or a member of the DateInterval enumA String literal (see the second item in Rules at a Glance) or a constant of the

DateInterval enum (see the third item in Rules at a Glance) that defines the part of thedate/time to extract from datevaluedatevalue

Use: RequiredData Type: Date, literal date, or an expression capable of conversion to a dateThe Date value to evaluate

firstdayofweekvalue

Use: OptionalData Type: FirstDayOfWeek enumA member of the FirstDayOfWeek enum

firstweekofyearvalue

Use: OptionalData Type: FirstWeekOfYear enumA member of the FirstWeekOfYear enum

225

Return Value

An Integer containing the specified part

Description

Extracts an individual component of the date or time (like the month or the second) from a date/timevalue

Rules at a Glance

· The DatePart function returns an Integer containing the specified portion of the given date.

DatePart is a single function encapsulating the individual Year, Month, Day, Hour, Minute, and

Second functions.

· interval can be one of the following literal strings:

String Description

yyyy Year

q Quarter

m Month

y Day of year

d Day

w Weekday

ww Week

h Hour

n Minute

s Second

· interval can also be a member of the DateInterval enum:

· Enum DateInterval

· Day

· DayOfYear

· Hour

· Minute

· Month

· Quarter

· Second

· Week

· Weekday

· WeekOfYearEnd Enum

· The firstdayofweekvalue argument can be any of the following members of the

FirstDayOfWeek enumeration:

· Enum FirstDayOfWeek

· System 'uses first day of week setting on local system

· Sunday

· Monday

· Tuesday

· Wednesday

· Thursday

226

· Friday

· SaturdayEnd Enum

· The firstdayofweekvalue argument affects only calculations that use either the Week (or

"w") or Weekday (or "ww") interval values.

· The firstweekofyearvalue argument can be any of the following members of the

FirstWeekOfYear enumeration:

FirstWeekOfYearconstant Value Description

System 0 Uses the local system setting

Jan1 1 Starts with the week in which January 1 occurs (the default value)

FirstFourDays 2 Starts with the first week that has at least four days in the newyear

FirstFullWeek 3 Starts with the first full week of the year

Example

MsgBox("Current hour: " & DatePart(DateInterval.Hour, Now))

Programming Tips and Gotchas

· When working with dates, always check that a date is valid using the IsDate function prior topassing it as a function parameter.

· If you attempt to extract the hours, minutes, or seconds, but datevalue does not contain thenecessary time element, the function assumes a time of midnight (0:00:00).

· If you specify datevalue within quotation marks (" ") and omit the year, the year is assumedto be the current year taken from the computer's date. For example:

Console.WriteLine(DatePart(DateInterval.Year, cDate("01/03")))

VB .NET/VB 6 Differences

· VB 6 lacks the DateInterval enumeration and therefore only accepts a string as the

interval argument.

· VB 6 supports a number of constants beginning with vb as values for the

firstdayofweekvalue and firstweekofyearvalue arguments. While these are stillsupported in VB .NET, VB .NET has also added the FirstDayOfWeek and

FirstWeekOfYear enumerations.

See Also

DateSerial Function, DateString Property, DateValue Function

DateSerial FunctionClass

Microsoft.VisualBasic.DateAndTime

227

Syntax

DateSerial(year, month, day)

year

Use: RequiredData Type: IntegerNumber between 100 and 9999, inclusive, or a numeric expression

month

Use: RequiredData Type: IntegerAny numeric expression to express the month between 1 and 12

day

Use: RequiredData Type: IntegerAny numeric expression to express the day between 1 and 31

Return Value

A Date representing the date specified by the arguments

Description

Returns a Date whose value is specified by the three date components (year, month, and day).For the function to succeed, all three components must be present, and all must be numeric values.The value returned by the function takes the short date format defined by the Regional Settings appletin the Control Panel of the client machine.

Rules at a Glance

· If the value of a particular element exceeds its normal limits, DateSerial adjusts the dateaccordingly. For example, if you tried DateSerial(96,2,31)—February 31, 1996—

DateSerial returns March 2, 1996.

· You can specify expressions or formulas that evaluate to individual date components asparameters to DateSerial. For example, DateSerial(98,10+9,23) returns 23 March 1999.This makes it easier to use DateSerial to form dates whose individual elements are unknownat design time or that are created on the fly as a result of user input.

Example

Dim iYear As Integer = 1987Dim iMonth As Integer = 3 + 11Dim iday As Integer = 16MsgBox(DateSerial(iYear, iMonth, iday))

228

Programming Tips and Gotchas

· If any of the parameters exceed the range of the Integer data type (-32,768 to 32,767), anerror (runtime error 6, "Overflow") is generated.

· DateSerial handles two-digit years in the same way as other Visual Basic date functions. Ayear argument between 0 and 29 is taken to be in the 21st century (2000 to 2029); yeararguments between 30 and 99 are taken to be in the 20th century (1930 to 1999). Of course,the safest way to specify a year is to use the full four digits.

See Also

DatePart Function, DateString Property, DateValue Function

DateString PropertyClass

Microsoft.VisualBasic.DateAndTime

Syntax

DateString( )

Return Value

A String representing the current system date

Description

Returns or sets a string representing the current system date in the format "mm-dd-yyyy"

Rules at a Glance

The allowed formats for setting the date are "m-d-yyyy," "m-d-y," "m/d/yyyy," and "m/d/y."

Programming Tips and Gotchas

· To get or set the current system time as a String, use the TimeString property.

· To access the current system date as a Date, use the Today property.

VB .NET/VB 6 Differences

The DateString property is new to VB .NET. It is a replacement for the VB 6 Date statement, whichsets the system date, and the Date and Date$ functions, which retrieve the system date.

See Also

Now Property, TimeString Property, Today Property

229

DateValue FunctionClass

Microsoft.VisualBasic.DateAndTime

Syntax

DateValue(stringdate)

stringdate

Use: RequiredData Type: StringA string containing any of the date formats recognized by IsDate

Return Value

A Date that represents the date specified by the stringdate argument

Description

Returns a Date containing the date represented by stringdate.The date value is formatted according to the short date setting defined by the Regional Settings appletin the Control Panel. DateValue can successfully recognize a stringdate in any of the date formatsrecognized by IsDate. DateValue does not return time values in a date/time string; they are simplydropped. However, if stringdate includes a valid date value but an invalid time value, a runtimeerror results.

Rules at a Glance

· The order of the day, month, and year within stringdate must be the same as the sequencedefined by the computer's regional settings.

· Only those date separators recognized by IsDate can be used.

· If you don't specify a year in your date expression, DateValue uses the current year from thecomputer's system date.

Example

Dim sDateExpression As StringsDateExpression = 10 & "/" & "March" & "/" & 1998If IsDate(sDateExpression) ThenConsole.WriteLine(DateValue(sDateExpression))ElseConsole.WriteLine("invalid date")End If

Programming Tips and Gotchas

230

· When working with dates, always check that a date is valid using the IsDate function prior topassing it as a function argument.

· If stringdate includes time information as well as date information, the time information isignored; however, if only time information is passed to DateValue, an error is generated.

· DateValue handles two-digit years in the following manner: Year arguments between 0 and29 are taken to be in the 21st century (2000 to 2029), while year arguments between 30 and99 are taken to be in the 20th century (1930 to 1999). Of course, the safest way to specify ayear is to use the full four digits.

· On Windows NT/2000 systems, the date formats are held as string values in the followingregistry keys:

Date Separator

HKEY_CURRENT_USER\Control Panel\International, sDate value entry

Long Date

HKEY_CURRENT_USER\Control Panel\International, sLongDate value entry

Short Date

HKEY_CURRENT_USER\Control Panel\International, sShortDate value entry

· The more common approach to date conversion is to use the CDate function. Microsoft alsorecommends using the C conversion functions due to their enhanced capabilities and theirlocale awareness.

See Also

DatePart Function, DateSerial Function, DateString Property

Day FunctionClass

Microsoft.VisualBasic.DateAndTime

Syntax

Day(datevalue)

datevalue

Use: RequiredData Type: Date or literal date

Return Value

An Integer from 1 to 31, representing the day of the month

Description

231

Returns an Integer ranging from 1 to 31, representing the day of the month of datevalue

Rules at a Glance

The range of datevalue is 1/1/1 to 12/31/9999.

Programming Tips and Gotchas

· When working with dates, always check that a date is valid using the IsDate function prior topassing it as a function parameter.

· With Option Strict On, you must first convert datevalue to a Date data type beforepassing it to the Day function. You can use the CDate function for thi s purpose.

· If the day portion of datevalue is outside of its valid range, the function regenerates runtimeerror 13, "Type mismatch." This is also true if the day and month portion of datevalue is2/29 for a non-leap year.

· To return the day of the week, use the WeekDay function.

See Also

DatePart Function, WeekdayName Function

DDB FunctionClass

Microsoft.VisualBasic.Financial

Syntax

DDB(cost, salvage, life, period[, factor])

cost

Use: RequiredData Type: DoubleThe initial cost of the asset.

salvage

Use: RequiredData Type: DoubleThe value of the asset at the end of life.

life

Use: Required

232

Data Type: DoubleLength of life of the asset.

period

Use: RequiredData Type: DoublePeriod for which the depreciation is to be calculated.

factor

Use: OptionalData Type: VariantThe rate at which the asset balance declines. If omitted, 2 (double-declining method) isassumed. However, the documentation doesn't mention what other values are supported orwhat they mean.

Return Value

Double representing the depreciation of an asset

Description

Returns a Double representing the depreciation of an asset for a specific time period. This is doneusing the double-declining balance method or another method that you specify using the factor

argument.The double-declining balance calculates depreciation at a differential rate, which varies inversely withthe age of the asset. Depreciation is highest at the beginning of an asset's life and declines over time.

Rules at a Glance

· life and period must be specified in the same time units. In other words, both must beexpressed in units of months, or both must be years.

· All arguments must be positive numbers.

Example

Dim dblInitialCost As Double = 2000Dim dblSalvageValue As Double = 50Dim dblUsefulLife As Double = 12Dim dblTotDepreciation As Double = 0Dim dblPeriod, dblThisPeriodDepr As DoubleFor dblPeriod = 1 To 12dblThisPeriodDepr = DDB(dblInitialCost, _dblSalvageValue, dblUsefulLife, dblPeriod)dblTotDepreciation = dblTotDepreciation + _dblThisPeriodDeprConsole.WriteLine("Month " & dblPeriod & ": " & _dblThisPeriodDepr)

233

Next dblPeriodConsole.WriteLine("TOTAL: " & dblTotDepreciation)

Programming Tips and Gotchas

· The double-declining balance depreciation method calculates depreciation at a higher rate inthe initial period and a decreasing rate in subsequent periods.

· The DDB function uses the following formula to calculate depreciation for a given period:

Depreciation / period = ((cost - salvage) * factor) / life

Debug ClassNamespace

System.Diagnostics

Createable

No

Description

The Debug object is used to send messages to the Output window (formerly called the Immediatewindow). The Debug object can also send output to other targets, such as text files, referred to as

listeners. See the Debug.Listeners Property entry. The Debug class also allows you to checkprogram logic with assertions.Because the Debug class' members are shared, you do not need to instantiate the Debug objectbefore accessing its members. The following code fragment, for instance, illustrates a call to the

Debug object's WriteLine method:

Debug.WriteLine(intCount & " iteration through the loop")

Debug class members marked with an plus sign (+) are discussed in detail in their own entries.

Public Shared Properties

AutoFlush +IndentLevel +IndentSize +Listeners +

Public Shared Methods

Assert +Close +FailFlush +Indent +Unindent +Write +WriteIf +

234

WriteLine +WriteLineIf +

VB .NET/VB 6 Differences

The VB 6 Debug object has only two methods, Assert and Print. The VB .NET Assert method is similarto the VB 6 method, except that the latter displays a message if an expression is False, while theformer suspends program execution. In VB .NET, the VB 6 Print method is gone, replaced by theWrite, WriteIf, WriteLine, and WriteLineIf methods.

See Also

Debug.Assert Method, Debug.Write Method, Debug.WriteLine Method

Debug.Assert MethodClass

System.Diagnostics.Debug

Syntax

Debug.Assert(booleanexpression[[, string1], string2])

booleanexpression

Use: RequiredData Type: BooleanExpression that evaluates to a Boolean value.

string1

Use: RequiredData Type: StringString to output if booleanexpression is False.

string2

Use: RequiredData Type: StringDetailed string to output. If booleanexpression is False, string2 is output to Outputwindow.

Return Value

None

235

Description

Outputs messages to the Output window if the condition is False

Rules at a Glance

booleanexpression must evaluate to a Boolean value.

Programming Tips and Gotchas

· Assert is typically used when debugging to test an expression that should evaluate to True.

· Debug.Assert executes only when an application is run in the design-time environment; thestatement has no effect in a compiled application. This means that Debug.Assert will neverproduce a runtime error if the call to it is inappropriate, nor will it suspend program executionoutside of the VB IDE. Because of this, you do not have to remove Debug.Assert statementsfrom finished code or separate them with conditional #IfThen statements.

Debug.AutoFlush PropertyClass

System.Diagnostics.Debug

Syntax

Debug.AutoFlush

Return Value

Boolean

Description

Sets or returns a Boolean value indicating whether each Write should be automatically followed by aFlush operation. By default, its value is False.

See Also

Debug.Flush Method

Debug.Close MethodClass

System.Diagnostics.Debug

236

Syntax

Debug.Close( )

Return Value

None

Description

Flushes the output buffer and closes the listeners (except for the default Output window)

Debug.Flush MethodClass

System.Diagnostics.Debug

Syntax

Debug.Flush( )

Return Value

None

Description

Flushes the output buffer, which causes all pending data to be written to the listeners

Debug.Indent MethodClass

System.Diagnostics.Debug

Syntax

Debug.Indent( )

Description

Increases the current IndentLevel by 1. The property is useful for improving the readability of outputsent to the Output window.

See Also

237

Debug.IndentLevel Property, Debug.IndentSize Property, Debug.Unindent Method

Debug.IndentLevel PropertyClass

System.Diagnostics.Debug

Syntax

Debug.IndentLevel( )

Return Value

An Integer specifying the indent level. The default is 0.

Description

Sets or retrieves the current indent level for the Debug object. The property is useful for improving thereadability of output sent to the Output window.

See Also

Debug.IndentSize Property, Debug.Unindent Method

Debug.IndentSize PropertyClass

System.Diagnostics.Debug

Syntax

Debug.IndentSize

Return Value

An Integer specifying the number of spaces per indent level. The default is 4.

Description

Sets or retrieves the current indent-size setting, which is the number of spaces per indent level. Theproperty is useful for improving the readability of output sent to the Output window.

See Also

238

Debug.IndentLevel Property, Debug.Unindent Method

Debug.Listeners PropertyClass

System.Diagnostics.Debug

Syntax

Debug.Listeners

Description

Retrieves the TraceListenerCollection collection of all TraceListener objects that monitor the debugoutput.

Example

The following code adds a text file to the listeners collection. As a result, all Debug.Write methodswill not only send the output to the Output window (the default listener) but also to the text file:

' Define a new TextWriterTraceListenerDim trace As New TextWriterTraceListener( )' Define a new FileStream objectDim fs As FileStream = New FileStream("c:\log.txt", FileMode.Append, _FileAccess.Write)' Set the Writer property to a new StreamWriter for this FileStreamtrace.Writer = New StreamWriter(fs)' Add listenerDebug.Listeners.Add(trace)' OutputDebug.WriteLine("xxxxx")Debug.WriteLine("yyyyy")' Close upDebug.Close( )fs.Close( )' Remove listenerDebug.Listeners.Remove(trace)' This goes only to Output windowDebug.WriteLine("zzzzz")

Debug.Unindent Method

239

Class

System.Diagnostics.Debug

Syntax

Debug.Unindent( )

Description

Decreases the current IndentLevel by 1. The property is useful for improving the readability of outputsent to the Output window.

See Also

Debug.Indent Method, Debug.IndentLevel Property

Debug.Write MethodClass

System.Diagnostics.Debug

Syntax

Debug.Write(Output[, Category])

Output

Use: RequiredData Type: String or ObjectThe string to be sent to the Output window, or the object whose name is to be sent to theOutput window

Category

Use: OptionalData Type: StringA category name used to group output messages

Description

Prints text in the Output window in the design-time environment

Rules at a Glance

· If Output is a string, the string is printed to the Output window.

240

· If Output is a nonstring object, the ToString property of the Object object is invoked. Thisjust outputs a string version of the name of the object.

· Supplying a Category argument is useful when you want to organize the output from several

Debug.Write statements by category. Output from the method then takes the form:

Category: Output

if Output is a string, and:

Category: Output.ToString

if Output is a nonstring object.

Programming Tips and Gotchas

In Visual Basic applications, Debug.Write executes only when an application is run in the design-timeenvironment; the statement has no effect in a compiled application.

See Also

Debug.WriteIf Method, Debug.WriteLine Method, Debug.WriteLineIf Method

Debug.WriteIf MethodClass

System.Diagnostics.Debug

Syntax

Debug.WriteIf(condition, message[, Category])

or:

Debug.WriteIf(condition, value[, Category])

condition

Use: RequiredData Type: BooleanCondition required for output to proceed

message

Use: RequiredData Type: StringThe string to be sent to the Output window, or the object whose name is to be sent to theOutput window

241

value

Use: RequiredData Type: AnyAn object whose name is to be sent to the Output window

Category

Use: OptionalData Type: StringA category name used to group output messages

Description

Prints text in the Output window in the design-time environment, provided that condition is True

Rules at a Glance

This method behaves identically to Debug.Write, with the exception that nothing is output unless

condition is True.

See Also

Debug.Write Method, Debug.WriteLine Method, Debug.WriteLineIf Method

Debug.WriteLine MethodClass

System.Diagnostics.Debug

Syntax

Debug.WriteLine(Output[, Category])

Output

Use: RequiredData Type: String or ObjectThe string to be sent to the Output window, or the object whose name is to be sent to theOutput window

Category

Use: Optional

242

Data Type: StringA category name used to group output messages

Description

Prints text, followed by a newline command, in the Output window in the design-time environment

Rules at a Glance

This method is identical to Debug.Write except that a newline command is sent to the Output windowafter any text is written.

See Also

Debug.Write Method, Debug.WriteIf Method, Debug.WriteLineIf Method

Debug.WriteLineIf MethodClass

System.Diagnostics.Debug

Syntax

Debug.Write(booleanexpression, Output[, Category])

booleanexpression

Use: RequiredData Type: BooleanCondition required for output to be produced

Output

Use: RequiredData Type: String or ObjectThe string to be sent to the Output window, or the object whose name is to be sent to theOutput window

Category

Use: OptionalData Type: StringA category name used to group output messages

243

Description

Prints text followed by a newline character in the Output window in the design-time environment,provided that booleanexpression is True

Rules at a Glance

This method behaves identically to Debug.WriteLine, except that nothing is output unless

booleanexpression is True.

See Also

Debug.Write Method, Debug.WriteIf Method, Debug.WriteLine Method

Declare StatementSyntax

Syntax for subroutines:

[accessmodifier] Declare [Ansi|Unicode|Auto] Sub name Lib "libname" _[Alias "aliasname"] [([arglist])]

Syntax for functions:

[accessmodifier] Declare [Ansi|Unicode|Auto] Function name _Lib "libname" [Alias "aliasname"] [([arglist])] [As type]

accessmodifier

Use: OptionalType: Keyword

accessmodifier can be any one of the following: Public, Private, Protected, Friend,or Protected Friend. The following table describes the effects of the various accessmodifiers. Note that Direct Access refers to accessing the member without any qualification,as in:

classvariable = 100

and Class/Object Access refers to accessing the member through qualification, either with theclass name or the name of an object of that class.

Direct Access scope Class/Object Access scope

Private Declaring class Declaring classProtected All derived classes Declaring classFriend Derived in-project classes Declaring projectProtected Friend All derived classes Declaring projectPublic All derived classes All projects

244

For more information, see Section 3.7 in Chapter 3.

Ansi

Use: OptionalType: KeywordConverts all strings to ANSI values.

Unicode

Use: OptionalType: KeywordConverts all strings to Unicode values.

Auto

Use: OptionalType: KeywordConverts the strings according to .NET rules based on the name of the method (or the aliasname, if specified). If no modifier is specified, Auto is the default.

name

Use: RequiredType: String literalAny valid procedure name. Note that DLL entry points are case sensitive. If the aliasname

argument is used, name represents the name by which the function or procedure is referencedin your code, while aliasname represents the name of the routine as found in the DLL.

Lib

Use: RequiredType: KeywordIndicates that a DLL or code resource contains the procedure being declared.

libname

Use: RequiredType: String literalName of the DLL or code resource that contains the declared procedure.

Alias

245

Use: OptionalType: KeywordIndicates that the procedure being called has another name in the DLL. This is useful whenthe external procedure name is the same as a keyword. You can also use Alias when a DLLprocedure has the same name as a public variable, constant, or any other procedure in thesame scope. Alias is also useful if any characters in the DLL procedure name aren't allowedby VB .NET naming conventions.

aliasname

Use: OptionalType: String literalName of the procedure in the DLL or code resource. If the first character is not a number sign(#), aliasname is the name of the procedure's entry point in the DLL. If # is the first character,all characters that follow must indicate the ordinal number of the procedure's entry point.

arglist

Use: OptionalList of variables representing arguments that are passed to the procedure when it is called.

type

Use OptionalType: KeywordData type of the value returned by a Function procedure; may be Byte, Boolean, Char,

Short, Integer, Long, Single, Double, Decimal, Date, String, Object, or any userdefinedtype. Arrays of any type cannot be returned, but an Object containing an array can.

Description

Used at module level to declare references to external procedures in a dynamic-link library (DLL)

Rules at a Glance

· arglist is optional and has the following syntax:

[ByVal | ByRef] varname[( )] [As type]

ByVal

Use: OptionalType: KeywordThe argument is passed by value; that is, a local copy of the variable is assigned the value ofthe argument. ByVal is the default method of passing arguments.

246

ByRef

Use: OptionalType: KeywordThe argument is passed by reference; that is, the local variable is simply a reference to theargument being passed. All changes made to the local variable are reflected in the callingargument.

varname

Use: RequiredType: String literalThe name of the local variable containing either the reference or value of the argument.

type

Use: OptionalType: KeywordThe data type of the argument. Can be Byte, Boolean, Char, Short, Integer, Long, Single,Double, Decimal, Date, String, Object, or any user-defined type, object type, or data typedefined in the BCL.

· The number and type of arguments included in arglist are checked each time theprocedure is called.

· The data type you use in the As clause following arglist must match that returned by thefunction.

Example

The following example retrieves the handle of a window from its title bar caption. This is done usingthe FindWindow API function.

Declare Function FindWindow Lib "user32" Alias "FindWindowA" ( _ByVal lpClassName As String, _ByVal lpWindowName As String _) As IntegerPrivate Sub GetWindowHandle( )MsgBox(FindWindow(vbNullString, "Document - WordPad"))End Sub

Programming Tips and Gotchas

· Using an Alias is useful when the name of an external procedure conflicts with a VisualBasic keyword or with the name of a procedure within your project, or when the name of theprocedure in the code library is not allowed by Visual Basic naming conventions. In addition,

aliasname is frequently used with functions in the Win32 API that have string parameters,where the "official" documented name of the function is used in code to call either of two "real"functions—one an ANSI and the other a Unicode version. For example:

· Declare Function ExpandEnvironmentStrings _

247

· Lib "kernel32" Alias "ExpandEnvironmentStringsA" _

· (ByVal lpSrc As String, ByVal lpDst As String, _ByVal nSize As Long) As Long

defines the documented Win32 function ExpandEnvironmentStrings to a VB application.However, although calls to the function take the form:

lngBytes = ExpandEnvironmentStrings(strOriginal, _strCopy, len(strCopy))

the actual name of the function as it exists in Kernel32.dll is ExpandEnvironmentStringsA.(Windows. API functions ending in "A" are the ANSI string versions, and those ending in "W"(for Wide) are the Unicode string versions.)

· You can use the # symbol at the beginning of aliasname to denote that aliasname is in factthe ordinal number of a procedure within the DLL or code library. In this case, all charactersfollowing the # sign and composing the aliasname argument must be numeric. For example:

· Declare Function GetForegroundWindow Lib "user32" _Alias "#237" ( ) As Long

· Remember that DLL entry points are case sensitive. In other words, either name or

aliasname (if it is present and does not represent a routine's ordinal position) mustcorrespond in case exactly to the routine as it is defined in the external DLL. Otherwise, VBdisplays runtime error 453, "Specified DLL function not found." If you aren't sure how theroutine name appears in the DLL, use the DumpBin.exe utility to view its export table. Forinstance, the following command displays the export table of advapi32.dll, one of the Windowssystem files:

dumpbin /exports c:\windows\system32\advapi32.dll

· libname can include an optional path that identifies precisely where the external library islocated. If the path is not included along with the library name, VB by default searches thecurrent directory, the Windows directory, the Windows system directory, and the directories inthe path, in that order.

· If the external library is one of the major Windows system DLLs (such as Kernel32.dll or

Advapi32.dll), libname can consist of only the root filename, rather than the completefilename and extension.

· One of the most common uses of the Declare statement is to make routines in the Win32API accessible to your programs. For more on this topic, see Win32 API Programming withVisual Basic by Steven Roman (O'Reilly 1999).

· In addition to the standard VB data types, you can also include BCL data types that are notwrapped by VB in arglist. Most useful are the unsigned integers, UShort, UInt16, andUInt32.

· In many cases, you can use routines available in the .NET Base Class Library or FrameworkClass Library instead of calling the Win32 API.

VB .NET/VB 6 Differences

· In VB 6, it is possible to declare the data type of an argument as Any, which suspendstypechecking by the VB runtime engine. In VB .NET, this usage is not supported.

· In VB 6, if ByVal or ByRef is not specified, an argument is passed to the calling procedure byreference. In VB .NET, arguments are passed by value by default.

· In VB 6, it is possible to override the method in which an argument is passed to an externalfunction within the call itself by specifying either ByVal or ByRef before the argument. InVB .NET, this usage is not permitted.

248

· The size of the integer data types in VB 6 and VB .NET are different, making it necessary torewrite any arglist that has a data type of Integer or Long in VB 6. The VB 6 Integer datatype is equivalent to the VB .NET Short data type. The VB 6 Long data type is equivalent tothe VB .NET Integer data type.

· VB 6 lacks a signed 8-bit integer data type and unsigned data types to correspond to theInteger and Long types. In the .NET platform, unsigned data types are available for 16-bitintegers (UInt16), 32-bit integers (UInt32), and 64-bit integers (UInt64). A signed byte datatype (SByte) is also available. All are BCL classes not wrapped by VB .NET.

Delegate StatementSyntax

[AccessModifier] Delegate Sub name [([arglist])])[AccessModifier] Delegate Function name [([arglist])]) As typeAccessModifier

Use: OptionalData Type: KeywordSpecifies scope/accessibility the same as when declaring a subroutine or function. Can beone of Public, Private, Protected, Friend, Protected Friend, or Shadows.

name

Use: RequiredType: String literalThe name of the delegate class.

arglist

Use: OptionalThe argument list; it has the same syntax as when defining a subroutine or function.

Description

Declares the parameters and return type of a delegate class. Note that the syntax is the same as thatused when declaring a subroutine or function, with the addition of the keyword Delegate.

Rules at a Glance

· Any procedure whose argument list and return type matches that of a declared delegate classcan be used to create an instance of this delegate class, as the upcoming example illustrates.

· For more information on delegates, see Section 6.1 in Chapter 6.

Example

249

Consider the following method:

Public Class Class1Public Sub AMethod(ByVal s As String)Msgbox(s)End SubEnd Class

Consider the following delegate declaration:

Delegate Sub ADelegate(ByVal s As String)

The following code uses the delegate to call the AMethod of Class1:

Protected Sub Form1_Click(ByVal sender As Object, _ByVal e As System.EventArgs) _Handles MyBase.Click' Object of type Class1Dim obj As New Class1( )' Declare a new delegateDim delg As ADelegate' Define the delegate, passing the address of the object's methoddelg = New ADelegate(AddressOf obj.AMethod)' Call the method using the Invoke method of the delegatedelg.Invoke("test")End Sub

DeleteSetting ProcedureClass

Microsoft.VisualBasic.Interaction

Syntax

DeleteSetting(appname[, section[, key]])

appname

Use: RequiredData Type: StringThe name of the application. This must be a subkey of the

HKEY_CURRENT_USER\Software\VB and VBA Program Settings registry key.

section

Use: Optional

250

Data Type: StringThe name of the application key's subkey that is to be deleted. section can be a single keyor a registry path separated with backslashes.

key

Use: OptionalData Type: StringThe name of the value entry to delete.

Description

Deletes a complete application key, one of its subkeys, or a single value entry from the Windowsregistry

Rules at a Glance

· section can contain a relative path (similar to that used to describe the folders on a harddrive) to navigate from the application key to the subkey to be deleted. For example, to deletethe value entry named TestKey in the registry key HKEY_CURRENT_USER\Software\VBand VBA Program Settings\RegTester\BranchOne\BranchTwo, you would use:

· DeleteSetting "RegTester", "BranchOne\BranchTwo", _"TestKey"

· You cannot use DeleteSetting to delete entries from registry keys that are not subkeys of

HKEY_CURRENT_USER\Software\VB and VBA Program Settings.

· If key is supplied, only the value entry named key and its associated value are deleted.

· If key is omitted, the subkey named section is deleted.

· If section is omitted, the entire application key named appname is deleted.

Example

Sub TestTheReg( )SaveSetting("MyRealGoodApp", _"TestBranch\SomeSection\AnotherSection", _"Testkey", "10")MsgBox("Now look in RegEdit")End SubSub TestDelete( )If GetSetting("MyRealGoodApp", _"TestBranch\SomeSection\AnotherSection", _"TestKey") <> "" ThenDeleteSetting("MyRealGoodApp", _"TestBranch\SomeSection\AnotherSection", _"TestKey")MsgBox("Look again!")End IfEnd Sub

Programming Tips and Gotchas

251

· DeleteSetting was designed to operate on initialization files in 16-bit platforms and on theregistry in 32-bit platforms. But the terminology used to describe the statement in the officialdocumentation is based on initialization files, rather than on the registry. In particular, what isdescribed as a key is a named key in an initialization file and a value entry in the registry.

· The behavior of the DeleteSetting statement differs under Windows 95 and Windows NTwhen it is used to remove a key from the registry. Under Windows 95, if the statement is usedto delete either appname or section, all subkeys belonging to the key to be deleted will alsobe deleted. Under Windows NT, on the other hand, the keys appname and section are onlydeleted if they don't contain subkeys.

· DeleteSetting cannot be used to delete the default value (i.e., the unnamed value entry)belonging to any key. If you're using only the VB registry functions, though, this isn't a seriouslimitation, since SaveSetting does not allow you to create a default value.

· Unless you are quite sure about what you're doing, you should only delete registry settingsthat have been placed in the registry by your own code. Inadvertently deleting the wrongentries can have disastrous consequences. However, because this statement only gives youaccess to the subkeys of HKEY_CURRENT_USER\Software\VB and VBA ProgramSettings, the potential damage is minimized.

· Never assume that the key you want to delete is necessarily present in the registry.

DeleteSetting deletes a user key (that is, a subkey of HKEY_CURRENT_USER); except onWindows 95 systems that are not configured to support multiple users, the user key is formedfrom a file that reflects only the present user's settings. This means that when one user runsan application, user settings are stored in his registry key. But when a second user runs theapplication for the first time, settings for that user are not likely to be present. Attempting todelete a nonexistent key produces runtime error 5, "Invalid procedure call or argument." Toprevent the error, you should first test for the presence of the registry key, as shown in theearlier example.

· Rather than rely on the relatively underpowered registry-access functionality available inVisual Basic, we highly recommend that you instead use the Registry and RegistryKeyclasses available in the BCL's Microsoft.Win32 namespace.

See Also

GetAllSettings Function, GetSetting Function, SaveSetting Procedure

Dim StatementSyntax

[Overrides] [Shadows] Dim [WithEvents] varname[([subscripts])] _[As [New] type] [= initexpr]

Overrides

Use: OptionalType: KeywordIn a derived class definition, indicates that a variable overrides a similar variable in a baseclass

Shadows

Use: Optional

252

Type: KeywordIn a derived class definition, indicates that calls to derived class members that are madethrough a base class ignore the shadowed implementation

WithEvents

Use: OptionalType: KeywordIn an object variable definition, indicates that the object will receive event notification

varname

Use: RequiredYour chosen name for the variable

subscripts

Use: OptionalDimensions of an array variable

New

Use: OptionalType: KeywordKeyword that creates an instance of an object

type

Use: OptionalThe data type of varname

initexpr

Use: OptionalAny expression that provides the initial value to assign to the variable; cannot be used if an AsNew clause is used

Description

Declares and allocates storage space in memory for variables. The Dim statement is used either atthe start of a procedure or the start of a module to declare a variable of a particular data type.

Rules at a Glance

· Object is the default data type created when no data type is explicitly declared.

253

· The declaration of a nonobject variable actually creates the variable. For an object variable,the variable is not created unless the optional New statement is used. If not, then the objectvariable is set to Nothing and must be assigned a reference to an existing object at somelater point in the code.

· When multiple variables are declared on the same line, if a variable is not declared with anexplicit type declaration, then its type is that of the next variable with an explicit typedeclaration. Thus, in the line:

Dim x As Long, i, j, k As Integer, s As String

the variables i, j, and k have type Integer. (In VB 6, the variables i and j have type Variant.)

· VB .NET permits the initialization of variables in the same line as their declaration (at longlast!). Thus, we may write:

Dim x As Integer = 5

to declare an Integer variable and initialize it to 5. Similarly, we can declare and initialize morethan one variable on a single line:

Dim x As Integer = 6, y As Integer = 9

· Variables that are not explicitly initialized by the Dim statement have the following defaultvalues:

Data type Initial value

All numeric types 0Boolean False

Date 01/01/0001 12:00:00 AMDecimal 0

Object Nothing

String Zero-length string ("")

· Local variables can have procedure-level scope or block-level scope. A variable that isdeclared using the Dim keyword within a Visual Basic procedure but not within a code blockhas procedure-level scope; that is, its scope consists of the procedure in which it is declared.On the other hand, if a variable is declared inside a code block (i.e., a set of statements that isterminated by an End, a Loop, or a Next statement), then the variable has block-levelscope; that is, it is visible only within that block.

· A variable cannot be declared using the Dim statement with WithEvents within a method,function, or procedure, since this creates a local variable with procedure-level scope only.

· In VB .NET, all arrays have a lower bound of 0. This is a change from earlier versions of VB,where we could choose the lower bound of an array.

· To declare a one-dimensional array variable, use one of the following example syntaxes:

· 'Implicit constructor: No initial size & no initialization

· Dim Arrayname( ) As Integer

· 'Explicit constructor: No initial size & no initialization

· Dim Arrayname() As Integer = New Integer( ) {}

·

· 'Implicit constructor: Initial size but no initialization

· Dim Arrayname(6) As Integer

·

· 'Explicit constructor: Initial size but no initialization

· Dim Arrayname( ) As Integer = New Integer(6) {}

254

·

· Implicit constructor: Initial size implied by initialization

· Dim Arrayname( ) As Integer = {1, 2, 3, 4, 5, 6, 7}

·

· 'Explicit constructor, Initial size and initializationDim Arrayname( ) As Integer = New Integer(6) {1, 2, 3, 4, 5, 6, 7}

· To declare a multidimensional array, use one of the following example syntaxes:

· ' Two-dimensional array of unknown size

· Dim arrayname(,) As Integer

·

· ' Two-dimensional array of unknown size

· Dim arrayname(,) As Integer = New Integer(,) {}

·

· ' Two-dimensional array of size 3 by 2

· Dim arrayname(3, 2) As Integer

·

· ' Two-dimensional array of size 3 by 2

· Dim arrayname(,) As Integer = New Integer(3, 2) {}

·

· ' Two-dimensional array of size 3 by 2, initialized

· Dim arrayname(,) As Integer = {{1, 4}, {2, 5}, {3, 6}}

·

· ' Two-dimensional array of size 3 by 2, initialized

· Dim arrayname(,) As Integer = New Integer(3, 2) {{1, 4}, _{2, 5}, {3, 6}}

· The WithEvents keyword cannot be used when declaring an array.

· You can set or change the number of elements of an array using the ReDim statement.

· The maximum allowed dimensions for an array are 60.

Programming Tips and Gotchas

· When you declare an object reference as WithEvents, that object's events can be handledwithin your application. Object variables must be declared WithEvents at the module level toallow you to provide an error handler.When you declare an object variable as WithEvents in the declarations section of themodule, the name of the object variable appears in the Object drop-down list at the top left ofyour code window. Select this and note that the events exposed by the object are available inthe Procedure drop-down list at the top right of the code window. You can then add code tothese event procedures in the normal way, as shown here:

Private WithEvents oEmp As EmployeePrivate Sub oEmp_CanDataChange(EmployeeCode As String, _Cancel As Boolean)'event handling code goes hereEnd SubPrivate Sub oEmp_DataChanged(EmployeeCode As String)'event handling code goes hereEnd Sub

255

For a fuller description and discussion of the uses of WithEvents, Event, and RaiseEvent,see the Event, RaiseEvent, and WithEvents entries.

· One word of warning when using the WithEvents keyword: if you are building a client-serversystem using a WithEvents object reference, you must ensure that the client machine givespermission for the server machine to create processes on it. Otherwise, even though the clientcan create instances of the object on the server, the server will not be able to call back to theclient with event notifications. In fact, your application will not even launch before a"Permission Denied" or similar error is generated. You can alter the permissions on the clientusing the DCOM Config utility.

· The way in which you declare an Object variable with the Dim statement dictates whether yourapplication uses early binding or late binding. Early binding allows object references to beresolved at compile time. Late binding resolves an object reference at runtime, which has anegative impact on runtime efficiency. To optimize the performance, you should use earlybinding whenever possible. For more information on this, see the discussion of binding in

Chapter 2.

· When you declare an array without dimensioning it, you risk an ArgumentNullExceptionexception if you attempt to iterate the array, as in the following code fragment:

· Dim aInts( ), iCtr As Integer

·

· For iCtr = 0 To UBound(aInts)

· Console.WriteLine(aInts(iCtr)) ' Raises exceptionNext

One workaround is to declare an empty array as having -1 element, as the following codefragment illustrates:

Dim aInts(-1) As IntegerFor iCtr = 0 to UBound(aInts) ' For loop never executedConsole.WriteLine(aInts(iCtr))Next

VB .NET/VB 6 Differences

· In VB 6, all variables declared using Dim without specifying a specific data type are created asVariants. In VB .NET, all variables whose data type is not specified are Objects.

· When multiple variables are declared on a single line of code in VB 6, variables not explicitlyassigned a data type are cast as variants. For example, in the statement:

Dim Var1, Var2, Var3 As String

both Var1 and Var2 are variants rather than strings. In VB .NET, the type declaration appliesto all undeclared variables since the last explicit type declaration. So the previous statement inVB .NET would cast Var1, Var2, and Var3 as strings.

· In VB 6, variables cannot be initialized at the same time they are declared. In VB .NET,variables can be assigned an initial value when they are declared.

· In VB 6, all variables defined within a procedure using the Dim keyword have procedure-levelscope. In VB .NET, variables defined using Dim in code blocks (such as loops) have blocklevelscope and are not accessible throughout the procedure. Hence, code such as thefollowing works under VB6 but may fail to compile under VB .NET:

· Dim iCtr As Integer

· 'Nested loop

· For iCtr = 0 To 10000

· Dim iCtr2 As Integer

· For iCtr2 = 0 To 10000

256

· Next

· Next

·

· ' Reinitialize iCtr2

· iCtr2 = 0

·

End Sub

· VB 6 supports fixed-length strings, but they are not supported in VB .NET.

· In VB 6, if an object is instantiated using the New keyword as part of a Dim statement, testingfor the validity of the object reference with a statement such as:

If obj Is Nothing Then

always fails, since the statement itself reinstantiates the object if it is Nothing. In VB .NET,this undesirable behavior has been changed, and setting the object to Nothing destroyes theobject.

· In VB 6, you could instantiate an object instantiated using the New keyword as part of a Dim

statement, release the object reference by setting it to nothing, then reinstantiate the object byreferencing it or its members. In VB.NET, setting the object reference to Nothing destroysthe object; subsequent attempts to reference the object generate a NullReferenceExceptionexception.

· In VB 6, arrays could be either fixed length or dynamic; in VB .NET, all arrays are dynamic.

· VB 6 allows you to define the lower bound of an array when it is initialized. In VB .NET, allarrays have a lower bound of 0. For example, the VB 6 syntax:

Dim array(1 To 20) As String

is not supported in VB .NET.

· In VB .NET, an array cannot be declared using the New keyword. Practically, this means thatyou cannot create an array of creatable objects, and must instead use a collection. VB 6, incontrast, allows arrays of objects.

See Also

Private Statement, Public Statement, ReDim Statement, Static Statement, WithEventsKeyword

Dir FunctionClass

Microsoft.VisualBasic.FileSystem

Syntax

Dir[(pathname[, attributes])]

pathname

257

Use: OptionalData Type: StringA string expression that defines a path, which may contain a drive name, a folder name, and afilename

attributes

Use: OptionalData Type: Numeric or Constant of the FileAttribute enumerationA FileAttribute enumeration constant or numeric expression specifying the file attributesto be matched

Return Value

String

Description

Returns the name of a single file or folder matching the pattern and attribute passed to the function

Rules at a Glance

· A zero-length string ("") is returned if a matching file is not found.

· Possible values for attributes are:

FileAttribute enumeration Value Description

Normal 0 Normal (not hidden and not a system file)

ReadOnly 1 Read-only file

Hidden 2 Hidden

System 4 System file

Volume 8 Volume label; if specified, all other attributes are ignored

Directory 16 Directory or folder

Archive 32 Archive

Alias 64 Alias or link

· The attributes constants can be Ored together to create combinations of attributes tomatch; e.g., FileAttribute.Hidden Or FileAttribute.Directory will match hiddendirectories.

· If attributes is not specified, files matching pathname are returned regardless of

attributes.

· You can use the wildcard characters * and ? within pathname to return multiple files.

· Although pathname is optional, the first call you make to Dir must include it. pathname mustalso be specified if you are specifying attributes. In addition, once Dir returns a zerolengthstring, subsequent calls to Dir must specify pathname, or runtime error 5, "Invalidprocedure call or argument," results.

· A call to Dir with no arguments continues the search for a file matching the last used

pathname argument (and attribute argument, if it was supplied).

258

Example

Private Sub Button1_Click(ByVal sender As System.Object, _ByVal e As System.EventArgs) _Handles Button1.ClickDim sFileName As StringDim sPath As String = "c:\windows\*.txt"sFileName = Dir(sPath)Do While sFileName > ""ListBox1.Items.Add(sFileName)sFileName = Dir( )LoopEnd Sub

Programming Tips and Gotchas

· Dir can only return one filename at a time. To create a list of more than one file that matches

pathname, you must first call the function using the required parameters, then makesubsequent calls using no parameters. When there are no more files matching the initialspecification, a zero-length string is returned. Once Dir has returned a zero-length string, youmust specify a pathname in the next call, or an error is generated.

· In previous versions of Visual Basic, the Dir function was commonly employed to determinewhether a particular file existed. Although it can still be used for this purpose, the use of theBCL System.IO namespace's File.Exists method is more straightforward. Since Exists is ashared public member of the File class, it can be called as follows:

If File.Exists("c:\windows\network.txt")

· The Dir function returns filenames in the order in which they appear in the file-allocation table.If you need the files in a particular order, you should first store the names in an array beforesorting. Note that an array can be easily sorted using the Array object's Sort method; the Arrayclass is part of the BCL's System namespace.

· The Dir function saves its state between invocations. This means that the function cannot becalled recursively. For example, if the function returns the name of the directory, you cannotthen call the Dir function to iterate the files in that directory and then return to the originaldirectory.

· If you are calling the Dir function to return the names of one or more files, you must provide anexplicit file specification. In other words, if you want to retrieve the names of all files in the

Windows directory, for instance, the function call:

strFile = Dir("C:\Windows", FileAttribute.Normal)

necessarily fails. Instead, the Dir function must be called with pathname defined as follows:

strFile = Dir("C:\Windows\*.*", FileAttribute.Normal)

· A major limitation of Dir is that it returns only the filename; it does not provide otherinformation, such as the size, date, and timestamp, or attributes of a file.

· Many difficulties with the Dir function result from not fully understanding how various

attributes constants affect the file or files returned by the function. By default, Dir returns a"normal" file (i.e., a file whose hidden or system attributes are not set). Hidden returns anormal file or a hidden file, but not a system file and not a system file that is hidden. System

returns a normal file or a system file, but not a hidden file, including a system file that is hidden.

259

FileAttribute.System Or FileAttribute.Hidden returns any file, regardless ofwhether it is normal, hidden, system, or system and hidden.

Directory ClassNamespace

System.IO

Createable

No

Description

The Directory class represents a directory or folder. (It appears that Microsoft is retreating from theterm folder, in favor of the legacy term directory.) The Directory class has a number of methods thatallow you to retrieve information about the directory's system properties, to move and delete adirectory, and to create a new directory. (Unfortunately, however, the Directory class lacks a Copymethod.)All of the members of the Directory class are shared methods, so they can be called withoutinstantiating any objects. For example, you can call the CreateDirectory method as follows:

Directory.CreateDirectory("C:\projects\project1")

This syntax may seem a bit awkward, especially to those familiar with earlier version of VB. Ratherthan the Directory object itself representing a directory, as it does in the case of a Folder object in theVB 6 FileSystemObject object model, the Directory class is simply a means to access a set ofdirectory-related functions.Directory class members marked with a plus sign (+) are discussed in further detail in their own entries.

Public Shared Methods

CreateDirectory +Delete +Exists +GetCreationTime +GetCurrentDirectoryGetDirectories +GetDirectoryRoot +GetFiles +GetFileSystemEntries +GetLastAccessTimeGetLastWriteTimeGetLogicalDrives +GetParent +Move +SetCreationTimeSetCurrentDirectorySetLastAccessTimeSetLastWriteTime

260

VB .NET/VB 6 Differences

The Directory object loosely corresponds to the Folder object in the FileSystemObject objectmodel. (The FileSystemObject object and its child objects are implemented in the MicrosoftScripting Runtime Library in the file scrrun.dll.) There is, however, a significant difference in themembers of each class, and in some cases, methods with similar functionality have different names.

See Also

File Object

Directory.CreateDirectory MethodClass

System.IO.Directory

Syntax

Directory.CreateDirectory(path)

path

Use: RequiredData Type: StringThe path of the new directory

Return Value

None

Description

Creates a new directory

Rules at a Glance

· path must represent a legal path.

· path can be an absolute or a relative path. For example:

Directory.CreateDirectory("C:\Temp")

specifies an absolute path (it begins with a drive's root directory), while:

Directory.CreateDirectory("..\Chapter2")

is a relative path that begins from the current directory. Relative paths can make use of the "."and ".." characters, which represent the current directory and the parent of the currentdirectory, respectively.

261

· The CreateDirectory method creates all directories required to create a specified path. Forexample, the code:

Directory.CreateDirectory("c:\NewDirectory\NewSubDirectory")

will create the NewDirectory folder if it does not exist and then the newSubDirectory folder if itdoes not exist.

· path can be either a path on the local system, the path of a mapped network drive, or a UNCpath.

Programming Tips and Gotchas

The CreateDirectory method does not raise an error if the directory to be created already exists.

Directory.Delete MethodClass

System.IO.Directory

Syntax

Directory.Delete(path [,recursive])

path

Use: RequiredData Type: StringThe path of the folder to delete.

recursive

Use: OptionalData Type: BooleanIndicates whether the folder and its contents are to be deleted if the folder is not empty. Itsdefault value is False.

Return Value

None

Description

Removes or deletes an existing directory

Rules at a Glance

262

· If path does not exist, the method generates a runtime error.

· If recursive is set to False (its default value), the directory must be empty to besuccessfully deleted; otherwise, a runtime error will be generated.

· If recursive is set to True, the method will delete not only the final directory in path, butalso of its files and all of its subdirectories, as well as all nested subdirectories and nested files.

· path can be either an absolute path (a complete path from the root directory to the directorywhose existence is to be confirmed) or a relative path (starting from the current directory to thepath whose existence is to be confirmed).

· path can be either a path on the local system, the path of a mapped network drive, or a UNCpath.

· path cannot contain wildcard characters.

Programming Tips and Gotchas

· The Delete method permanently deletes directories and their contents. It doesn't move themto the Recycle Bin.

· Care must be taken when setting recursive to True due to the danger of accidentallyremoving files, especially since the method does not prompt whether it should delete anyfolders or files.

· If the user has adequate rights, the source or destination can be a network path or sharename. For example:

· Directory.Delete("\\NTSERV1\d$\RootTwo")Directory.Delete("\\RootTest")

Directory.Exists MethodClass

System.IO.Directory

Syntax

Directory.Exists(path)

path

Use: RequiredData Type: StringThe path of the directory whose existence is to be determined

Return Value

True if the specified path exists; False otherwise

Description

Determines whether a given directory exists

Rules at a Glance

263

· path can be either an absolute path (a complete path from the root directory to the directorywhose existence is to be confirmed) or a relative path (starting from the current directory to thepath whose existence is to be confirmed).

· path can be either a path on the local system, the path of a mapped network drive, or a UNCpath.

· path cannot contain wildcard characters.

Directory.GetCreationTime MethodClass

System.IO.Directory

Syntax

Directory.GetCreationTime(path)

path

Use: RequiredData Type: StringA valid path

Return Value

A Date value indicating the creation date and time of the directory

Description

Indicates when a given directory was created

Rules at a Glance

· path can be either an absolute path (a complete path from the root directory to the directorywhose creation time is to be retrieved) or a relative path (starting from the current directory tothe directory whose creation time and existence is to be retrieved).

· path can be either a path on the local system, the path of a mapped network drive, or a UNCpath.

· path cannot contain wildcard characters.

Directory.GetDirectories MethodClass

System.IO.Directory

264

Syntax

Directory.GetDirectories(path [, searchpattern])

path

Use: RequiredData Type: StringA valid path to a directory

searchpattern

Use: OptionalData Type: StringA directory specification, including wildcard characters

Return Value

An array of strings, each element of which is the name of a subdirectory

Description

Returns the names of the subdirectories in a particular directory

Rules at a Glance

· path can be either an absolute path (a complete path from the root directory to the directorywhose subdirectories are to be retrieved) or a relative path (starting from the current directoryto the directory whose subdirectories are to be retrieved).

· path can be either a path on the local system, the path of a mapped network drive, or a UNCpath.

· path cannot contain wildcard characters.

· If searchpattern is specified, the method returns only those directories whose namesmatch the string, which can contain wildcard characters. Otherwise, searchpattern returnsthe names of all the subdirectories in the target directory specified by path.

· If the directory specified by path has no subdirectories, or if no directories match

searchpattern, an empty array is returned.

Example

The following code displays all subdirectories of c:\ whose names start with the letter P:

Dim sDirs( ) As StringDim i As IntegersDirs = Directory.GetDirectories("c:\", "P*")For i = 0 To UBound(sDirs)Console.WriteLine(sDirs(i))Next

Programming Tips and Gotchas

265

Since GetDirectories can return an empty array, you can prevent an array access error in either of twoways: you can iterate the returned array using the For EachNext construct, or you can retrievethe value of the UBound function, which is -1 in the case of an uninitialized array.

See Also

Directory.GetFiles Method, Directory.GetFileSystemEntries Method

Directory.GetDirectoryRoot MethodClass

System.IO.Directory

Syntax

Directory.GetDirectoryRoot(path)

path

Use: RequiredData Type: StringA valid path to a directory

Return Value

A String containing the name of the root directory of path

Description

Returns the name of the root directory of the drive on which path resides (assuming that path isvalid). For example, the code:

Directory.GetDirectoryRoot("c:\program files\accessories")

returns the string C:\ as the root directory.

Rules at a Glance

· path can be either an absolute path (a complete path from the root directory to the targetdirectory) or a relative path (starting from the current directory to the target directory).

· path can be either a path on the local system, the path of a mapped network drive, or a UNCpath. For example, the code:

Directory.GetDirectoryRoot("\\Pentium\C\AFolder")

returns \\Pentium\C, and if the folder \\Pentium\C\AFolder maps to the network drive Z,then:

266

Directory.GetDirectoryRoot("Z:\temp")

returns Z:\.

· path cannot contain wildcard characters.

See Also

Directory.GetParent Method

Directory.GetFiles MethodClass

System.IO.Directory

Syntax

Directory.GetFiles(path [, searchpattern])

path

Use: RequiredData Type: StringA valid path to a directory

searchpattern

Use: OptionalData Type: StringA file specification, including the wildcard characters * and ?

Return Value

An array of strings, each element of which contains the name of a file

Description

Returns the names of the files in a specified directory

Rules at a Glance

· path can be either an absolute path (a complete path from the root directory to the directorywhose filenames are to be retrieved) or a relative path (starting from the current directory tothe directory whose filenames are to be retrieved).

· path can be either a path on the local system, the path of a mapped network drive, or a UNCpath.

267

· path cannot contain wildcard characters.

· If searchpattern is specified, the method returns only those files whose names match thestring, which can contain wildcard characters. Otherwise, the function returns the names of allthe files in the path directory.

· If the directory specified by path has no files, or if no files match searchpattern, an emptyarray is returned.

Example

The following code displays all files in c:\ that have the extension .txt:

Dim sFiles( ) As StringDim i As IntegersFiles = Directory.GetFiles("c:\", "*.txt")For i = 0 To UBound(sFiles)Console.WriteLine(sFiles(i))Next

Programming Tips and Gotchas

Since GetFiles can return an empty array, you can prevent an array-access error in either of two ways:you can iterate the returned array using the For EachNext construct, or you can retrieve thevalue of the UBound function, which is -1 in the case of an uninitialized array.

See Also

Directory.GetDirectories Method, Directory.GetFileSystemEntries Method

Directory.GetFileSystemEntries MethodClass

System.IO.Directory

Syntax

Directory.GetFileSystemEntries(path [, searchpattern])

path

Use: RequiredData Type: StringA valid path to a directory

searchpattern

Use: OptionalData Type: String

268

A file specification, including wildcard characters

Return Value

An array of strings, each element of which contains the name of a filesystem entry (that is, a file ordirectory) in the path directory

Description

Returns the names of the filesystem entries (that is, of files and directories) in a specified directory

Rules at a Glance

· path can be either an absolute path (a complete path from the root directory to the directorywhose entries are to be retrieved) or a relative path (starting from the current directory to thedirectory whose entries are to be retrieved).

· path can be either a path on the local system, the path of a mapped network drive, or a UNCpath.

· path cannot contain wildcard characters.

· If searchpattern is specified, the method returns only those filesystem entries whosenames match the string, which can contain wildcard characters. Otherwise, the functionreturns the names of all the filesystem entries in the target directory specified by path.

· If the directory specified by path has no filesystem entries, or if no filesystem entries match

searchpattern, an empty array is returned.

Example

The following code displays all filesystem entries in c:\:

Dim sEntries( ) As StringDim i As IntegersEntries = Directory.GetFileSystemEntries("c:\")For i = 0 To UBound(sEntries)Console.WriteLine(sEntries (i))Next

Programming Tips and Gotchas

· The GetFileSystemEntries method combines the functionality of the GetDirectories andGetFiles methods.

· Since GetFileSystemEntries can return an empty array, you can prevent an array-access errorin either of two ways: you can iterate the returned array using the For EachNext

construct, or you can retrieve the value of the UBound function, which is -1 in the case of anuninitialized array.

See Also

Directory.GetDirectories Method, Directory.GetFiles Method

Directory.GetLogicalDrives Method

269

Class

System.IO.Directory

Syntax

Directory.GetLogicalDrives( )

Return Value

An array of strings, each element of which contains the name of the root directory on each logical driveon a system

Description

Retrieves the names of all logical drives and root directories on a system

Rules at a Glance

In the case of a mapped network drive, GetLogicalDrives returns the letter to which the drive ismapped. For instance, if the folder \\Pentium\C\AFolder is mapped to the Z drive, thenGetLogicalDrives will return Z:\ for this logical drive.

Example

Dim sDrives( ) As StringDim i As IntegersDrives = Directory.GetLogicalDrives( )For i = 0 To UBound(sDrives)Console.WriteLine(sDrives(i))Next

On my system, this code displays the following:

A:\C:\D:\E:\F:\G:\

Directory.GetParent MethodClass

System.IO.Directory

Syntax

GetParent(path)

path

Use: Required

270

Data Type: StringA valid path to a directory

Return Value

A DirectoryInfo object representing the parent directory of path (assuming that path is valid).

Rules at a Glance

· path can be either an absolute path (a complete path from the root directory to the directorywhose filenames are to be retrieved) or a relative path (starting from the current directory tothe directory whose filenames are to be retrieved).

· path can be either a path on the local system, the path of a mapped network drive, or a UNCpath.

· path cannot contain wildcard characters.

Programming Tips and Gotchas

The DirectoryInfo object has properties Name and ToString (among others). The Name propertyreturns only the name of the directory, while the ToString property returns its absolute path. Thus, thefollowing code displays the string program files:

MsgBox(Directory.GetParent("c:\program files\accessories").Name)

whereas the following code displays the string c:\program files:

MsgBox(Directory.GetParent("c:\program files\accessories").ToString)

See Also

Directory.GetDirectoryRoot Method

Directory.Move MethodClass

System.IO.Directory

Syntax

Directory.Move(sourcedirname, destdirname)

sourcedirname

Use: RequiredType: StringThe name of the directory to be moved

271

destdirname

Use: RequiredData Type: StringThe location to which the source drive and its contents are to be moved

Return Value

None

Description

Moves a directory and all its contents, including nested subdirectories and their files, to a new location

Rules at a Glance

· sourcedirname can be either an absolute path (a fully qualified path from the root directoryto the directory to be moved) or a relative path (starting from the current directory to thedirectory to be moved).

· sourcedirname and destdirname can be either a path on the local system, the path of amapped network drive, or a UNC path.

· Neither sourcedirname nor destdirname can contain wildcard characters.

· destdirname must be either a fully qualified path or a relative path.

· destdirname can also be an absolute path or a relative path, except that it must include thename to be assigned to the moved directory. This allows you to rename the directory at thesame time as you move it.

· If the directory indicated by destdirname already exists, an error occurs.

Example

Suppose that the C drive contains the following folders:

c:\docs\lettersc:\Documents and Settings

Moving the letters folder to make it a subdirectory of c:\Documents and Settings is done by thefollowing code:

Directory.Move("c:\docs\letters", _"c:\Documents and Settings\letters")

Thus, the first argument is the fully qualified name of the folder to move. The second argument is thepath that results after the move is made, whereas one might have expected this argument to be thetarget folder for letters, which is c:\Documents and Settings.

See Also

Directory.Delete Method

DoLoop Statement

272

Syntax

Do [{While | Until} condition][statements][Exit Do][statements]Loop

or:

Do[statements][Exit Do][statements]Loop [{While | Until} condition]

condition

Use: OptionalData Type: Boolean expressionAn expression that evaluates to True or False

statements

Use: OptionalProgram statements that are repeatedly executed while, or until, condition is True

Description

Repeatedly executes a block of code while or until a condition becomes True

Rules at a Glance

· On its own, DoLoop infinitely executes the code that is contained within its boundaries.You therefore need to specify within the code under what conditions the loop is to stoprepeating. In addition, if the loop executes more than once, the variable controlling loopexecution must be modified inside of the loop. For example:

· Do

· intCtr = intCtr + 1 ' Modify loop control variable

· MsgBox("Iteration " & intCtr & " of the Do loop")

· ' Compare to upper limit

· If intCtr = 10 Then Exit SubLoop

Failure to do this results in the creation of an endless loop.

· Adding the Until keyword after Do instructs your program to Do something Until thecondition is True. Its syntax is:

· Do Until condition

· 'code to execute

Loop

273

If condition is True before your code gets to the Do statement, the code within the

DoLoop is ignored.

· Adding the While keyword after Do repeats the code while a particular condition is True.When the condition becomes False, the loop is automatically exited. The syntax of the DoWhile statement is:

· Do While condition

· 'code to execute

Loop

Again, the code within the DoLoop construct is ignored if condition is False when theprogram arrives at the loop.

· In some cases, you may need to execute the loop at least once. You might, for example,evaluate the values held within an array and terminate the loop if a particular value is found. Inthat case, you would need to execute the loop at least once. To accomplish this, you canplace the Until or the While keyword along with the condition after the Loop statement.

DoLoop Until always executes the code in the loop at least once, and continues to loopuntil the condition is True. Likewise, DoLoop While always executes the code at leastonce, and continues to loop while the condition is True. The syntax of these two statements isas follows:

· Do

· 'code to execute

· Loop Until condition

·

· Do

· 'code to execute

Loop While condition

· A Null condition is treated as False.

· Your code can exit the loop at any point by executing the Exit Do statement.

Programming Tips and Gotchas

You'll also encounter situations in which you intend to execute the loop continually while or until acondition is True, except in a particular case. This type of exception is handled using the Exit Do

statement. You can place as many Exit Do statements within a DoLoop structure as you require.As with any exit from a DoLoop, whether it is exceptional or normal, the program continuesexecution on the line directly following the Loop statement. The following code fragment illustrates theuse of Exit Do:

Do Until condition1

'

code to execute

If condition2 ThenExit DoEnd if'

more code to execute - only if condition2 is false

Loop

See Also

274

WhileEnd While Statement

E FieldClass

System.Math

Syntax

Math.E

Description

This field returns the approximate value of the irrational number e, which is the base of the naturallogarithm and the base of the natural exponential function. In particular:

Math.E = 2.71828182845905

Rules at a Glance

This is a Shared member, so it can be used without creating any objects.

VB .NET/VB 6 Differences

The E Field is new to VB .NET.

See Also

Pi Field

End StatementSyntax

EndEnd ClassEnd EnumEnd FunctionEnd GetEnd IfEnd InterfaceEnd ModuleEnd NamespaceEnd PropertyEnd SelectEnd SetEnd Structure

275

End SubEnd SyncLockEnd TryEnd WithEnd While

Description

Ends a procedure or a block of code

Rules at a Glance

The End statement is used as follows:

Statement Description

End Terminates program execution

End Class Marks the end of a class definition

End Enum Marks the end of a series of enumerated constants

End Function Marks the end of a Function procedure

End Get Marks the end of a Property Get definition

End If Marks the end of an IfThenElse statement

End Interface Marks the end of an interface definition

End Module Marks the end of a code module

End Namespace Markes the end of a namespace definition

End Property Marks the end of a Property Let, PropertyGet, or Property Set procedure

End Select Marks the end of a Select Case statement.

End Set Marks the end of a Property Set definition

End Structure Ends the definition of a structure or user-defined type

End Sub Marks the end of a Sub procedure

End SyncLock Terminates synchronization code

End Try Marks the end of a TryCatch statement

End With Marks the end of a With statement

End While Marks the end of a While statement

Programming Tips and Gotchas

When used alone, the End statement wraps calls to the private FileSystem.CloseAllFiles function, aswell as to the System.Environment object's Exit method, making it relatively safe to call to terminate anapplication. However, it does not release resources not automatically handled by the garbage collector,and does not automatically call the Finalize destructor.

VB .NET/VB 6 Differences

· In VB 6, the End statement used by itself was to be avoided, since it terminated programexecution abruptly without performing normal cleanup operations. In VB .NET, End is muchsafer, and is not to be avoided.

· A number of the End statements are new to VB .NET. These include End Class (classesare defined in saparate CLS files in VB 6), End Get (Property Get statements are terminatedwith an End Property statement in VB 6), End Interface (interfaces are implemented asvirtual base classes in VB 6), End Module (code modules are defined in separate BAS files inVB 6), End Namespace (namespaces do not exist in VB 6), End Set (Property Set and

276

Property Let statements are terminated with an End Property statement in VB 6), End Try

(VB 6 does not support structured exception handling), and End While (VB 6 supports the

Wend statement to terminate a While loop).

See Also

Exit Statement

Enum StatementSyntax

accessmodifier Enum name [As type]

membername [= constantexpression]membername [= constantexpression]

 End Enum

accessmodifier

Use: OptionalType: KeywordThe possible values of accessmodifier are Public, Private, Friend, Protected, or

Protected Friend. For more information, see Section 3.7 in Chapter 3.

name

Use: RequiredType: String literalThe name of the enumerated data type.

membername

Use: RequiredType: String literalThe name of a member of the enumerated data type.

constantexpression

Use: OptionalData Type: LongThe value to be assigned to membername.

type

277

Use: OptionalType: KeywordThe data type of the enumeration. All enumerated members must be integers; possible valuesare Byte, Short, Integer, and Long.

Description

Defines an enumerated data type. All of the values of the data type are defined by the instances of

membername.

Rules at a Glance

· The Enum statement can only appear at module level, in the declarations section of a form,code module, or class module.

· Access rules for Enums are the same as for variables and constants. In particular, the optional

accessmodifier can be any one of the following: Public, Private, Protected, Friend,or Protected Friend. The following table describes the effects of the various accessmodifiers:

Direct access scope Class/object access scope

Private Declaring class Declaring classProtected All derived classes Declaring classFriend Derived in-project classes Declaring projectProtected Friend All derived classes Declaring projectPublic All derived classes All projects

· constantexpression can be either a negative or a positive number. It can also be anothermember of an enumerated data type or an expression that includes integers and enumerateddata types.

· If you assign a floating point value to constantexpression, it is automatically rounded andconverted to an integer only if Option Strict is off; otherwise, it generates a compiler error.

· If you do not specify type, it defaults to Integer.

· If constantexpression is omitted, the value assigned to membername is 0 if it is the firstexpression in the enumeration. Otherwise, its value is 1 greater than the value of thepreceding membername.

· The values assigned to membername cannot be modified at runtime.

Programming Tips and Gotchas

· Once you define an enumerated type, you can use name as the return value of a function. Forexample, given the enumeration:

· Public Enum enQuarter

· enQ1 = 1

· enQ2 = 2

· enQ3 = 3

· enQ4 = 4End Enum

you can use it as the return value of a function, as illustrated by the following functiondeclaration:

278

Public Function QuarterFromDate(datVar as Date) _As enQuarter

You can also use it in a procedure's parameter list when defining a parameter's data type, asin the following code fragment:

Public Function GetQuarterlySales(intQ As enQuarter) _As Double

· Although you can declare an enumerated type as the argument to a procedure or the returnvalue of a function, VB .NET does not provide type safety in these cases. That is, if the valueof the argument or the return value of the function is outside of the range of the enumeratedtype, VB .NET does not generate an error. In cases such as these, you should rely onvalidation routines to make sure that an input value is in fact within the range of anenumerated type.

· Individual values of an enumerated type can be used in your program just like normalconstants, except that they must be prefaced with the name of the enumeration.

· Enumerated types provide the advantage of allowing you to replace numeric values with moremnemonic labels and of allowing you to select values using the Auto List Members feature inthe Visual Studio IDE.

VB .NET/VB 6 Differences

· In VB 6, members of an enumeration can be accessed without having to qualify them with thename of the enumeration to which they belong. In VB .NET, this behavior is not permitted; allmembers of an enumeration can only be accessed by referring to the name of theirenumeration.

· In VB 6, all enumerated members are Longs. In contrast, VB .NET allows you to define theinteger data type of the enumeration's members.

· In VB 6, members of a public enumeration can be hidden from the Object Browser by adding aleading underscore to the member name. For example, in the enumeration:

· Public Enum Primes

· [_x0] = 0

· x1 = 1

· x2 = 3End Enum

the constant _x0 is hidden in Intellisense and the Object Browser unless the Object Browser'sShow Hidden Members option is selected. In Visual Studio .NET, a leading underscore doesnot hide a member.

See Also

Const Statement

Environ FunctionClass

Microsoft.VisualBasic.Interaction

279

Syntax

Environ(expression)

expression

Use: RequiredData Type: String, or a numeric expressionIf expression is a string, it must be the name of the required environment variable; if

expression is numeric, it must be the 1-based ordinal number of the environment variablewithin the environment table.

Return Value

A String containing the text assigned to expression

Description

Returns the value assigned to an operating-system environment variable

Rules at a Glance

· A zero-length string ("") is returned if expression does not exist in the operating system'senvironment-string table or if there is no environment string in the position specified by

expression.

· expression can be either a string or a numeric expression; that is, you can specify one orthe other, but not both.

Example

Public Module modMainPublic Structure envDim strVarName As StringDim strValue As StringEnd StructurePublic Sub Main( )Dim intCtr, intPos As IntegerDim strRetVal As StringDim udtEnv As envintCtr = 1DostrRetVal = Environ(intCtr)If strRetVal <> "" ThenintPos = InStr(1, strRetVal, "=")udtEnv.strVarName = Left(strRetVal, intPos - 1)udtEnv.strValue = Mid(strRetVal, intPos + 1)Console.Writeline(udtEnv.strVarName & ": " & udtEnv.strValue)ElseExit DoEnd IfintCtr = intCtr + 1Loop

280

End SubEnd Module

Programming Tips and Gotchas

· If expression is numeric, both the name and the value of the variable are returned. An equalsign (=) is used to separate them. For example, the function call Environ(1) might return thestring TEMP=C:\WINDOWS\TEMP.

· If you retrieve environment variables and their values by ordinal position, the first variable is inposition 1, not position 0.

· Due to the flexibility offered, it is now accepted and recommended practice to use the registryfor variables needed by your application, rather than the environment-string table.

· Environment variables can be defined in a variety of ways, including by the AUTOEXEC.BAT

and MSDOS.SYS files, as well as by the

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\SessionManager\Environment and HKEY_CURRENT_USER\Environment keys in the registry.

VB .NET/VB 6 Differences

· In VB 6, the Environ function retrieved environmental variables and their values only from theenvironment-string table. In VB .NET, the function retrieves values both from the environmentstringtable and the system registry.

· In VB 6, the function could be called using either the envstring named argument (if theargument was the name of an environment variable) or the number named argument (if thenumber represented the ordinal position of the variable in the environment table). VB .NETreplaces these with a single named argument, expression.

EOF FunctionClass

Microsoft.VisualBasic.FileSystem

Syntax

EOF(filenumber)

filenumber

Use: RequiredData: IntegerAny valid file number

Return Value

A Boolean indicating when the end of the file has been reached

Description

281

Returns a Boolean indicating when the end of the file has been reached. Applies to files opened forbinary, random, or sequential input.

Rules at a Glance

· filenumber must be an Integer that specifies a valid file number.

· If a file is opened for binary access, you cannot use EOF with the Input procedure. Instead,use LOF and Loc. If you want to use EOF, you must use FileGet rather than Input. In thiscase, EOF returns False until the previous FileGet procedure is unable to read an entirerecord.

Example

Dim fr As Integer = FreeFile( )Dim sLine As StringFileOpen(fr, "c:\data.txt", OpenMode.Input, OpenAccess.Read, _OpenShare.Default, -1)Do While Not EOF(fr)sLine = LineInput(fr)Debug.WriteLine(sLine)Loop

Programming Tips and Gotchas

· EOF allows you to test whether the end of a file has been reached without generating an error.

· Because you always write data to sequential files at the end of the file, the file marker isalways at the end of the file, and EOF will therefore always return True when testing filesopened with their modes set equal to either Input or Append.

See Also

LOF Function

Erase StatementSyntax

Erase arraylistarraylist

Use: RequiredData Type: String literalA list of array variables to clear

Description

Releases an array object. This is equivalent to setting the array variable to Nothing.

Rules at a Glance

282

· Specify more than one array to be erased by using commas to delimit arraylist.

· The Erase statement causes all memory allocated to arrays to be released.

Programming Tips and Gotchas

Once you use Erase to clear an array, it must be redimensioned with ReDim before being used again.This is because Erase releases the memory storage used by the array.

See Also

Dim Statement, ReDim Statement

Erl PropertyClass

Microsoft.VisualBasic.Information

Syntax

Erl

Return Value

An Integer containing the line number

Description

Indicates the line number on which an error occurred

Rules at a Glance

· Erl returns the line number only if one has been provided in the source code.

· If the error occurs on a line that does not have a line number, Erl returns 0.

Programming Tips and Gotchas

· Erl is not affected by compiler settings. Compiling with the /debug- switch does not prevent Erl

from accurately reporting the line number.

· Line numbers are rarely used in modern VB code. In VB.NET, line numbers are labels thatmust be followed by a colon.

· Although programmers have been requesting an error-handling function that reports the linenumber on which an error occurred, Erl has one major limitation: namely, it requires that thedeveloper assign a line number to source code lines in advance.

· Erl is not new to VB .NET. It was an undocumented and little known function in previousversions of Visual Basic (and of QBasic as well).

VB .NET/VB 6 Differences

283

In VB 6, line numbers are distinct from labels, and do not require that any symbol (other than whitespace) separate them from that their lines' source code. In VB .NET, line numbers are labels that mustbe followed by a colon.

Err ObjectClass

Microsoft.VisualBasic.ErrObject

Createable

No

Description

The Err object contains properties and methods that allow you to obtain information about a singleruntime error in a Visual Basic program. The Err object also lets you generate errors and reset theerror object. Because the Err object is an intrinsic object with global scope (which means that it is partof every VB project you create), you do not need to create an instance of it within your code.When an error is generated in your application—whether it is handled or not—the properties of the Errobject are assigned values that you can then access to gain information about the error that occurred.You can even generate your own errors explicitly using the Err.Raise method. You can also defineyour own errors to unify the error-handling process.When your program reaches an Exit Function, Exit Sub, Exit Property, Resume, or On Error

statement, the Err object is cleared and its properties reinitialized. This can also be done explicitlyusing the Err.Clear method.

Public Instance Properties

Propertyname Description

Description The string associated with the given error numberHelpContext A context ID within a Visual Basic Help fileHelpFile The path to a Visual Basic Help fileLastDLLError The last error code generated by a DLL; available only on 32-bit Windows systemsNumber A long integer used to describe an error (i.e., an error code)Source Either the name of the current project or the class name of the application thatgenerated the error

Public Instance Methods

Method name Description

Clear Resets all the properties of the Err objectRaise Forces an error of a given number to be generated

Programming Tips and Gotchas

284

· The Visual Basic Err object is not a collection; it only contains information about the last error,if one occurred. You could, however, implement your own error collection class to store anumber of errors by copying error information from the Err object into an application-definederror collection object.

· An Err object cannot be passed back from a class module to a standard code module.

· VB also supports structured error-handling through the TryCatchFinally statement.

· For a full description of error handling, see Chapter 7.

See Also

Err.Description Property, Err.HelpContext Property, Err.HelpFile Property, Err.NumberProperty, Err.Source Property

Err.Clear MethodClass

Microsoft.VisualBasic.ErrObject

Syntax

Err.Clear( )

Description

Explicitly resets all the properties of the Err object after an error has been handled

Rules at a Glance

You only need to clear the Err object if you need to reference its properties for another error within thesame subroutine, or before another On Error statement within the same subroutine.

Example

On Error Resume Nexti = oObjectOne.MyFunction(iVar)If Err.Number <> 0 ThenMsgBox ("The Error : " & Err.Description & vbCrLf _& " was generated in " & Err.Source)Err.ClearEnd Ifj = oObjectTwo.YourFunction(iVar)If Err.Number <> 0 ThenMsgBox ("The Error : " & Err.Description & vbCrLf _& " was generated in " & Err.Source)Err.ClearEnd If

Programming Tips and Gotchas

285

· Resetting the Err object explicitly using the Clear method is necessary in situations where youare using On Error Resume Next and are testing the value of Err.Number repeatedly.Unless you reset the Err object, you run the very real risk of catching the previously handlederror, the details of which are still lurking in the Err object's properties.

· The Err object is automatically reset when either a Resume, Exit Sub, Exit Function,

Exit Property, or On Error statement is executed.

· You can achieve the same results by setting the Err.Number property to 0; however, yourcode will be more readable if you use the Clear method.

· VB also supports structured error-handling through the TryCatchFinally statement.

· Internally, in VB .NET the Err object is an instance of the Microsoft.VisualBasic.ErrObjectclass. It is returned by the Err property of the Microsoft.VisualBasic.Information class.

See Also

Err.Raise Method

Err.Description PropertyClass

Microsoft.VisualBasic.ErrObject

Syntax

To set the property:

Err.Description = string

To return the property value:

string = Err.Description

string

Use: RequiredData Type: StringAny string expression

Description

A read/write property containing a short string describing a runtime error

Rules at a Glance

· When a runtime error occurs, the Description property is automatically assigned the standarddescription of the error.

· For application-defined errors, you must assign a string expression to the Description property,or the error will not have an accompanying textual message.

286

· You can override the standard description by assigning your own description to the Err objectfor both VB errors and application-defined errors.

Programming Tips and Gotchas

· If an error occurs within a class module, an ActiveX DLL, or an EXE—regardless of whether itis running in or out of your application's process space—no error information from thecomponent will be available to your application unless you explicitly pass back an error codeas part of the error-handling routine within the component. This is done using the Err.Raisemethod, which allows you to raise an error on the client, passing custom arguments forNumber, Source, and Description.

· If you raise an error with the Err.Raise method and do not set the Description property, theDescription property will be automatically set to "Application-efined or Object-Defined Error."

· You can also pass the Err.Description to a logging device, such as a log file in Windows 95 orthe application log in Windows NT, by using the App.LogEvent method, as the following codefragment demonstrates:

· EmployeesAdd_Err:

· App.LogEvent "EmployeesAdd" & "; " & _Err.Description, vbLogEventTypeError

· The best way to set the Description property for your own application-defined errors is to usethe named-description argument with the Raise method, as the following code shows:

· Sub TestErr( )

·

· On Error GoTo TestErr_Err

·

· Err.Raise 65444, _

· Description="Meaningful Error Description"

·

· TestErr_Exit:

· Exit Sub

· TestErr_Err:

· MsgBox (Err.Description)

· Resume TestErr_Exit

·

End Sub

· VB also supports structured error-handling through the TryCatchFinally statement.

See Also

Err.HelpContext Property, Err.HelpFile Property, Err.Number Property, Err.Source Property

Err.GetException MethodClass

Microsoft.VisualBasic.ErrObject

Syntax

287

Err.GetException( )

Return Value

A System.Exception object or an object inherited from it containing the current exception

Description

Returns the Exception object associated with the current exception

Rules at a Glance

· The GetException method can be called at any time in a program.

· If there is no exception, the method returns an uninitialized exception object (i.e., an objectwhose value is Nothing).

Example

The following code renames a file:

Private Sub RenameFile( )Dim sOldName, sNewName As StringTrysOldName = InputBox("Enter the file name to rename")sNewName = InputBox("Enter the new file name")Rename("c:\" & sOldName, "c:\" & sNewName)Catch ex As ExceptionMsgBox(Err.GetException( ).ToString)Exit SubEnd TryEnd Sub

If the user inputs an invalid filename in the first input box, the result is the following message thatdisplays information about the error:

System.IO.FileNotFoundException: File not found atMicrosoft.VisualBasic.FileSystem.Rename(String OldPath, String NewPath)at WindowsApplication2.Form1.RenameFile( ) inC:\Documents and Settings\sr\My Documents\Visual Studio Projects\ClipboardSave2\WindowsApplication2\Form1.vb:line 59

Programming Tips and Gotchas

· The Err.GetException method can be used with the unstructured On Error Resume Next

statement as well as with the TryCatchEnd Try structure.

· Since GetException is a member of the Err object, its major application is to provide access toerror information stored to an instance of the Exception class from code that relies onunstructured exception handling.

VB.NET/VB6 Differences

The GetException method is new to VB.NET.

See Also

288

Exception Class

Err.HelpContext PropertyClass

Microsoft.VisualBasic.ErrObject

Syntax

Err.HelpContext

Description

A read/write property that either sets or returns an Integer value containing the context ID of theappropriate topic within a Help file.

Rules at a Glance

· The Err object sets the HelpContext property automatically when an error is raised ifErr.Number is a standard VB .NET error.

· If the error is user-defined and you don't explicitly set the HelpContext property yourself, theErr object will set the value to 1000095, which corresponds to the "Application-defined orobject-defined error" help topic in the VB Help file. (The HelpContext property is set by the fifthparameter to the Err.Raise method.

· HelpContext IDs are decided upon when writing and creating a Windows Help file. Once theHelp file has been compiled, the IDs cannot be changed. Each ID points to a separate Helptopic.

Example

Sub TestErr( )On Error GoTo TestErr_ErrDim ii = 8MsgBox(i / 0)TestErr_Exit:Exit SubTestErr_Err:MsgBox(Err.Description, vbMsgBoxHelpButton, "ErrorVille", _Err.HelpFile, Err.HelpContext)Resume TestErr_ExitEnd Sub

Programming Tips and Gotchas

· You can display a topic from the Visual Basic Help file by using the MsgBox function with the

vbMsgBoxHelpButton constant and passing Err.HelpContext as the HelpContext

289

argument (as shown in the previous example). While this is a simple and very effective way toadd much more functionality to your applications, bear in mind that some of your users couldfind the explanations within the VB Help file somewhat confusing. If time and budget allow, thebest method is to create your own help file (for which you will need the Help compiler andother Help file resources from the full version of VB) and to pass both the HelpContext andHelpFileName to MsgBox.

· Some objects that you may use within your application have their own help files, which youcan access using HelpContext to display highly focused help to your users.

See Also

Err.HelpFile Property, Err.Number Property, Err.Source Property

Err.HelpFile PropertyClass

Microsoft.VisualBasic.ErrObject

Syntax

Err.HelpFile

Description

A read/write String property that contains the fully qualified path of a Windows Help file.

Rules at a Glance

The HelpFile property is automatically set by the Err object when an error is raised.

Example

See Err.HelpContext Property.

Programming Tips and Gotchas

· You can display a topic from the Visual Basic Help file by using the MsgBox function with the

vbMsgBoxHelpButton constant and passing Err.HelpFile as the HelpFile argument(as shown in the example for the Err.HelpContext property). While this is a simple and veryeffective way to add more functionality to your applications, bear in mind that some of yourusers could find the explanations within the VB Help file somewhat confusing. If time andbudget allow, the best method is to create your own help file (for which you will need the Helpcompiler and other Help file resources from the full version of VB) and to pass both theHelpContext and HelpFileName to MsgBox.

· Some objects that you may use within your application have their own help files, which youcan access using HelpFile to display highly focused help to your users.

· Remember that once the program encounters an Exit statement or an On Error

statement, all the properties of the Err object are reset; this includes the Help file. You musttherefore set the Err.HelpFile property each time that your application needs to access thehelp file.

290

See Also

Err.HelpContext Property, Err.Number Property

Err.LastDLLError PropertyClass

Microsoft.VisualBasic.ErrObject

Syntax

Err.LastDLLError

Description

A read-only property containing a system error code representing a system error produced within aDLL called from a VB program.

Rules at a Glance

· Only direct calls to a Windows system DLL from VB code will assign a value to LastDLLError.

· The value of the LastDLLError property depends upon the particular DLL being called. Yourcode must be able to handle the various codes that can be returned by the DLL you are calling.

· Don't forget that a failed DLL call does not itself raise an error within your VB program. As aresult, the Err object's Number, Description, and Source properties are not filled.

Programming Tips and Gotchas

· The LastDLLError property can be changed by VB at any time, so it is important to save thevalue in an independent variable as soon as possible.

· The LastDLLError property is only used by system DLLs, such as kernel32.dll. Therefore,errors that occur within DLLs you may have created will not cause an error code to beassigned to the property.

· Obtaining accurate documentation about the return values of system DLLs can be achallenging experience! Most useful information can be found by studying the APIdocumentation for Visual C++. However, you can use the Win32 API FormatMessage functionto return the actual Windows error message string from within Kernel32.DLL, whichincidentally will also be in the correct language. The following is a brief example that you canuse in your applications to display the actual Windows error description:

· Module modMain

· Declare Function FormatMessage Lib "kernel32" _

· Alias "FormatMessageA" ( _

· ByVal dwFlags as Integer, ByRef lpSource As Integer, /

· ByVal dwMessageId As Integer, _

· ByVal dwLanguageId As Integer, _

· ByVal lpBuffer As String, ByVal nSize As Integer, _

· By Ref Arguments As Integer) As Integer

·

· Public Const FORMAT_MESSAGE_FROM_SYSTEM As Integer = &H1000

· Public Const FORMAT_MESSAGE_IGNORE_INSERTS As Integer = &H200

291

·

· Function apiErrDesc (iErrCode As Integer) As String

· Dim sErrDesc As String = Space(256)

· Dim iReturnLen, lpNotUsed As Integer

·

· iReturnLen = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM _

· Or FORMAT_MESSAGE_IGNORE_INSERTS, _

· lpNotUsed, iErrCode, 0&, sErrDesc, _

· Len(sErrDesc), lpNotUsed)

· if iReturnLen > 0 Then

· apiErrDesc = Left(sErrDesc, iReturnLen)

· End If

· End FunctionEnd Module

Here's a snippet demonstrating how you can use this utility function:

lReturn = SomeAPICall(someparams)If lReturn <> 0 thenErr.Raise(Err.LastDLLError & vbObjectError, _"MyApp:Kernel32.DLL", _apiErrDesc(Err.LastDLLError))End If

· Note that some API calls return 0 to denote a successful function call, and others return 0 todenote an unsuccessful call. You should also note that some API functions do not appear toset the LastDLLError property. In most cases, these are functions that return an error code.You could therefore modify the previous snippet to handle these cases:

· lReturn = SomeAPICall(someparams)

· If lReturn <> 0 then

· If Err.LastDLLError <> 0 Then

· Err.Raise(Err.LastDLLError & vbObjectError, _

· "MyApp:Kernel32.DLL", _

· apiErrDesc(Err.LastDLLError))

· Else

· Err.Raise(lReturn & vbObjectError, _

· "MyApp:Kernel32.DLL", _

· apiErrDesc(lReturn))

· End IfEnd If

See Also

Err Object

Err.Number PropertyClass

Microsoft.VisualBasic.ErrObject

292

Syntax

Err.Number

Description

A read/write property containing a numeric value that represents the error code for the last errorgenerated.

Rules at a Glance

· When a runtime error is generated within the program, the error code is automaticallyassigned to Err.Number.

· The Number property is updated with an application-defined error whose code is passed as anargument to the Err.Raise method.

· When using the Err.Raise method in normal code, your user-defined error codes cannot begreater than 65536 nor less that 0. (For an explanation, see the final note in ProgrammingTips and Gotchas of Err.Raise Method.)

· VB reserves error numbers in the range of 1-1000 for its own trappable errors. In addition,error numbers from 31001 to 31037 are also used for VB trappable errors. In implementing aseries of application-defined errors, your error handlers should either translate applicationerrors into VB trappable errors or, preferably, assign a unique range to application-definederrors.

· When using the Err.Raise method in ActiveX objects, add the vbObjectError constant (-2147221504) to your user-defined error code to distinguish OLE errors from local-applicationerrors.

· When control returns to the local application after an error has been raised by the OLE server,the application can determine that the error originated in the OLE server and extract the errornumber with a line of code like the following:

· Dim lError as Long

· If (Err.Number And vbObjectError) > 0 Then

· lError = Err.Number - ObjectErrorEnd If

Programming Tips and Gotchas

· An error code is a useful method of alerting your program that a function within an ActiveX orclass object has failed. By returning a number based on the vbObjectError constant, youcan easily determine that an error has occurred. (vbObjectError is a constant that isdefined in the Microsoft.VisualBasic.Constants class.) By then subtracting vbObjectError

from the value returned by the object's function, you can determine the actual error code:

· If Err.Number < 0 then

· Err.Number = Err.Number - ObjectErrorEnd If

· You can create a sophisticated multiresult error-handling routine by using the Err.Numberproperty as the Case statement within a Select Case block, taking a different course ofaction for different errors, as this snippet demonstrates:

· Select Case Err.Number

· Case < 0

· 'OLE Object Error

· Set oObject = Nothing

· Resume DisplayErrorAndExit

· Case 5

· 'increment the retry counter and try again

· iTries = iTries + 1

293

· If iTries < 5 Then

· Resume RetryFunctionCall

· Else

· Resume DisplayErrorAndExit

· End If

· Case 20

· 'we almost expected this one!

· Resume Next

· Case Else

· Resume DisplayErrorAndExitEnd Select

· Directly assigning a Visual Basic-defined error code to the Number property does notautomatically update the Description or other properties of the Err object.

See Also

Err.HelpContext Property, Err.HelpFile Property, Err.Source Property

Err.Raise MethodClass

Microsoft.VisualBasic.ErrObject

Syntax

Err.Raise(number, source, description, _helpfile, helpcontext)

number

Use: RequiredData Type: Long integerA numeric identifier of the particular error

source

Use: OptionalData Type: StringThe name of the object or application responsible for generating the error

description

Use: OptionalData Type: String

294

A useful description of the error

helpfile

Use: OptionalData Type: StringThe fully qualified path of a Microsoft Windows Help file containing help or reference materialabout the error

helpcontext

Use: OptionalData Type: LongThe context ID within helpfile

Description

Generates a runtime error

Rules at a Glance

· To use the Err.Raise method, you must specify an error number.

· If you supply any of the number, source, description, helpfile, and helpcontext

arguments when you call the Err.Raise method, they are supplied as values to the Number,Source, Description, HelpFile, and HelpContext properties, respectively. Refer to the entriesfor the individual properties for full descriptions of and rules for each property.

· The number argument is a Long integer that identifies the nature of the error. Visual Basicerrors (both Visual Basic-defined and user-defined errors) are in the range 0-65535. Therange 0-512 is reserved for system errors; the range 513-65535 is available for user-definederrors. When setting the Number property to your own error code in a class module, you addyour error-code number to the vbObjectError constant.

Programming Tips and Gotchas

· The Err.Raise method replaces the older Error statement, which should not be used in newcode.

· The Raise method does not reinitialize the Err object prior to assigning the values you pass inas arguments. This can mean that if you Raise an error against an Err object that has notbeen cleared since the last error, any properties for which you don't specify values will stillcontain the values from the last error.

· As well as using Raise in a runtime scenario, you can put it to good use in the developmentstages of your program to test the viability of your error-handling routines under variouscircumstances.

· The fact that Err.Number only accepts numbers in the range 0-65536 may appear to bestrange at first because the data type of the Error Number parameter in the Raise event is aLong. However, deep in the recesses of the Err object, the error code must be declared as anunsigned integer—a data type not supported by VB.

See Also

Err.Clear Method

295

Err.Source PropertyClass

Microsoft.VisualBasic.ErrObject

Syntax

Err.Source

Description

A read/write string property containing the name of the application or the object that has generated theerror.

Rules at a Glance

· When a runtime error occurs in your code, the Source property is automatically assigned theproject name (that is, the string that is assigned to the project's Name property). Note that thisis not necessarily the filename of the project file.

· For clarity of your error messages, when you raise an error in a class module, the format ofthe source parameter should be project.class.

Programming Tips and Gotchas

Knowing what type of error has occurred within a program without knowing where the error wasgenerated is often of little use to the programmer. However, if you enhance the standard Source byadding the name of the procedure, you can cut your debugging time dramatically.

See Also

Err.HelpContext Property, Err.HelpFile Property, Err.Number Property

Error StatementSyntax

Error [errornumber]

errornumber

Use: OptionalData Type: LongAny valid error code

Description

296

Raises an error

Rules at a glance

The Error statement is included only for backward compatibility; instead, if you're using standardVisual Basic error handling, you should use the Err.Raise method and the Err object. Otherwise, youshould use structured exception handling with the TryCatch construct.

Programming Tips and Gotchas

The Error statement has been a "compatibility" statement for several versions of Visual Basic.Interestingly, it managed to survive the general purge of outdated language elements. Despite itspersistence, we still recommend that its use be strictly avoided.

See Also

Err.Raise Method, TryCatchFinally Statement

ErrorToString FunctionClass

Microsoft.VisualBasic.Conversion

Syntax

ErrorToString([errornumber])

errornumber

Use: OptionalData Type: LongA numeric error code

Return Value

A String containing an error message

Description

Returns the error message or error description corresponding to a particular error code

Rules at a Glance

· If errornumber is present, the function returns the text of the error message correspondingto that error code.

· If no arguments are passed to the function, it returns the text of the error messagecorresponding to the Description property of the Err Object.

297

See Also

Err.Description Property

Event StatementSyntax

[Public] Event eventName [(arglist)]

Public

Use: OptionalType: KeywordIndicates that the event is visible throughout the project

eventName

Use: RequiredType: String literalThe name of the event

arglist is optional and has the following syntax:

[ByVal | ByRef] varname[( )] [As type]

ByVal

Use: OptionalType: KeywordThe argument is passed by value; that is, a local copy of the variable is assigned the value ofthe argument.

ByRef

Use: OptionalType: KeywordThe argument is passed by reference; that is, the local variable is simply a reference to theargument being passed. All changes made to the local variable are reflected in the callingargument. ByRef is the default method of passing variables.

varname

Use: Required

298

Type: String literalThe name of the local variable containing either the reference or value of the argument.

type

Use: OptionalType: KeywordThe data type of the argument. It can be Byte, Boolean, Char, Short, Integer, Long, Single,Double, Decimal, Date, String, Object, or any user-defined type, object type, or data typedefined in the BCL.

Description

Defines a custom event that the object can raise at any time using the RaiseEvent statement.

Rules at a Glance

· The event declaration must be Public so that it is visible outside the object module; it cannotbe declared as Friend or Private. However, the Public keyword can be omitted from thedeclaration, since it is Public by default.

· An Event statement can only appear in the Declarations section of an object module, that is,in a form or class module.

Example

The following code snippet demonstrates how you can use an event to communicate a statusmessage back to the client application. To take advantage of this functionality, the client must declarea reference to this class using the WithEvents keyword.

Public Event Status(Message As String)Private Function UpdateRecords( ) as Boolean RaiseEvent Status "Opening the database" RaiseEvent Status "Executing the query" RaiseEvent Status "Records were updated" End Function

Programming Tips and Gotchas

· To allow the client application to handle the event being fired, the object variable must bedeclared using the WithEvents keyword.

· VB custom events do not return a value; however, you can use a ByRef argument in

arglist to simulate a return value. For more details, see the RaiseEvent statement.

· Unlike parameter lists used with other procedures, Event parameters lists cannot include

Optional or ParamArray arguments or default values.

· If you use the Event statement in a standard interface class (i.e., a class in which onlyproperties and methods are defined, but no code is included in the procedures) for use with

299

the Implements statement, the Implements statement does not recognize the "outgoinginterfaces" used by events, and therefore the event will be ignored.

· For more information about implementing your own custom events, see Section 6.2 in

Chapter 6.

See Also

RaiseEvent Statement

Exception ClassNamespace

System

Createable

Yes

Description

The Exception class and its inherited (child) classes represent runtime exceptions.

Selected Exception Class Members

The following provides a brief description of the more important members of the Exception class:

HelpFile property

Sets or retrieves a link to the help file associated with the exception. Its value is a UniformResource Name (URN) or Uniform Resource Locator (URL).

InnerException property

Returns a reference to the inner Exception object in the case of nested exceptions.

Message property

Returns the text of the error message.

Source property

Returns or sets a string containing the name of the application or the object that causes theerror.

StackTrace property

Returns a string (the stack trace) consisting of a list of all methods that are currently in thestack. The following shows a stack trace when the procedure DoArithmetic calls the procedure

300

Arithmetic, which generates an exception that is thrown up to DoArithmetic (the string hasbeen formatted to fit the margins of the page):

at WindowsApplication6.Form1.Arithmetic(String Action, Double x,Double y) in C:\Projects\WindowsApplication6\Form1.vb:line 68at WindowsApplication6.Form1.DoArithmetic( ) inC:\Projects\WindowsApplication6\Form1.vb:line 87

TargetSite property

Returns a MethodBase object representing the method that throws the exception. Forexample, if e is the exception whose stack trace is shown in the discussion of the StackTraceproperty, then the code.

e.TargetSite.Name

will return the string Arithmetic.

GetBaseException Method

This method returns the exception object for the innermost exception. For instance, in theprevious example (see the discussion of the StackTrace property) the code:

e.GetBaseException.ToString

returns the string:

System.ArithmeticException: There was an overflow orunderflow in the arithmetic operation.at WindowsApplication6.Form1.Arithmetic(String Action,Double x, Double y) inC:\Projects\WindowsApplication6\Form1.vb:line 68at WindowsApplication6.Form1.DoArithmetic( ) inC:\Projects\WindowsApplication6\Form1.vb:line 87///

ToString Method

Returns the fully qualified name of the exception and possibly the error message, the name ofthe inner exception, and the stack trace.

Children of the Exception Class

The System namespace contains the Exception class, which is the base class for a substantialcollection of derived exception classes, listed as follows. Note that the indentation indicates classinheritance. For example, EntryPointNotFoundException (the fifth from the last entry in the list) inheritsfrom TypeLoadException.

ExceptionApplicationExceptionSystemExceptionAccessExceptionFieldAccessExceptionMethodAccessExceptionMissingMemberExceptionMissingFieldExceptionMissingMethodExceptionAppDomainUnloadedException

301

AppDomainUnloadInProgressExceptionArgumentExceptionArgumentNullExceptionArgumentOutOfRangeExceptionDuplicateWaitObjectExceptionArithmeticExceptionDivideByZeroExceptionNotFiniteNumberExceptionOverflowExceptionArrayTypeMismatchExceptionBadImageFormatExceptionCannotUnloadAppDomainExceptionContextMarshalExceptionCoreExceptionExecutionEngineExceptionIndexOutOfRangeExceptionStackOverflowExceptionExecutionEngineExceptionFormatExceptionInvalidCastExceptionInvalidOperationExceptionMulticastNotSupportedExceptionNotImplementedExceptionNotSupportedExceptionPlatformNotSupportedExceptionNullReferenceExceptionOutOfMemoryExceptionRankExceptionServicedComponentExceptionTypeInitializationExceptionTypeLoadExceptionEntryPointNotFoundExceptionTypeUnloadedExceptionUnauthorizedAccessExceptionWeakReferenceExceptionURIFormatException

Programming Tips and Gotchas

· As Microsoft states: "Most of the exception classes that inherit from Exception do notimplement additional members or provide additional functionality." Thus, it is simply the classname that distinguishes one type of exception from another. The properties and methodsapplied to an exception object are inherited from the Exception base class.

· You can trap the generic Exception object, or you can trap a specific exception object. Thereare two circumstances in particular when you may want to trap a specific exception, ratherthan the more general Exception object:

o You want to handle errors differently based on their class. For instance, you may wantto issue different custom error messages for different exception types.

o You want to take advantage of members of a particular exception class that are notimplemented in the Exception base class. For instance, the ArgumentException classhas a ParamName property that returns the name of the parameter that causes theexception. If you trap the Exception class rather than the ArgumentException class,this member is unavailable.

VB .NET/VB 6 Differences

The Exception class, along with Structured Exception Handling (SEH), is new to the .NET platform.

302

Exit StatementSyntax

Exit DoExit ForExit FunctionExit PropertyExit SelectExit SubExit TryExit While

Description

Prematurely