I ran into a chunk of code today that threw me.
The code was contained within a particular class definition.
Every member (properties and methods) were marked Shared.
Public Shared Sub MyMethod()
blah....
End Sub
The net effect was a class that, even if you could instantiate it, wouldn’t have any purpose as in instance. Its sole use was to provide essentially a dumping ground for a bunch of general purpose library routines.
At first, I just wrote this off as odd and continued on.
But, on further reflection, this just doesn’t seem right. As Scott Hanselman might say, it’s got a rather nasty Code Smell.
So, I did what any reasonable person would do; I googled it.
Apparently, there are more than a few people out there contemplating the same thing. What’s scary though, is that usually, the arguments end up along these lines:
- Modules in VB.Net are a throwback, a hack, and a nod to compatibility and should be avoided.
- Modules can’t be accessed from other .NET languages.
- Other .Net Languages don’t have modules, so you’re better off using shared members on a class definition.
- Modules aren’t OOP. Classes are. Use classes.
It’s already been proven that, internally, VB.Net modules compile to exactly the same code as a Class with shared methods. Further, the modules have been documented as a first class feature of VB.Net, the language, not some soon-to-be-deprecated nod to VB6 compatibility. And Modules CAN be accessed from other languages. So I’m not going to bother with any of those bits again.
Personally, I think in comes down to two simple concepts, syntactics and semantics.
The Syntactics
Syntactically, defining members in a Module allows them to be accessed without qualifying the member with the module name. So, given this Module definition:
Public Module MyModule
Public Shared Sub MyMethod()
blah....
End Sub
End Module
I can access the method MyMethod by simply invoking the name MyMethod(); I don’t have to qualify it, as in MyModule.MyMethod().
This is a minor issue, except that it effectively allows you to extend the VB language. You can add your own RIGHT() function or LEFT() function, etc. This can be a fantastic aid to code readability when used appropriately.
The Semantics
However, I believe the even bigger argument is semantics. A class, is, by its very definition, the definition of an object that can be instantiated. Even singleton classes can be instantiated at least once. But a class of shared methods is a class that has no reason to be instantiated. And that really isn’t a class, it’s just a set of loosely related, if even related at all, functions. And that is the definition of a Module!
Do you regularly create an integer type variable when what you really need is a DATE? Then why collect a bunch of shared functions in a class when a module is specifically intended for such purposes?
Discoverability
The only rational I can come up with is discoverability. Forcing developers to prefix shared methods with the class name provides a means (through Intellisense) of discovering the capabilities of that class. But, you can optionally prefix module members in exactly the same way, so again, shouldn’t we be using the features of the language as they’re intended?
Why Shared Members at all?
Given this, what benefit is there of using shared members at all? I’ve seen a few examples for defining singleton objects where you retrieve the single instance via a shared member of the class. That certainly makes logical sense, but from a coding perspective, it seems ugly to me. Why ask the class for the single available instance of itself? Shouldn’t you ask something else for that instance? It also makes for some odd-reading code:
Dim TheOneInstance = MySingletonClass.GetTheOneInstance
And other than singletons, what other purpose is there for shared members?
(UPDATE 10/29/08: After further experiments and reading, it turns out that, as far as I can tell, the ONLY way to truly get a singleton object is to use a shared method within the object itself. Nasty, but that appears to be the only way to do it).
I know the .NET framework uses them extensively, but are they used effectively?
For example, take the String class. It exposes quite a few shared members, such as Compare and Copy. But in practice, using those shared members doesn’t quite make sense. If I’m comparing two strings, the object-oriented thinker in me naturally gravitates to:
If OneString.Compare(OtherString) Then
Not
If String.Compare(OneString, OtherString) then
The bottom line is, if I’m comparing two strings or copying a string, I’ll always be working with an instance of a string, so why shouldn’t those methods be instance methods and not shared methods?
I suppose String.Format() is a passable example, but even that would seem to make more sense as:
dim x = "Insert a parameter here {0}".Format(Arg0)
Do you have a good use for shared methods that you’d like to, uh, share?
Let me know!