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

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.

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

How to get the class type in a inherited shared method

Folks;
Code looks like:
Public Class MasterA
Inherits Underling
End Class
Public Class MasterB
Inherits Underling
End Class
Public Mustinherit Class Underling
Sub DoSomething()
Me.GetType 'Using the instance, I can get the class.
end sub
Shared function() as ???? 'How can I define the return type based on the class that inherited me?
'Me.GetType 'Won't work as this is a shared function with no instance 'Me'
End Function
End class
OK. The question is: is there a way to get at the class type from within a shared function that was inherited by another class?
What I'm building is an XML serializer/desrializer as an inheritable class so that classes that inherit it can be serilized to an XML file, and back again. Rather than writing a serializer/deserializer for each type of class I want to do this with, I'd like to just inherit the functionality.
To do that, though, requires that I be able to ascertain the clas that inherited me in the shared function.
You could get the desired behavior with a generic base class, my VB is a little rusty so you might find stray parens or brackets. This would really be the only way to get a type reference to an inheriting class in a shared base class function.
Public Mustinherit Class Underling(Of T)
Sub DoSomething()
Me.GetType 'Using the instance, I can get the class.
end sub
Shared function() As T
' GetType(T) should get the type at this point
End Function
End class
Public Class MasterA
Inherits Underling(Of MasterA)
End Class
Public Class MasterB
Inherits Underling(Of MasterB)
End Class
As a side note it does seem like a rather weird solution to handle XmlSerialization rather than through your own serializer implementation or XmlSerializer

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

VB.NET inheritance - do all properties in the derived classes have to be declared in the base class?

I'm refactoring, and have run into a roadblock.
Background:
I have a base class and several inherited derived classes. The derived classes don't always need to have the same properties. If any properties are shared among the derived classes, those properties would live at the base class level ('Contents', for example).
Similarly, GoodDocument below has 'GoodThings' but would not want/need to have 'BadThings'.
I want to treat instances of both 'GoodDocument' and 'BadDocument' as type 'Document'
public mustinherit class Document
public property Contents as string
public sub new()...
end class
public class GoodDocument
inherits Document
public property GoodThings as string
public sub new()...
end class
public class BadDocument
inherits Document
public property BadThings as string
public sub new()...
end class
The 'DocumentWriter' class will also have several derived classes: ('GoodDocumentWriter' and 'BadDocumentWriter').
I need to pass around the DocumentWriter.Doc as a 'Document' to a number of other places in the code. Doc.GoodThings would only be called from within an instance of either 'GoodDocument' or 'GoodDocumentWriter'.
public mustinherit class DocumentWriter
public property Doc as Document
public sub new()...
end class
public class GoodDocumentWriter
inherits DocumentWriter
public sub new
mybase.Doc = new GoodDocument
end sub
end class
public class BadDocumentWriter
inherits DocumentWriter
public sub new
mybase.Doc = new BadDocument
end sub
end class
Question:
Is there a design pattern that allows for derived classes to have members that don't exist at the base class level?
Do all properties have to live at the base class level?
Revised
I was trying to be brief with my initial question and I made the mistake of over simplifying the situation. In short, I did realize that it should be possible to have different properties on each of the derived classes. (I typed that in a tongue-in-cheek manor and didn't mean to keep it in the final post).
I realize now that the problem that I was experiencing was really symptomatic of a larger issue which needed addressing.
It appears that I was encountering compiler complaints that could be corrected by further refactoring and looser coupling. While others answered the basic question that I posed, Ryan Gross' example really helped kick start some new ideas.
Thanks!
What you should do in this case is define the operations that can be performed on instances of Document in an interface. In your case maybe there is a WriteThings operation, so you would have:
public interface Writeable {
public sub WriteThings();
}
Then in your derived classes you would implement the method to utilize the internal data of the class. For example:
public mustinherit class Document implements Writeable
public property Contents as string
public sub new()...
public sub WriteThings();
end class
public class GoodDocument
inherits Document
public property GoodThings as string
public sub new()...
public sub WriteThings()
//Do something with GoodThings
end sub
end class
public class BadDocument
inherits Document
public property BadThings as string
public sub WriteThings()
//Do something with BadThings
end sub
public sub new()...
end class
Finally, client code that needs to call WriteThings accesses it through an interface:
public mustinherit class DocumentWriter
public property Doc as Writable
public sub new()...
public sub PerformWrite()
Doc.WriteThings();
end sub
end class
It is generally not a good idea to build several parallel class hierarchies. In this case, one DocumentWriter class should be able to write any class that implements Writeable by invoking its WriteThings method.
If all the properties live at the base class level, then I'm not sure what the point of a derived class would be. :) You'd be able to do everything with the base class.
So, yes. If something is applicable only to GoodDocument and not to Document, then it should be in GoodDocument.
To answer your question specifically:
Yes, you just create multiple layers in the inheritance hierarchy: You have a base class, and then many two “branches” (good and bad, to use your terminology). Any properties that are only relevant to either branch, you declare in the class that inherits from the base class. Those properties will only be visible to that class and any classes inheriting from it.
No, properties can be declared anywhere within your inheritance hierarchy.

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