In the article Combine Static and Dynamic Types Danijel Arsenovski explains a refactoring technique using late-bound calls or duck typing as a lot of people call it. I don't particularly care for duck typing. I much prefer my coding errors to be caught at compile time. With duck typing if you try to call a method on an object that doesn't support that method an exception is usually thrown at runtime. A lot of people say this type of error should be caught in the unit test phase, but how many people have 100% code coverage for their unit tests? Discussing the Pros and Cons of Duck Typing can take quite awhile so we'll just say that I don't particularly like it and I wanted to try and find a different solution.

The Problem

The two classes Worksheet and Document both have methods with the signature CheckSpelling() and SaveAs(FileName As String). We wanted to write a function like the following that works on both Worksheet and Document objects.

Public Sub CheckSpellingAndSave(ByVal officeOjbect As Object)
End Sub

Seeings how we don't have the source for Worksheet and Document we can't change them to inherit from a common interface that supports these methods, and the common base class that they have doesn't expose these methods.

Duck Typing Solution

So he proposed an interesting combination of dynamic and static typing. In one file have the following:

Option Strict Off
Public Class OfficeWrapper
   Implements IOfficeWrapper

   Private docOrsheet As Object

   Public Sub New(ByRef docOrsheet As Object)
      Me.docOrsheet = docOrsheet
   End Sub

   Public Sub CheckSpelling() Implements IOfficeWrapper.CheckSpelling       docOrsheet.CheckSpelling()
   End Sub

   Public Sub SaveAs(ByVal fileName As String) Implements IOfficeWrapper.SaveAs
   End Sub

End Class

And in the other file:

Option Strict On
Public Interface IOfficeWrapper
   Sub CheckSpelling()
   Sub SaveAs(ByVal fileName As String)
End Interface

Public Sub CheckSpellingAndSave(ByVal officeOjbect As Object)
   Dim officeWrapper As IOfficeWrapper = New OfficeWrapper(officeOjbect)
End Sub

This of course allows you to make the following two calls just fine:


What do you gain from this though? In my opinion you don't gain enough. Yes you get IntelliSense support when working of IOfficeWrapper and yes you are only allowed to call known methods of IOfficeWrapper, BUT you can pass ANY object into IOfficeWrapper and you won't see a problem until runtime!


The following is the code I wrote using delegates to avoid using Option Strict Off

Public Delegate Sub CheckSpelling()
Public Delegate Sub SaveAs(ByVal fileName As String)

Public Sub CheckSpellingAndSaveDelegates(ByVal checkSpellingSub As CheckSpelling, ByVal saveSub As SaveAs)

End Sub

CheckSpellingAndSaveDelegates(New CheckSpelling(AddressOf worksheetObject.CheckSpelling), New SaveAs(AddressOf worksheetObject.SaveAs))
CheckSpellingAndSaveDelegates(New CheckSpelling(AddressOf documentObject.CheckSpelling), New SaveAs(AddressOf documentObject.SaveAs))
' this line doesn't compile because database doesn't implement CheckSpelling!

' compile time error rather than runtime!
CheckSpellingAndSaveDelegates(New CheckSpelling(AddressOf databaseObject.CheckSpelling), New SaveAs(AddressOf databaseObject.SaveAs))

I prefer this solution because it forces the developer the think about what methods they are going to pass to the CheckSpellingAndSaveDelegates to handle the CheckSpelling part. If someone just does blind copy and paste and changes the object it won't compile.


To be fair I think Danijel's article was more about an interesting use of Option Strict than the particular solution given. I still haven't been sold on Duck Typing though and as long as I can find reasonable solutions with static typing I think I'll stay away from ducks. This was also my first time touching Visual Basic since 1998 so it is possible that I have done something terribly egregious.