Click here to Skip to main content
15,867,704 members
Articles / Programming Languages / Visual Basic
Article

VB’s Statically Typed Wrapper for Dynamic Code

Rate me:
Please Sign up or sign in to vote.
4.92/5 (11 votes)
23 Apr 2008CPOL12 min read 31.8K   23   1
Combining dynamic and static typing in VB.NET
Statically typed wrapper interface implemented by a dynamically typed wrapper class

Introduction

Dynamic 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 Basic

In Visual Basic .NET late binding can be accomplished by combining the Option Strict Off statement with declaring variables as Object. Essentially, late binding means that variable type is not known at compile time. This results in type checking being performed in runtime. This is why late binding is also often referred to as dynamic typing. So, since the type checking is limited to verifying that a certain class member is present in a late bound object, you can compile the code that will produce type related error in runtime. Take a look at the example in Listing 1.

Listing 1: In Dynamically Typed Code Certain Errors Can Be Discovered Only at Runtime

VB.NET
' 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 MissingMemberException will be thrown. Since Tree class does not have Start method, the last line in Main is the source of an error.

If you now activate the Strict option (replace Option Strict Off with Option Strict On), you get a compile time error with the following message: “Option Strict On disallows late binding.” You are no longer able to compile such code.

This can be of great help in preventing typos and similar errors. Just imagine that I have written engine.Stat() by mistake. The compiler is now helping me identify the error right away, during compilation: “Stat is not member of Engine”. And since IntelliSense is also present, there is only a slim chance I'll lose too much time on this type of error.

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 Prior

In 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 CreateObject function with ProgId as a parameter. That meant that Visual Basic runtime would check for the existence of a method or a property only at runtime, during the execution. As long as the object supported a function or a property in question, the code could execute without any problems.

Duck Typing

This 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 Option Strict On statement. This option did not exist in pre-.NET versions of Visual Basic. This means that in pre-.NET versions of VB, anything declared as Object is late bound. However, in VB.NET, once you place the Option Strict On statement at the top of your file, you have effectively turned off late binding in that file.

Now take a look at a sample where this style can come in handy. Consider the following snippet:

VB.NET
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 Option Strict to off. As long as the officeObject parameter was created as Excel.Worksheet or Word.Document, this code executes fine thanks to the fact that both of these objects have both the CheckSpelling and SaveAs methods.

It is also possible to write this code with Option Strict on. However, as you'll see, it is a lot more verbose—it requires you to write much more code to accomplish the same behavior, and it’s based on the reflective capabilities of Visual Basic. Again, take a look at the code:

VB.NET
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:

VB.NET
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 Document and Worksheet inherit the same base class or implement the same interface. As a matter of fact, Extract Interface is a standard refactoring technique that I will discuss in Chapter 12. It means no duplication and strict code at the same time.

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 Document or Worksheet class. You need to look for a different solution.

Resetting Dynamic or Static Behavior at the File Level

As I already mentioned, once Option Strict is activated, VB behaves as a completely statically typed language, on par with C# or Java. However, as a unique feature of VB compared to other static or dynamic languages, VB lets you specify this option on the file level. This means that you can mix files written in static and in dynamic style in the same project. You have another level of flexibility when you write code. The VB team from Microsoft sees this as a distinctive advantage of VB over static-only or dynamic-only languages, and some further enhancements of VB as a dynamically typed language are planned for the next release of Visual Basic. Microsoft’s position on the issue is expressed in the following sentence by Erik Meijer and Peter Drayton: “Static Typing Where Possible, Dynamic Typing When Needed.”

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 Reflection.Emit namespace or extension methods in VB 2008. So you can be pretty sure that all types you are going to encounter existed in the moment of compiling the program. Even in the case when some types are loaded dynamically, they were most probably compiled and created as static types. Consequently, there is actually a physical assembly or DLL file containing these types. It is highly probable that these types were created intentionally to represent a certain defined entity, along usual OO design and analysis guidelines.

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 CheckSpellingAndSave code snippet, you have seen that this code interacted with two types: Word.Document and Excel.Worksheet from the Office automation library. Knowing all this, it makes sense providing a statically typed wrapper over the dynamic code. The rest of the code will never know that for the moment static type checking was turned off. Client code will treat the wrapper just as any other static type and will never know that you have used it to encapsulate dynamic style implementation.

Providing a Statically Typed Wrapper for Dynamic Code

In the sample, you are dealing with two types, Document and Worksheet, and they are exhibiting some common behavior. In the sample those are CheckSpelling and SaveAs methods. However, since they do not share any common type, except the ultimate base type Object common to any other type in .NET, you cannot access the services these objects provide in a common way.

Now, if you had source code for these two classes, you could just declare an interface,for example, you could name it IOfficeWrapper. Declare all common methods for Document and Worksheet in that interface, and make Document and Worksheet implement this interface. This is typical Extract Interface refactoring, and we'll talk about it, as I already mentioned, in Chapter 12. This way, Document and Worksheet can be seen through a common interface and treated in a uniform manner. So, you can start by doing just that, writing an IOfficeWrapper interface to contain all the methods used in a common manner (see Listing 2).

Listing 2: Statically Typed Interface for Office Wrapper

VB.NET
Option 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 Excel.Worksheet nor the Word.Document class, you need to create a new class to implement this interface. This new class will delegate calls to the original officeObject, and to be able to treat both the Worksheet and Document objects in the same way, this class will be written in dynamically typed style. This means you need to place the class in a separate file and deactivate Option Strict.

You provide a wrapper with a constructor, so you can receive an officeObject and keep a reference to the officeObject in a private field. The CheckSpelling and SaveAs methods will only delegate a call to a reference of the original officeObject maintained as a field in the OfficeWrapper class. Listing 3 shows the resulting code:

Listing 3: Dynamically Typed Class Wrapper for Office Wrapper Implementing IOfficeWrapper Interface

VB.NET
Option 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 Document or Worksheet objects. The Document or Worksheet object is passed to the wrapper in a moment of wrapper creation. Of course, another very important detail here is that in the wrapper class you have deactivated Option Strict. This way, you can treat Document and Worksheet in polymorphic manner. However, in the wrapper interface, Option Strict is set On. So, now take a look at how you can make use of the wrapper. The following code refactors the original CheckSpellingAndSave method so the code is not duplicated and strict typing is maintained.

VB.NET
'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 officeObject parameter to officeWrapper.

In short, the dynamically typed wrapper class implements a statically typed wrapper interface and delegates all calls to an instance of a polymorphic Document or Worksheet object. The Document or Worksheet instance is accepted as a parameter by the wrapper constructor method at the moment of wrapper creation. Figure 1 shows a statically typed wrapper interface implemented by a dynamically typed wrapper class.

Statically typed wrapper interface implemented by a dynamically typed wrapper class
Figure 1

Conclusion

Presuming 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 Option Strict On on any file except the file containing the OfficeWrapper source. You have managed to isolate dynamic code from the rest of the application, minimizing its impact while preserving benefits of dynamic code and avoiding code duplication.

Additional Information

This 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

  • Version 1.0.0

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
Chile Chile
Danijel Arsenovski is senior developer and consultant from Santiago, Chile. His interests include advanced OO programming techniques and refactoring. He holds Microsoft's Solution Developer Certification and is often speaker at Microsoft's technical conferences. Recently, he has been recognized as Microsoft MVP.
He is the author of book "Professional Refactoring in Visual Basic" and "Professional Refactoring in C# and ASP .NET" from Wrox.
From time to time he blogs at http://blog.refactoringin.net

Comments and Discussions

 
GeneralSo you get Dynamic Interfaces after all... Pin
PatrickTb30-Apr-08 14:21
PatrickTb30-Apr-08 14:21 
I remember VB team announcing “Dynamic Interfaces” feature for VB9:
http://www.microsoft.com/uk/msdn/nuggets/nugget/176/VB9-Dynamic-Interfaces.aspx
This looks a lot like you just demonsterated.
They didn’t implement it because of timing, just as well. Surely they didn’t realize you can achieve the same thing using existing VB feature Wink | ;) ?
Great stuff!

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.