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)
officeOjbect.CheckSpelling()
officeOjbect.SaveAs(FileName)
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
docOrsheet.SaveAs(fileName)
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)
officeWrapper.CheckSpelling()
officeWrapper.SaveAs(FileName)
End Sub
This of course allows you to make the following two calls just fine:
CheckSpellingAndSave(worksheetObject)
CheckSpellingAndSave(documentObject)
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!
Delegates
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)
checkSpellingSub()
saveSub(FileName)
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.
Conclusion
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.