|I l @ ve RuBoard|
Moving Beyond Visual Basic 6.0
Visual Basic .NET provides a number of new language-specific features. I'll focus here on three of them: the
compilation option, operator
Option Strict Is Not Optional
Visual Basic developers are all familiar with the
compilation option and know how important it is as a development practice. Without the
directive, Visual Basic won't require using the
statement to declare a variable ”it will implicitly create
Visual Basic .NET also goes further and adds a
Of these four requirements, two require a little more investigation: the restrictions on narrowing conversions and the
Type conversion is the process of converting one type into another. The simplest example of a type conversion is storing a String object as a variable of type Object . This involves casting to a type further up its inheritance hierarchy and is referred to as a widening conversion . (All classes have System.Object as a base class.) A narrowing conversion, on the other hand, is a special type of conversion that comprises one or more of the following:
The following conversions are among those
Even a brief reading of this list should give you a sense that the conversions are dangerous from the standpoint of logical correctness. It's clearly important to ensure that if the conversion is intended, it is stated explicitly. Other wise, with
, you might perform a narrowing conversion without
This is not to say that narrowing conversions are never appropriate.
just requires that you explicitly specify the cast (making the code very easy for the casual reviewer to understand). It is a way of forcing developers to explicitly state their intentions rather than
As mentioned earlier,
disallows something called late binding. For
Late binding delays the resolving of the object's type information until run time. Essentially, it provides a way to access objects in a typeless manner using the
type. (The real type is
Late binding allows a single variable to contain a variety of unrelated types. This can give you extraordinary design flexibility but often leads to subtle programmatic errors. In Visual Basic 6.0, late binding was implemented using COM Automation (implementing the IDispatch interface). Late binding in Visual Basic .NET is accomplished through a mechanism called reflection. Despite the two different implementation mechanisms, the end result is the same: runtime method signature lookups. Visual Basic .NET allows you to design much better solutions by using class and interface inheritance and thereby avoiding late binding altogether.
Even though late binding provides a great deal of flexibility, it has two major drawbacks: bugs and performance issues. Consider the following code example:
SubMain() Dimo=NewSystem.Random() Console.WriteLine(o.Value) EndSub
This code compiles fine but it will throw a
at run time because the
class does not contain a public method called
is set to
, the compiler will not allow you to specify methods that don't exist, which forces you to fix the problem then and there. The compiler will do this in two ways. First, the background compiler will insert a "squiggly" line underneath the errant method along with a tooltip error message that corresponds to an entry in the Visual Basic .NET task list. Second, if you attempt to build the component, the build operation will fail and the
ultimately gives you the choice between generating a compile-time error or a runtime failure. Allowing late binding
I'll explore performance issues related to late binding in Chapter 11, "Common Performance Issues."
As mentioned earlier, even if the method does exist on the object, the compiler must generate more code to look up the method and execute it. Turning on Option Strict will prevent this. For example, in the previous code snippet we set Option Strict to On , and therefore we must declare all variables explicitly. In this case, the variable o must be declared as type Random so that we can access its properties. When we do so, we discover that we want to use the Next property, not the Value property. If you were to declare o as type Object (and set Option Strict to Off ), the Visual Basic .NET compiler would have to insert code to look up the property Next at run time.
The following code makes a call to the Next property without all of the extra behind-the-scenes method calls and gives you the benefit of additional compile-time validation:
OptionStrictOn SubMain() DimoAsNewSystem.Random() Console.WriteLine(o.Next) EndSub
The important thing to take away from this discussion is that the compiler can generate more optimized (faster and less resource-
Short-Circuiting Your Operators
We're all familiar with the logical operators And and Or . They perform simple but necessary logical evaluations. The behavior of these operators is not always obvious and can lead to some confusion, especially if you've used other languages such as C++ or C#. To see what I'm talking about, look at the following code sample:
SubMain() 'Case1: IfFunctionTrue()OrFunctionFalse()Then 'DoSomething EndIf 'Case2: IfFunctionFalse()AndFunctionTrue()Then 'DoSomethingelse EndIf EndSub FunctionFunctionTrue()AsBoolean 'Hugecomputationlogic ReturnTrue EndFunction FunctionFunctionFalse()AsBoolean 'LotsofDatabaseactivity ReturnFalse EndFunction
In Sub Main , both cases result in FunctionTrue and FunctionFalse being called, regardless of the return values. In this respect, the And and Or logical operators always evaluate their arguments ”without exception. With me so far?
Table 2-1 and Table 2-2 describe how these operators work. But notice that with both operators, the output is highly predictable. In the case of the And operator, if the first argument is False , the result is always False . In the case of the Or operator, if the first argument is True , the result is always True . What does this mean? It means that there's a way to optimize the And and Or operators: conditional short-circuiting.
Table 2-1. Truth Table for the And Operator (Result = A And B)
Table 2-2. Truth Table for the Or Operator (Result = A Or B)
Visual Basic .NET introduces the AndAlso and OrElse operators to allow conditional short-circuiting. Both of these operators determine whether to evaluate the second argument depending on the value of the first argument. (Remember that And and Or always evaluate both arguments.) Table 2-3 and Table 2-4 show the result when the second argument is evaluated. The AndAlso operator looks at the first argument of the expression A . If it is False , AndAlso doesn't bother to evaluate B . (The X in the table means "don't care" because the expression cannot be True .) On the other hand, if A is True , AndAlso must evaluate B to determine the result.
Table 2-3. Truth Table for the AndAlso Operator (Result = A AndAlso B)
Table 2-4. Truth Table for the OrElse Operator (Result = A OrElse B)
Using the previous code example, replacing
will eliminate the need to run
in the first case and
in the second case. This can increase the efficiency of your application's logical expressions, but it can also lead to confusion. If you decide to use the
DimcustAsCustomer ... 'Short-circuitingallowsanicecompactstatement If(NotcustIsNothing)AndAlsocust.IsValidThen ... EndIf 'Ifwetriedthis,wewouldgeta 'NullReferenceExceptionifcust=Nothing If(NotcustIsNothing)Andcust.IsValidThen ... EndIf 'Thestandardoperatorsrequirealittlemore 'verbosecodetogetitright IfNotcustIsNothingThen Ifcust.IsValidThen ... EndIf EndIf
Blindly converting operators in existing projects can introduce logical errors. When you're converting operators in existing projects, do so
In early beta versions of Visual Basic .NET, the And and Or operators were short-circuited by default, but this was changed for backward compatibility reasons and the AndAlso and OrElse operators were introduced. I'm not exactly thrilled by this ”I prefer the earlier behavior ”but the decision was made for a reason and highlights beautifully the reality that software development (including development languages) is an exercise in balancing competing interests.
Calling Platform Functions Directly: Declare and DllImport
The .NET Framework class library is quite
Be careful when you decide to use a Win32 API call instead of the .NET Framework. Sometimes it can be difficult to find certain features in the .NET Framework (especially when you're first familiarizing yourself with the framework), and it might take a good deal of searching to find them. A lot of work has gone into making the framework classes very robust. If you choose to forgo using the .NET Framework, you'll have to do more coding to achieve the level of error checking and handling that the framework classes provide. If the functionality just isn't exposed, fine. Otherwise, you probably shouldn't bother trying to reproduce what is already available in the .NET Framework. The .NET Framework significantly
One possible reason for using
is to support INI configuration file access. The .NET Framework does not provide direct support for INI files ”XML configuration files are the preferred way to store configuration information. However, some businesses still rely on INI files. For example, older applications that use INI files might need to share them with
PublicDeclareAnsiFunctionGetPrivateProfileStringLib "kernel32" Alias_ "GetPrivateProfileStringA" (ByValapplicationNameAsString,_ ByValkeyNameAsString,_ ByValdefaultValueAsString,_ ByValreturnedStringAsSystem.Text.StringBuilder,_ ByValsizeAsInteger,_ ByValfileNameAsString)AsInteger SubMain() DimsbAsNewSystem.Text.StringBuilder(256) DimxAsInteger=GetPrivateProfileString("Mail",_ "MAPI",_ "Default",_ sb,_ sb.Capacity,_ "C:\WINDOWS\win.ini") Console.WriteLine(sb.ToString) EndSub
Visual Basic .NET provides an alternative way to import native methods without using the Declare statement: the DllImport attribute. The following code is similar in functionality to the previous example:
ImportsSystem.Runtime.InteropServices PublicModuleModule1 SubMain() DimsbAsNewSystem.Text.StringBuilder(256) DimxAsInteger=GetPrivateProfileString("Mail",_ "MAPI",_ "Default",_ sb,_ sb.Capacity,_ "C:\WINDOWS\win.ini") Console.WriteLine(sb) EndSub <DllImport("KERNEL32.DLL",EntryPoint:="GetPrivateProfileStringA",_ SetLastError:=True,CharSet:=CharSet.Ansi,ExactSpelling:=True,_ CallingConvention:=CallingConvention.WinApi)>_ PublicFunctionGetPrivateProfileString(_ ByValapplicationNameAsString,_ ByValkeyNameAsString,_ ByValdefaultValueAsString,_ ByValreturnedStringAsSystem.Text.StringBuilder,_ ByValsizeAsInteger,_ ByValfileNameAsString)AsInteger EndFunction EndModule
The DllImport attribute forwards all calls to the GetPrivateProfileString method to the GetPrivateProfileStringA function in the Kernel32 library. Note that these two native method invocation forms are functionally distinct. The DllImport attribute is a syntax that is common across languages; the Declare statement is specific to Visual Basic .NET. In addition, the Declare statement often injects more parameter attributes to ensure that the Declare statement mirrors the Visual Basic 6.0 Declare statement behavior as closely as possible. This means that the Declare statement is often not the most performance-inducing option. It also lacks the flexibility of DllImport ” it gives you fewer options and less control over the method importing process.
So when should you use Declare ? When performance is not an issue and the default Declare behavior is suited to your needs, there is no reason to choose DllImport . You should absolutely use DllImport when performance is a consideration or the target method has requirements that cannot be addressed using the Declare statement (such as nonstandard calling conventions or tricky parameter marshaling). Generally speaking, for the enterprise developer I wholeheartedly endorse using DllImport consistently. This allows you to ignore any performance considerations and ensures a reliable standard throughout your project regardless of the development language.
|I l @ ve RuBoard|