Is there a way to set a Module to behave like a static class? - vb.net

This questions is for VBers, it's irrelevant in C#.
In VB, when you create a module, all it's functions and members are available in the scope without need to type the module name, just like all the VB functions (Rnd, Mid, IIf etc.).
I want to create a module but I should have to explicitly write it's name to access it's members, i.e. it shouldn't be loaded to the scope like a namespace.
Update
For example, I have a Module of extension methods, I don't want all it's members to show up on the scope and in the intellisense.
I want it to be available only by instance.ExtensionMethod().
Any ideas?

If you create a Class instead of a Module then VB.NET will insist you use the class name. For example:
Public MustInherit Class Utils
Public Shared Function Sqr(ByVal arg As Double) As Double
Return arg * arg
End Function
End Class
...
Dim result As Double = Utils.Sqr(42) 'Utils required
It is hardly necessary, but you can prevent anyone from inheriting this class by adding a private constructor.
Update
To avoid extension methods from polluting the global namespace in IntelliSense. I found a rather unexpected workaround for this:
Imports System.Runtime.CompilerServices
Imports System.ComponentModel
<EditorBrowsable(EditorBrowsableState.Never)> _
Module Extensions
<Extension()> _
Public Sub Method(ByVal obj As ExampleClass)
End Sub
End Module

You could just add it to another namespace. I.e. if you want to call Foo.Bar and you have a module called FooModule, put it in a namespace called Foo.
Or... just have a regular class with a bunch of shared methods.

Just add a namespace around the module:
Namespace MyModule
Module MyModule
Sub MyMethod()
End Sub
End Module
End Namespace
(Oh, and I'm not a VB:er... ;) )

Related

Is a Module really identical to a SharedMembers-NotInheritable-PrivateNew Class?

I can read a lot over the Internet that VB.Net Modules are the same thing as c#.Net Static Classes. I can also read that something close to a Static Class is a class which would look like this:
'NotInheritable so that no other class can be derived from it
Public NotInheritable Class MyAlmostStaticClass
'Private Creator so that it cannot be instantiated
Private Sub New()
End Sub
'Shared Members
Public Shared Function MyStaticFunction() as String
Return "Something"
End Function
End Class
I find this code heavy to draft, and to read. I would be much more confortable just using a Module like this:
Public Module MyEquivalentStaticClass
Public Function MyStaticFunction() as String
Return "Something"
End Function
End Module
However, with a Module you loose one level of Namespace hierarchy, and the following 3 statements are equal:
'Call through the Class Name is compulsory
Dim MyVar as String = Global.MyProject.MyAlmostStaticClass.MyStaticFunction()
'Call through the Module Name is OPTIONAL
Dim MyVar as String = Global.MyProject.MyEquivalentStaticClass.MyStaticFunction()
Dim MyVar as String = Global.MyProject.MyStaticFunction()
I find this very inconvenient and this either pollutes the Intelisense, or forces me to create additionnal levels of Namespace, which then means more Module declaration, i.e., more Intelisense pollution.
Is there a workaround or is this the price to pay if you want to avoid the heavy SharedMembers-NotInheritable-PrivateNew Class declaration?
Additionnal references include the very good post by Cody Gray: https://stackoverflow.com/a/39256196/10794555
No, there is no exact equivalent to a C# static class in VB.NET. It would be nice if VB had the ability to add the Shared modifier to a class declaration, like this:
Public Shared Class Test ' This won't work, so don't try it
' Compiler only allows shared members in here
End Class
But, unfortunately, it does not. If you do that, the compiler gives you the following error:
Classes cannot be declared 'Shared'
That leaves us with the two options you listed:
Either you make a non-instantiable class containing only Shared members (without the safety of that rule being enforced by the compiler), or
Use a Module, which makes everything Shared, even though you don't explicitly say so via the Shared modifier
As you said, many people don't like the loss of the class name being required, as a sort-of extra namespace layer, so they prefer the Class with only Shared members over the Module. But, that's a matter of preference.
It's worth noting that, while you don't have to specify the module name everywhere you call its members, you can always do so if you wish:
MyModule.MyMethod()
While a "SharedMembers-NotInheritable-PrivateNew Class", as you so eloquently called it, is the closest approximation to a static class, it's only functionally equivalent. If you use reflection, you'll see that the attributes of the type are not the same. For instance, in VB:
Module MyModule
Public Sub Main()
Dim t As Type = GetType(MyClass)
End Sub
End Module
Public NotInheritable Class MyClass
Private Sub New()
End Sub
Public Shared Sub MyMethod()
End Sub
End Class
If you take a look at t.Attributes, you'll see that it equals Public Or Sealed. So the MyClass type is both sealed (NotInheritable) and public. However, if you do this in C#:
class Program
{
static void Main(string[] args)
{
Type t = typeof(Test);
}
}
public static class MyClass
{
public static void MyMethod()
{ }
}
And you inspect the t.Attributes again, this time, the value is Public | Abstract | Sealed | BeforeFieldInit. That's not the same. Since you can't declare a class in VB as both NotInheritable and MustInherit at the same time, you have no chance of exactly duplicating that thing. So, while they more-or-less are equivalent, the attributes of the types are different. Now, just for fun, let's try this:
Module MyModule
Public Sub Main()
Dim t As Type = GetType(MyModule)
End Sub
End Module
Now, the t.Attributes for the module are Sealed. That's it. Just Sealed. So that's not the same either. The only way to get a true static class in VB (meaning, the type has the same attributes when inspected via reflection) is to write it in a C# class library, and then reference the library in VB.
I would be much more confortable just using a Module
So use a Module.
Module SomeModuleNameHere
Public Function MyStaticFunction() As String
Return "Something"
End Function
End Module
You don't need Global.MyProject or the Module name at all, just call your function directly, from anywhere:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim x As String = MyStaticFunction()
Debug.Print(x)
End Sub
But if you want to, you can use the Module name, without the Global part:
Dim x As String = SomeModuleNameHere.MyStaticFunctions
The only time you must use the Module name, however, is if you have two functions with the exact same name in different modules. Then you'd have to differentiate them by using their fully qualified names.
From all the discussions held so far, and thanks to the input by Steven Doggart and comments by TnTinMn, I have come to conclude with the following broad feedbacks and guidelines.
Nota: This post refers to 'Static' Classes, whilst the Static keyword is used for C#.Net, not VB.Net. The VB equivalent is Shared, but Shared Classes are not permited with VB (only the Members). The guidelines described below are tentatives to achieve in VB something close to a C# Static Class.
Since such VB Classes cannot be Shared, they are described as 'Static'.
Nota bis: In all the examples, I purposely added a layer of Namespace (consistently called "MySpace") so that there is no confusing as to in which Namespace layer the examples sit: they are all in the MySpace layer. The MySpace layer is not compulsory and can be stripped out depending on your needs.
In general
Use a Module but do not rely on the Module name as a Namespace layer. Rather, fully integrate the path in a Namespace declaration, such as:
Namespace MySpace.MyStaticClass
Module _Module
Function MyStaticFunction()
Return "Something"
End Function
End Module
End Namespace
Then the Static 'Members' should be accessed via Global.MyProject.MySpace.MyStaticClass.MyStaticFunction()
Nota: Part of the Namespace path can be stripped depending on where
you are located. Usually, MySpace.MyStaticClass.MyStaticFunction()
will be sufficient.
Nota bis: Using _Module as the Module name will
reduce the appereance of the Module in the Intelisense dropdown, and
yet make it crystal clear this is a Module.
When wishing to encaspulate Static Classes
Under such context the general above-mentionned style would produce:
Namespace MySpace.MyStaticClass
Module _Module
Function MyStaticFunction()
Return "Something"
End Function
End Module
End Namespace
Namespace MySpace.MyStaticClass.MyStaticSubClass1
Module _Module
Function MyStaticFunction()
Return "Something"
End Function
End Module
End Namespace
Namespace MySpace.MyStaticClass.MyStaticSubClass2
Module _Module
Function MyStaticFunction()
Return "Something"
End Function
End Module
End Namespace
This can quickly be heavy in the sense that it requires a separate Namespace declaration for each 'encapsulated' 'Static Class'. Disadvantages include:
Heavy review because understanding the Namespace architecture/arborescence will be less intuitive: in the above example that would mean checking all the declaration which include 'MyStaticClass'.
Heavy drafting because of the additionnal Namespace declarations.
Heavy maintenance because changing a parent Namespace will require a change in several Namespace declarations: in the above example that would mean changing 'MyStaticClass' 3 times. (Right-Click/Rename is your best friend here)
An alternative is to use encapsulated Classes with Shared members:
Namespace MySpace
Public Class MyStaticClass
Public Function MyStaticFunction()
Return "Something"
End Function
Public Class MyStaticSubClass1
Public Shared Function MyStaticFunction()
Return "Something"
End Function
End Class
Public Class MyStaticSubClass2
Public Shared Function MyStaticFunction()
Return "Something"
End Function
End Class
End Class
End Namespace
Nota: As Steven Doggart pointed out in a separate post, people usually import Namespaces, but do not import Classes, so encapsulating Classes will usually "force" the reliance on the full path across encapsulated Classes : MyStaticClass.MyStaticSubClass1.
You cannot encapsulate a Module within another Module, but you could always use a mixture of a Module in which you encapsulate one or several Classes and Sub-Classes. The example below achieves something similar to the above example:
Namespace MyStaticClass
Public Module _Module
Public Function MyStaticFunction()
Return "Something"
End Function
Public Class MyStaticSubClass1
Public Shared Function MyStaticFunction()
Return "Something"
End Function
End Class
Public Class MyStaticSubClass2
Public Shared Function MyStaticFunction()
Return "Something"
End Function
End Class
End Module
End Namespace
When publishing a Class Library (DLL)
If your final product is a DLL you intend to share with a broader audience, it is recommended to put safety nets around your 'Static' Classes. Although this will not affect how the compiler will see your code, it will prevent someone else from making mistakes, or at least quickly trigger errors and assist debugging swiftly:
Make the Class NotInheritable, so that no one tries to derive a Class from a Static Class: it is typically useless to derive such Classes.
Make the New Creator statement Private, so that no one tries to instantiate the Class: the Static Class should not include any non-Static (Shared) members; if so, that is a typo and trying to instantiate the non-Shared Member will likely bring problems.
The example below achieves something similar to the above examples:
Namespace MySpace
Public NotInheritable Class MyStaticClass
Private Sub New()
End Sub
Public Function MyStaticFunction()
Return "Something"
End Function
Public NotInheritable Class MyStaticSubClass1
Private Sub New()
End Sub
Public Shared Function MyStaticFunction()
Return "Something"
End Function
End Class
Public NotInheritable Class MyStaticSubClass2
Private Sub New()
End Sub
Public Shared Function MyStaticFunction()
Return "Something"
End Function
End Class
End Class
End Namespace
When dealing with an Extension
A <System.Runtime.CompilerServices.Extension()> can only be declared within a Module block. However the Module Name has no impact on the Extension so this topic is not really relevant here.
See link provided by Peter Macej: https://learn.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/procedures/extension-methods

vb.net is it possible to extend a system class?

I want to add a function to System.IO.Path Class.
The Problem is Path is NotInheritable.
So doing that is impossible:
Imports System.IO
Public Class Path : Inherits System.IO.Path
End Class
You can use Extension Methods. Those seem to be the functions of the extended class, but there is no real connection between them. They do make programming a bit more convenient though.
http://msdn.microsoft.com/en-us/library/bb384936.aspx
Update: Let's see an example. Note that these are snippets from 3 files.
'Noninheritable baseclass
Public NotInheritable Class BaseClass
Function f()
Return 42
End Function
End Class
'Extension
Imports System.Runtime.CompilerServices
Module ExtModule
<Extension()>
Public Sub Print(ByVal bc As BaseClass)
Console.WriteLine(bc.f())
End Sub
End Module
'Usage
Sub Main()
Dim bc As BaseClass
bc = New BaseClass()
bc.Print() 'Calling the extension method
End Sub
And it says 42.
The problem is not in inheriting a "system" class, but a NonInheritable one, which are by design and as the name suggests, not inheritable.
Why would you want to inherit it anyway? You extend (inherit) a class in order to add behavior to its instances. In the case of Path there are no instances, Path is just a "container" to group similar methods that deal with filesystem path manipulation.
What you should do is to create a Module for your additional methods or a NotInheritable class of your own.

Making extension method local to the file scope

Currently I use the following hack to make my extensions methods local to a
file by hiding it in a uniquely named namespace. It doesn't really hide it
but as long as nobody else imports the namespace then it's cool. However
the pattern is ugly. The below code is just to ilustrate the extension method
trick. Don't try to draw any conclusions about what I'm doing with ISubject ;)
MyFancyComponent.cs
--
Import MyFancyComponentMixins
Namespace MyFancyComponentMixins
Module X
<Extension>
Public Function Format(s As ISubject(Of Double)) as ISubject(Of String)
Return s.LocalizeLengthUnits().
Select(
parser:=Function(x) Double.Parse(x),
formatter:=Function(x) x.ToString("0.00"))
End Function
End Module
End Namespace
class MyFancyComponent
Inherits ReactiveUserControl(of MyFancyViewModel)
Public Overrides Iterator Function MakeReactiveBindings() as IEnumerable(of IDisposable)
Yield Me.ViewModel.
PropertySubject(Function(x) x.Height).
Format().
BindToControl(Me.HeightTextBox)
Yield Me.ViewModel.
PropertySubject(Function(x) x.Width).
Format().
BindToControl(Me.WidthTextBox)
End Function
end class

Is this possible to set Attributes in an Class Extension?

Is this possible to use <Attributes> on a method in a Class extension ?
Here is a Class A
Public Class Goblin
Inherits Monster
Enum goblinsRole
Chief
Grount
End Enum
Public Property name As String
Public Property role As goblinsRole
Private healthPoints As Integer
Public Sub New(into As Integer)
healthPoints = into
End Sub
Public Sub hitBy(damage As Integer)
<...>
End Sub
<ValidationMethod()>
Public Function checkByTheWitchDoctor()
<...>
End Function
<ValidationMethod()>
Public Function isAlive()
<...>
End Function
End Class
Here is an extension of this class A
Module ModuleExtension
<ValidationMethod()>
<Extension()>
Public Sub PrintDateOfDeath(ByVal aGoblin As DomainModelFake.Goblin)
<...>
End Sub
<Extension()>
<ValidationMethod()>
Public Function smashedByAGrount(ByVal aGoblin As DomainModelFake.Goblin) As Boolean
<...>
End Function
End Module
When I use reflection to get the methods that are tagged with this attribute, I only get the list of methods that are located in the Class A definition without those that are located in the extension.
In extenso, it means that I only see :
checkByTheWitchDoctor()
isAlive()
but i do NOT see :
PrintDateOfDeath()
smashedByAGrount()
is this a normal behavior of reflection ? It doesn't search into the extensions ?
I am assuming that you try to find all methods of the Goblin class, and if that is the case, then yes, this is the normal behavior of reflection.
Extension methods are not a member of the type they are designed for. If you do Type.[GetType]("Goblin").GetMethods(), you will only get the methods defined there.
Extension methods are nothing but static (Shared in VB) methods that are decorated with a special attribute. So if you want to find those, you need to get the methods of ModuleExtension.
If you do not know the name of ModuleExtension at runtime, you could try looking for all static types in the assembly, and search those for static methods marked with an ExtensionAttribute and accepting a first parameter of type Goblin.

Can we declare a class in two files in vb.net

Say some function is defined in one file and another function is defined in another file
In C++ you can do that right
You have to create partial class. That way you may splits the definition of class into two or more files.
You can use Partial classes msdn link.
This means you can do something like this:
Partial Public Class partialClass
Public Sub method1()
End Sub
End Class
Partial Public Class partialClass
Public Sub method2()
End Sub
End Class
BTW, this is not the same you can do in C++. In C++, you can separate function declaration from the implementation. In VB, you can separate methods and properties, but you can't declare a function in one partial class and implement it in the another partial class