Sub to create object of type passed by caller in VB.NET - vb.net

I'm having trouble creating a sub that can create objects of a variable type on the fly. Here's an example of what I'm trying to achieve:
class systemSettings
'some properties
end class
Class fireSystemSettings
inherits systemSettings
'some additional properties
end class
Class windSystemSettings
inherits systemSettings
'some additional properties
end class
sub createSystem(systemType as Type, arg1 as object, arg2 as object)
Dim newSystem as New systemType(arg1, arg2)
systemCollection.add(newSystem)
end sub
I can't get it to work. I've done a fair bit of research, and looked at generic types, reflection, and other tools, but I'm having trouble determining how best to tackle this problem.

You're looking for Activator.CreateInstance(systemType)

Use generics for this:
Sub createSystem(Of T As {New, systemSettings})()
Dim newSystem As New T
systemCollection.add(newSystem)
End Sub
And call it with:
createSystem(Of windSystem)
To explain:
The term Of T lets you create a method that can be used for any type. Every time you call it for a new value of T, a new method is created in memory.
The term As {New, systemSettings} constrains T. It says that T must represent a type that is or derives from systemSettings. It also says that T must contain a default constructor: New() which is required for the command New T. Note that you cannot specify a more elaborate constructor as a generics constraint.
If you require parameters in your constructor, you can create an Initialise method in the base class. Because T is constrained to systemSettings, it is guaranteed that the Initialise method exists.
Class systemSettings
Public Overridable Sub Initialise(arg1 As Object, arg2 As Object)
'initialise properties
End Sub
'some properties
End class
Class fireSystemSettings
Inherits systemSettings
Public Overrides Sub Initialise(arg1 As Object, arg2 As Object)
'initialise properties
End Sub
'some additional properties
End Class
Class windSystemSettings
Inherits systemSettings
Public Overrides Sub Initialise(arg1 As Object, arg2 As Object)
'initialise properties
End Sub
'some additional properties
End Class
Sub createSystem(Of T As {New, systemSettings})(arg1 As Object, arg2 As Object)
Dim newSystem As New T
newSystem.Initialise(arg1, arg2)
systemCollection.add(newSystem)
End Sub

Related

Creating Generic Definition For Singleton Objects/Forms

I'm tying to create a generic solution for instantiating my forms using singleton behavior in vb.net. But it's not working anyway and always protecting me to compile:
Public Class SingletonGenerator(Of TForm)
Private _inst As Object
Public ReadOnly Property Instance As SingletonInstance(Of TForm)
Get
If _inst Is Nothing Then
_inst = New TForm()
End If
Return _inst
End Get
End Property
End Class
But this error restricts me to continue:
Error 9 'New' cannot be used on a type parameter that does not have a 'New' constraint.
And I'm not sure if I replace my code with New Form() it works as expected (because it create objects of parent form() class and may loose some initialization in child class.)
Can somebody please explain why this happen or how can I have singleton instances of objects in an OOP way which not require to copy/paste those common lines of code which are used in singleton on every new defined class?
You have to convince the compiler that the TForm type in fact has a parameterless constructor so that New TForm() can never fail. That requires a constraint.
Not the only thing you need to do, a Form object becomes unusable when it is closed. And you'll have to create another one to re-display it. Failure to do so causes an ObjectDisposedException at runtime. In other words, you should be interested in the Disposed event. That requires a further constraint, the TForm type parameter always needs to derive from Form. Required to convince the compiler that it is okay to use the event. Adding it up:
Public Class SingletonGenerator(Of TForm As {Form, New})
Private _inst As TForm
Public ReadOnly Property Instance As TForm
Get
If _inst Is Nothing Then
_inst = New TForm()
AddHandler _inst.Disposed, Sub() _inst = Nothing
End If
Return _inst
End Get
End Property
End Class
Do be a bit careful with this, you are painting yourself into a corner. You can only ever use this code to create form objects whose constructor takes no argument. In practice you may find they often need one.
Check this code:
Module Startup
Public Sub Main()
Dim f As Form = FormsManager.Instance.GetForm(Of Form1)()
f.ShowDialog()
Dim f1 As Form = FormsManager.Instance.GetForm(Of Form1)()
f1.ShowDialog()
End Sub
End Module
Public Class FormsManager
Private Shared _formsManager As FormsManager
Private _forms As List(Of Form)
Public Shared ReadOnly Property Instance As FormsManager
Get
If (_formsManager Is Nothing) Then
_formsManager = New FormsManager
End If
Return _formsManager
End Get
End Property
Private Sub New()
If _forms Is Nothing Then _forms = New List(Of Form)
End Sub
Public Function GetForm(Of T As {Form, New})() As Form
Dim f As Form = _forms.Where(Function(o) o.GetType = GetType(T)).SingleOrDefault
If f Is Nothing Then
f = New T
_forms.Add(f)
End If
Return f
End Function
End Class
This is what I finally produced (a generic singlton forms generator):
Imports System.Windows.Forms
Imports System.Runtime.CompilerServices
<HideModuleName()> _
Public Module SingletoneForms
<Extension> _
Public Function GetInstance(Of TForm As {Form, New})(ByRef obj As TForm) As TForm
Return SingletonForm(Of TForm).Instance
End Function
Public Class SingletonForm(Of TForm As {Form, New})
Private Shared WithEvents _inst As TForm
Public Shared Property Instance As TForm
Get
If _inst Is Nothing Then
SetInstance(New TForm())
End If
Return _inst
End Get
Set(value As TForm)
SetInstance(value)
End Set
End Property
Private Shared Sub SetInstance(ByVal newInst As TForm)
If _inst IsNot Nothing Then
RemoveHandler _inst.FormClosing, AddressOf FormClosing
End If
_inst = newInst
AddHandler _inst.FormClosing, AddressOf FormClosing
End Sub
Private Shared Sub FormClosing(sender As Object, e As FormClosingEventArgs)
If e.CloseReason = CloseReason.UserClosing Then
e.Cancel = True
_inst.Hide()
Else
_inst = Nothing
End If
End Sub
End Class
End Module
and call it simply this way:
frmMain.GetInstance().Show()
Form1.GetInstance().Show()
Form1.GetInstance().Hide()
Form2.GetInstance().ShowDialog()

VB.NET constructors in derived classes

I have a base class that I use with reflection to fill the fields of the derived classes, reading from the database.
Public MustInherit Class DaoBase : Implements IEquatable(Of DaoBase)
Sub New()
' Empty, hate to have it
End Sub
Sub New(reader As DbDataReader)
' Reads the DB and fills the instance fields
End Sub
' More stuff...
End Class
The derived classes usually have a non-default constructor to set its fields:
Public Class Customer
Inherits DaoBase
Public Sub New(
id As Integer,
description As String)
Me.id = id
Me.description = description
End Sub
End Class
Questions:
1) I don't like to have the empty constructor in the base class. It sits there unused and could create an object in an incorrect state. If I remove it, then the compiler gives an error because, missing the default constructor, the derived class constructor should call the only-one base class constructor.
2) I can't do new Customer(myReader) because that constructor is not in the derived class, even if it's in the base class. I have to explicitly declare it, which I don't like.
Public Class Customer
Inherits DaoBase
Public Sub New(
id As Integer,
description As String)
Me.id = id
Me.description = description
End Sub
Public Sub New(reader As DbDataReader)
MyBase.New(reader)
End Sub
End Class
If your base class is filling fields in the derived class, it sounds like you should be using an interface instead of what you're doing.
As for your questions, just because you don't like it doesn't make it wrong. But as one comment said, if you change the second New to:
Sub New(Optional reader as DbDataReader = Nothing)
then you fulfill the requirement to have an empty constructor and you can have it do the right thing when no reader is given.

MustOverride turns out as a Virtual Method?

While researching Assembly.GetInterfaces(), I found the method was a MustOverride method. Which in my understanding means it has no default action to derived classes. Its just a signature basically, an abstract method. Yet, I can still use it on a type and it will return all implemented interfaces without writing any code for the MustOverride method.
Where is this code that has slipped into the MustOverride method? Have I somehow indirectly overridden it just simply by calling the method on a created type?
This question is purely on the basis of study and discovery, I am not trying to do anything other than understand the confines of the language.
Here is the code I used:
Public Class Form1
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim t As Type
Debug.WriteLine(GetType(Integer))
t = GetType(Integer)
Dim interfaceArr As Type() = t.GetInterfaces
For i As Integer = 0 To interfaceArr.Length - 1
Debug.WriteLine(interfaceArr(i))
Next
End Sub
End Class
Output Is:
System.IComparable
System.IFormattable
System.IConvertible
System.IComparable 1[System.Int32]
System.IEquatable 1[System.Int32]
Any MustOverride method can always be called on an instance of any type because you couldn't possibly create an instance of a class unless the class provides concrete implementations of all of the MustOverride methods. In this case, your confusion is that you are assuming that the t variable is referencing a Type object, but that is not the case. Since Type is a MustInherit class, it's impossible to ever instantiate an object of that type directly. You could only ever instantiate an object of a class that derives from Type. If you use the debugger to inspect the T variable, you will notice that it is actually referencing an instance of the RuntimeType class, which is an undocumented class which obviously derives from Type.
For instance, consider this example, which duplicates the behavior:
Public Class Form1
Public MustInherit Class BaseClass
Public MustOverride Function GetGreeting() As String
End Class
Public Class DerivedClass
Inherits BaseClass
Public Overrides Function GetGreeting() As String
Return "Hello world"
End Function
End Class
Public Function GetInstance() As BaseClass
Return New DerivedClass()
End Function
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim t As BaseClass = GetInstance()
Debug.WriteLine(t.GetGreeting())
End Sub
End Class
As you can see, the t variable is of the BaseClass type, but it's actually referencing a DerivedClass object. Therefore, even though the BaseClass class defines the method as MustOverride, you can still call it because the actual type of the object does implement it.

Constructor within a constructor

Is this a bad idea? Does calling a generic private constructor within a public constructor create multiple instances, or is this a valid way of initializing class variables?
Private Class MyClass
Dim _msg As String
Sub New(ByVal name As String)
Me.New()
'Do stuff
End Sub
Sub New(ByVal name As String, ByVal age As Integer)
Me.New()
'Do stuff
End Sub
Private Sub New() 'Initializer constructor
Me._msg = "Hello StackOverflow"
'Initialize other variables
End Sub
End Class
That's perfectly valid and a commonly used way to reuse constructor code. Only one object is instantiated.
It is a valid approach. There are some caveats with where the new function can be called:
The Sub New constructor can run only once when a class is created. It
cannot be called explicitly anywhere other than in the first line of
code of another constructor from either the same class or from a
derived class.
Read more about the object lifetime on MSDN.
Chaining constructors like this will certainly not create additional object instances.
It is desirable to only write code for a certain portion of initialization once. This is a common and valid initialization pattern.

Similar classes with different signatures

I have two classes:
Public Class Subscribing
Private _subscribingObjects As IList(Of String)
Public Sub Add(ByVal obj As SubscribeObject)
'...code...'
End Sub
Public Sub Remove(ByVal index As Integer)
'...code...'
End Sub
End Class
Public Class Providing
Private _providingObjects As IList(Of String)
Public Sub Add(ByVal obj As ProvideObject)
'...code...'
End Sub
Public Sub Remove(ByVal index As Integer)
'...code...'
End Sub
End Class
Is there a more elegant way to add do this? One class would suffice, but since the Add methods have different arguments, then one really wouldn't work.
Any help would be appreciated.
this?
Public Class SubscribingProviding(Of t)
Private _subscribingObjects As IList(Of String)
Public Sub Add(ByVal obj As t)
'...code...'
End Sub
Public Sub Remove(ByVal index As Integer)
'...code...'
End Sub
End Class
Your add functions should be fine. As long as you have different variable types being passed in you can have the function names be the same. Your remove Subs will not be allowed in the same class because it is using the same parameter Integer.
Eh.. probably not. They are different enough that you cant even Interface them.
I personally wouldn't mix the two responsibilities (of subscribing and providing) in one class. The classes themselves can easily be simplified by just inheriting from List(Of T)
Public Class Subscribing
Inherits List(Of SubscribeObject)
End Class
Public Class Providing
Inherits List(Of ProvideObject)
End Class
If you really want to get down to one class and make sure that it can only accept SubscribeObject and ProvideObject respectively, implement a common interface in both SubscribeObject and ProvideObject. Then create a generic class that accepts the interface:
' Common interface '
Public Interface ISubscribeProvideObject
End Interface
' SubscribeObject and ProvideObject both implementing the common interface '
Public Class SubscribeObject
Implements ISubscribeProvideObject
'...'
End Class
Public Class ProvideObject
Implements ISubscribeProvideObject
'...'
End Class
' Generic class accepting both types '
Public Class SubscribingProviding(Of T As ISubscribeProvideObject)
Inherits List(Of T)
'... Add() and Remove() methods only needed if custom logic applies ...'
End Class