Tuesday, October 18, 2005

Modules in Visual Basic .NET

If you are a Visual Basic 6 programmer getting your feet wet with Visual Basic .NET, you would certainly go through an unavoidable phase which involves a mixture of happiness and sadness - the pain of finding some feature of VB6 not being supported in VB.NET and the happiness of finding that some of your other favourite features are still there. If you loved writing your functions in modules when you used to program in VB6, you can rest assured that the VB.NET team has gone some length to keep you happy.

In case you never programmed in VB6, you would be wondering what a module is. Its a convenience offered by VB6 to write functions (you can have variables in a module too). You can access the functions (or variables) in a module just like calling global functions (or variables) i.e. by omitting the module name as prefix.

Please note that the CLR has no notion of a module. Then how come we still have modules in VB.NET? In a moment we will see how the VB.NET team manages to keep the CLR as well as the VB6 programmer happy.

Let us create a module as follows :

Public Module TestModule
Public Sub TestFunction()
Console.WriteLine("Test function of TestModule called !")
End Sub
End Module

You can call the TestFunction from outside the module in two ways:
- TestModule.TestFunction()
- TestFunction

The first method would give us some clue as to what happens to a module when it is compiled. Yes, you are right, it will be compiled into a class and all of its members will be Shared (static). But wait, if that is the case then how is it possible to make the function call in the second way? The VB.NET team has more tricks in their bag it seems. To make it possible to call the TestFunction as if it were a global function, an attribute is applied to it - Microsoft.VisualBasic.CompilerServices.StandardModuleAttribute. For the casual programmers this completes the illusion - the VB6 module is still there in VB.NET. The rest of us who does not believe in magic, would look under the hood and find the truth. For this purpose you will need to find a good friend in ILDASM (or a similar tool, if you so prefer).

Here is the declaration of our TestModule in IL :

.class public auto ansi sealed TestModule
extends [mscorlib]System.Object

From this declaration that we find that our module has been compiled to a class TestModule which is sealed (means NotInheritable). Thus you cannot use a module as a base class. Although not obvious from this declaration, we should be aware of two things :
- all members are implicitly Shared (this can be verified by looking at the IL for the function). You cannot explicitly declare a member as Shared, it will result in a compilation error.
- you cannot create an instance of this class.

But if you think about it again, the class is NotInheritable (sealed) and you cant create an instance of it either, which means that the class should be MustInherit (abstract). A class cannot be NotInheritable and MustInherit at the same time, but then how do we explain this apparent paradox ? The compiler pulls a trick here. The class isnt made abstract, instead the compiler just "forgets" to add a default constructor. We know that if we dont define any constructor for a class, the compiler will automatically add one for you (the default constructor). In the case of Modules, VB.NET wont allow you to write a constructor manually. Neither will it add a constructor automatically for you when it compiles the module. It is the absence of the constructor that prevents the creation of an instance of a Module.

If you are a C# programmer, you would have but one doubt - how can you use a VB.NET Module from C#?
If you expose a Module from a VB.NET library and use it in a C# project, we wouldnt have any problem if we just call the functions like we are calling static functions declared in a class. We are forced to use the Type prefix (the name of the Module) unlike in VB.NET where we could simply invoke the function by omitting the module name. We cant create an instance of the the Module, just like in VB.NET.

If you think that all is well with Modules, then you havent heard the full story. If we have 2 modules in your project and both contain functions with same name and signature, then we are forced to prefix the Module name to resolve the ambiguity. This issue is caught by the compiler and is not much of a problem. But you we could get in trouble if there are two functions of same name and signature in two modules which reside in different assemblies. The compiler would look in the modules of the current assembly for a match before it would look in external assemblies. This makes perfect sense but can catch us off gaurd if we arent aware of the possiblity, especially if multiple developers work on the same module. It would be better to prefix the Module name to prevent such problems - we might end up calling a function in a Module in the local assembly when we really wanted to call a function residing in a Module in an outside assembly.


Post a Comment

Subscribe to Post Comments [Atom]

<< Home