|
|||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionDynamic languages like PHP, Python, Pearl, Ruby, Groovy and others have become increasingly popular in the last decade. Dynamic language converts often quote ease of use and high productivity as a main benefit over classical static languages. Today, dynamic languages have come out of scripting department and are used for development of full blown applications, especially in the Web domain. Visual Basic (in some of its forms) has always been Microsoft’s choice in dynamic quarter. With the latest release of Visual Basic 9.0 (comes with Visual Studio 2008), dynamic features of VB have been further improved. So, in VB 9.0 you can use extension methods, local variable type inference, lambda expressions and others that make VB even more dynamic. What’s unique about VB is that dynamic behavior can be controlled with a simple directive.Static vs. Dynamic Typing and Visual BasicIn Visual Basic .NET late binding can be accomplished by combining the Listing 1: In Dynamically Typed Code Certain Errors Can Be Discovered Only at Runtime' Static type checking deactivated
Option Strict Off
Module Module1
Sub Main()
Dim engine As Engine = New Engine
Dim tree As Object = New Tree
engine.Start()
'Calling Start method on Tree instance
tree.Start()
End Sub
End Module
Public Class Engine
Public Sub Start()
'...
End Sub
End Class
'Tree class does not have Start member
Public Class Tree
Public Sub Grow()
'...
End Sub
End Class
You can compile this code successfully, but once you try to run it, a If you now activate the This can be of great help in preventing typos and similar errors. Just imagine that I have written However, traditional Visual Basic programmers have a lot of experience in using late binding, and there is a reason for that. To get some historic perspective on late binding, you need to look at the issue of late binding in Visual Basic 6. Late Binding in Visual Basic 6 and PriorIn the COM era (Visual Basic 6 and prior), late binding often presented a workaround for COM versioning and binary compatibility issues. When performing early binding in referencing COM components, Visual Basic would bind to a specific version of a component. In COM, a different set of GUIDs was used to identify a component. Each time a component interface was changed, meaning that a signature of any non-private member, a method, or a property was changed, a completely new GUID for that component was created. This version of the component had no relation with the previous version of that same component. This means that if an application was performing early binding, any other binary non-compatible version of component would provoke the infamous "Error 429 - ActiveX component can't create object" error. In the case of late binding, as long as a new version of a component had all the same methods that the previous version had, even when containing new non-private methods, the application would continue to work. For this to be accomplished, a variable had to be declared as Object and created using the Duck TypingThis style of programming where you rely on type being checked by member signature comparison at runtime is also known as duck typing. The name originates from the following phrase: "If it walks like a duck and quacks like a duck, it must be a duck." As long as the object has the member that corresponds to what client code is expecting, everything works out fine. There are a number of modern languages like Ruby and Python and some older ones like Smalltalk that permit this style of programming. Devotees of dynamically typed languages emphasize the flexibility and productivity that can be obtained from this approach and praise it over the benefits that static type checking can bring. This style generally works well when paired with unit testing that can create a much deeper safety net than static typing can. In dynamically typed languages, function calls are resolved at runtime, which permits treating objects in polymorphic manner, but without any interface or implementation inheritance relationship between those objects. A similar style is possible in VB.NET, but can be turned off at will by using Now take a look at a sample where this style can come in handy. Consider the following snippet: Option Strict Off
‘...
Public Sub CheckSpellingAndSave(ByVal officeObject As Object)
officeObject.CheckSpelling()
officeObject.SaveAs(FileName)
End Sub
For this code to compile, you need to set It is also possible to write this code with Option Strict On
‘...
Public Sub CheckSpellingAndSave(ByVal officeObject As Object)
'Object array of parameters is passed to Invoke method
'of System.Reflection.MethodInfo class
Dim saveAsParams As Object() = {FileName}
'Invoking method without parameters using reflection
officeObject.GetType.GetMethod("CheckSpelling"). _
Invoke(officeObject, Nothing)
'Invoking method with parameter using reflection
officeObject.GetType.GetMethod("SaveAs"). _
Invoke(officeObject, saveAsParams)
End Sub
This code is based on reflection, and all that you can do with late binding in VB you can also accomplish using reflection and even more. However, there is quite a difference in the quantity of code that has been written. In this case, the first sample has only two lines, but the second, reflection-based version is significantly more verbose. Now imagine you have to write a lot of code similar to this. You will definitely wish to use the less prolix variant. Finally, without using reflection, the only solution in strict mode is to have two overloaded methods: Option Strict On
‘...
Public Sub CheckSpellingAndSave(ByVal officeObject As Worksheet)
officeObject.CheckSpelling()
officeObject.SaveAs(FileName)
End Sub
Public Sub CheckSpellingAndSave(ByVal officeObject As Document)
officeObject.CheckSpelling()
officeObject.SaveAs(FileName)
End Sub
The problem with this solution is that you have identical code in both methods. It means that the code is duplicated. As you will see in Chapter 9, duplicated code is number one smell and has to be eliminated. So obviously, this solution does not make me happy either. Now, in more a common situation you could resolve this by making both However, in this case you do not have a source for the Office automation libraries, so working the problem out this way is out of question. You cannot modify the Resetting Dynamic or Static Behavior at the File LevelAs I already mentioned, once Erik Meijer’s and Peter Drayton’s full paper “Static Typing Where Possible, Dynamic Typing When Needed: The End of the Cold War Between Programming Languages” can be accessed here. This leads to an interesting way of looking at this problem. Maybe there is some way to isolate dynamic from static code? This way, you could keep code strict in all but a minimal part of the application. But first, a few words about VB as dynamic language. Some dynamic languages go further than VB with the dynamic paradigm. In these languages, not only do you not need to know the exact type at compile time, but also you are allowed to modify types by adding properties and methods “on the fly” at runtime. Something similar is not possible in VB without using services provided by the With all this in mind, the following conclusion can be reached. In VB, even when writing dynamic code, you are still interacting with static types underneath. Going back to the Providing a Statically Typed Wrapper for Dynamic CodeIn the sample, you are dealing with two types, Now, if you had source code for these two classes, you could just declare an interface,for example, you could name it Listing 2: Statically Typed Interface for Office WrapperOption Explicit On
'Static type checking turned on
Option Strict On
Public Interface IOfficeWrapper
Sub CheckSpelling()
Sub SaveAs(ByVal fileName As String)
End Interface
Since you can change neither the You provide a wrapper with a constructor, so you can receive an Listing 3: Dynamically Typed Class Wrapper for Office Wrapper Implementing IOfficeWrapper InterfaceOption Explicit On
'Note Option Strict deactivated
Option Strict Off
' Class is implementing IOfficeWrapper interface
Public Class OfficeWrapper
Implements IOfficeWrapper
'Private field maintains a reference to Document or Worksheet object
Private wordDocOrExcelWorksheet As Object
Public Sub New(ByRef wordDocOrExcelWorksheet As Object)
'Constructor accepts reference to Document or Worksheet instance
'and keeps it in a private field
Me.wordDocOrExcelWorksheet = wordDocOrExcelWorksheet
End Sub
Public Sub CheckSpelling() Implements _
IOfficeWrapper.CheckSpelling
'CheckSpelling method delegates call to Document or Worksheet
wordDocOrExcelWorksheet.CheckSpelling()
End Sub
Public Sub SaveAs(ByVal fileName As String) _
Implements IOfficeWrapper.SaveAs
'SaveAs method delegates call to Document or Worksheet
wordDocOrExcelWorksheet.SaveAs(fileName)
End Sub
End Class
Basically, the wrapper class only delegates calls to the original 'Activate Option Strict in the wrapper client file
Option Strict On
‘...
Public Sub CheckSpellingAndSave(ByVal officeObject As Object)
'Create wrapper around officeObject
Dim officeWrapper as IOfficeWrapper = _
New OfficeWrapper(officeObject)
'Use wrapper to perform officeObject services
officeWrapper.CheckSpelling()
officeWrapper.SaveAs(FileName)
End Sub
The only code you had to add to the method was the wrapper creation code. You also had to redirect calls from the In short, the dynamically typed wrapper class implements a statically typed wrapper interface and delegates all calls to an instance of a polymorphic
Figure 1
ConclusionPresuming that this is not the only place you'll use the Office automation objects, the cost of creating and using the wrapper in order to activate strict typing is quite reasonable. Now you can place Additional InformationThis article has been adapted from the title “Professional Refactoring in Visual Basic” by Danijel Arsenovski published by Wrox. You can check out my blog at Fluid Code. History
|
||||||||||||||||||||||||||||||||||||||||||||