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

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

Related

Enumeration vs Interface for constants

Is it a good design practice to change an enumeration type:
Public Enum EnumerationType
EnumerationMember1
EnumerationMember2
End Enum
To an interface base with implementor classes (even if they are empty)?
Public Interface IEnumerationType
End Interface
Public Class EnumerationMember1 : Implements IEnumerationType
End Class
Public Class EnumerationMember2 : Implements IEnumerationType
End Class
For extensibility purposes.
Is there any downside? Is there any case in which is preferable to use just an enum?
INTERFACES used for contracts between classes
that is when you have two non-related classes sharing some methods
Enumeration type stores special values. These are named constants. With an Enum, we replace magic constants throughout a program.
This improves code clarity,
organizes code
and makes code easier to maintain
For extensibility purposes it is better to use interface cause you will have methods properties and you can inherit the class later
Public Enum
with interface you have enumeration with advanced options so what the downsides will be ? No downside :)
It's not entire clear what you're after, but take a look at the strongly typed enum pattern.
''' <completionlist cref="EnumerationType"/>
Class EnumerationType
Private Key As String
Public Shared ReadOnly Member1 As EnumerationMember1 = New EnumerationMember1("EnumerationMember1")
Public Shared ReadOnly Member2 As EnumerationMember2 = New EnumerationMember2("EnumerationMember2")
Private Sub New(key as String)
Me.Key = key
End Sub
Public Overrides Function ToString() As String
Return Me.Key
End Function
End Class
So when you use it, it looks like an enum:
Sub Main
DoSomething(EnumerationType.EnumerationMember1)
DoSomething(EnumerationType.EnumerationMember2)
End Sub
Sub DoSomething(test As IEnumerationType)
Console.WriteLine(test.ToString())
End Sub
but you can easily add functionality to it (like explicit conversions, or a way to iterate over all members using a shared dictionary, or whatever comes to your mind).
It's typesafe, and using the magic completionlist comment will enable Intellisense support.

How do I refer to two different classes by one name in vb.net

I have two classes to handle database operations, one for MySQL (DBMySQL), and another for SQLite (DBSQLite). The user chooses which database system to use.
The functions within the classes have the same names, but are obviously slightly different to handle the variations in databases.
I would like to refer to the chosen class by one name throughout the application. I have set a global variable DB.
In a procedure I can: Dim DB as New DBMySQL (or DBSQLite). This works within the procedure, but not globally, but I can see all the functions when coding.
If I instead use: DB = New DBMySQL, this works globally, but no class functions are displayed when coding.
Any alternatives?
Use the concept of inheritance, and create a MustInherit class with MustOverride methods and/or properties.
Public MustInherit Class AbstractDB
Public MustOverride Function MyQuery(input As Object) As Object
Public MustOverride Sub MyUpdateMethod(input As Object)
End Class
Public Class DBMySQL
Inherits AbstractDB
Public Overrides Function MyQuery(input As Object) As Object
End Function
Public Overrides Sub MyUpdateMethod(input As Object)
End Sub
End Class
Public Class DBSQLite
Inherits AbstractDB
Public Overrides Function MyQuery(input As Object) As Object
End Function
Public Overrides Sub MyUpdateMethod(input As Object)
End Sub
End Class
Then, when you want to use your classes, make your DB global variable of type AbstractDB. You could then create either DBMySql or DBSQLite and assign it to your DB variable. The method names will all be the same, because they all inherit the same base class. But each derived class must fill out the content of those methods on its own.
Dim DB as AbstractDB = New DBMySQL
You could also use an interface.
Public Interface IRepository
' common functions of MySQL and SQLLiteclasses
End Interface
Public Class MySQLRepository
Implements IRepository
End Class
Public Class SQLLiteRepository
Implements IRepository
End Class
Public Function GetDB(userChoice As String) As IRepository
If userChoice = "MySQL" Then
Return New MySQLRepository()
Else
Return New SQLLiteRepository
End if
End Function
Dim DB As IRepository = GetDB(userChoice)
This is a basic implementation of the Repository pattern. The example in the link is in C#, but, as you're probably aware, it's not easy finding examples in VB. Fortunately, there are lots of C# to VB converters.
The abstract example Sean Skelly gave should also work. You may want to research the difference between abstract classes and interfaces.

Implementing Inherited Interface with Overloaded Member vb.net

I am trying to implement a derived interface in a class. My interfaces and class are similar to the following. The Namespaces relate to different projects that hold these interfaces and the class:
Namespace ns1
Public Interface IParent
Function myFunction() As Double
End Interface
End ns1
Namespace ns2
Public Interface IDerived
Inherits ns1.IParent
Overloads / Shadows Function myFunction(ByRef myObject as Object) As Double
End Interface
End ns2
Namespace ns3
Public Class myClass
Implements ns2.IDerived
Public Function myFunction(ByRef obj as Object) As Double Implements ns2.IDerived.myFunction
End Function
End ns3
In the derived interface, I am trying to overload the function in a way that when I implement the derived interface, I only have to implement the function as defined therein - as is done with the code above on "myClass". However, I am getting an error saying I have to also implement the function from the parent interface (with the empty argument list). The error exists regardless of my using Overloads or Shadows on the function in the derived interface - both cause the error.
Is there anyway to accomplish what I am trying to do - implement only the derived interface's function in my class - using interfaces? If there is not a way using interfaces, can anyone suggest an alternate way? We really need to use interfaces and are trying to avoid using classes. That said, abstract classes my allow us to do all we need to do with these.
I have read a lot of info on all the topics covered by this question as every concept is pretty basic and well covered in online help. But, I have not found anything that I recognize as a direct solution to my specific issue.
Thanks in advance for any help.
I don't know if this is a typo but you have two distinct methods: one that takes no parameter, and another that takes an object, so the compiler requirement is legitimate.
If this is a typo and that you have only one method, say "myFunction()", then I fear VB.Net does not act like C# by simply hiding the base interface and allowing to only implement the derived one.
But you could easily fix this by forwarding:
Namespace ns1
Public Interface IParent
Function myFunction() As Double
End Interface
End Namespace
Namespace ns2
Public Interface IDerived
Inherits ns1.IParent
Function myFunction() As Double
End Interface
End Namespace
Namespace ns3
Public Class Class1
Implements ns2.IDerived
Public Function myFunction() As Double Implements ns2.IDerived.myFunction
Return 42
End Function
Private Function myFunction1() As Double Implements ns1.IParent.myFunction
Return myFunction()
End Function
End Class
End Namespace
Module Module1
Sub Main()
Dim cp As ns1.IParent = New ns3.Class1
cp.myFunction()
Dim cd As ns2.IDerived = New ns3.Class1
cd.myFunction()
End Sub
End Module
EDIT:
So was not a typo, here is the standard (good/best practice?) fix:
Public Class Class1
Implements ns2.IDerived
Public Function myFunction(ByRef obj As Object) As Double Implements ns2.IDerived.myFunction
End Function
Public Function myFunction() As Double Implements ns1.IParent.myFunction
Throw New NotImplementedException("'ns1.IParent.myFunction' has not been implemented because unicorns can't fly!")
End Function
End Class
I don't believe that what you want to accomplish is possible the way you are trying... As I recall when you inherit an Interface any class that implements your derived Interface is actually being told that it must implement both Interfaces rather allowing the options you have in a full class.
So effectively what you have in myClass is:
Namespace ns3
Public Class myClass
Implements ns2.IDerived
Implements ns1.IParent
Public Function myFunction(ByRef obj as Object) As Double Implements ns2.IDerived.myFunction
End Function
End ns3
So inheriting an interface is really just a way to enforce that a class implementing the derived interface must also implement the base interface.

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.

Is there a way to set a Module to behave like a static class?

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... ;) )