Accessing the same instance of a class in another form - vb.net

I'm sure this is a simple question, but I don't have enough experience to know the answer. :)
DataClass, Form1, Form2
I have a public class, DataClass, in a separate file, DataClass.vb. In DataClass I have data stored in several arrays that I need to access. I have methods in DataClass so that I can access the data. One of them is GetName. Everything works fine on Form1. I need to access the same data in the arrays on a another form, but I am required to call a new instance of the class, so when I call the methods to access the arrays, the data is empty.
I've seen some threads mention creating a singleton class, but most are about C# which I am not as familiar with.
What is the best practice?

There are many ways in which you can do this.
One of them would involve creating a Module and then making the variable that instantiates your class Public inside the module:
Module MyGlobalVariables
Public MyDataClass As DataClass
End Module
Now, all the forms in your project will be able to access the DataClass via MyGlobalVariables.MyDataClass.
A preferable method would be to add a property to your Form2 that can be set to the DataClass instance:
Public Property MyDataClass As DataClass
Then, you would instantiate your Form2 as follows (assuming the variable you use to instantiate DataClass in Form1 is called _dataClass):
Dim frm2 As New Form2()
frm2.MyDataClass = _dataClass
frm2.Show()
And finally, another way would be to override the constructor of Form2 and allow it to receive a parameter of type DataClass. Then, you could instantiate Form2 as:
Dim frm2 As New Form2(_dataClass)
Hope this helps...

You can create a singleton class like this
Public Class DataClass
Public Shared ReadOnly Instance As New DataClass()
Private Sub New()
End Sub
' Other members here
End Class
You can access a single instance through the shared Instance member which is initialized automatically. The constructor New is private in order to forbid creating a new instance from outside of the class.
You can access the singleton like this
Dim data = DataClass.Instance
But this is not possible
Dim data = new DataClass() 'NOT POSSIBLE!
Since the singleton class is accessed through the class name, you can access it from the two forms easily.

Related

What is the purpose of declaring a Class within another Class?

I come from the VBA world where options to breakdown your code into classes, namespaces, and modules is limited. Now I just landed in a world where the options are many, and I feel lost.
I would like to know what is the purpose of declaring a Class within another Class? (see example below)
Class FirstClass
Public OnePropertyInside As String
Class SecondClass
Public AnotherProperty As String
End Class
End Class
If I create a new instance of FirstClass (say myFirstClass), SecondClass is not instantiated.
Even more bizzare (to me at least), is that intelissense offers me myFirstClass.SecondClass. Obviously, because the class is not instantiated, I cannot access any of its members.
So, is that usefull only if the SecondClass contains shared members?
To try answering that question I added a shared member within SecondClass:
Class FirstClass
Public OnePropertyInside As String
Class SecondClass
Public AnotherProperty As String
Public Shared SharedProperty As String
End Class
End Class
I ran a few tests which brought secondary questions (see comments in code)
Sub Main()
Dim myFirstClass As New FirstClass
'Works as expected
Console.WriteLine(myFirstClass.OneProperty)
'What is the difference between the two lines below?
Console.WriteLine(myFirstClass.SecondClass.SharedProperty)
Console.WriteLine(FirstClass.SecondClass.SharedProperty)
'This line cannot be compiled, this demonstrates SecondClass is not instantiated when FirstClass is.
Console.WriteLine(myFirstClass.SecondClass.AnotherProperty)
Dim mySecondClass As New FirstClass.SecondClass
'Works as expected, but I feel this hierarchy should better be dealt with through a namespace statement?
Console.WriteLine(mySecondClass.AnotherProperty)
End Sub
You can think of it as if the inner most class is a helper class of sorts. It may not even need to be used at all. Nesting the inner class(or simply nested class) inside the outer class gives you access to all of the members of the outer one. You can even access the private members inside that initial outer class.
Edit: For clarification, I mean to say that the the inner can access the private members of the outer, not the other way around.
You usually do this because you want to restrict the scope of the nested class.
So, if you only need to use this class from within the "parent" class (in terms of scope), then its usually a good idea to define it as a nested class.
If you might might need to use the class outside of its assembly, then it is better to define it as a completely separate class (in its own file), and then define your relationship accordingly. You will need to instantiate one within the other (this is the same whether its seperate or nested - so its location is largely irrelevant for that point).
When you do that, and the inner class is accessible to other classes (it's accessibility is Public or Friend), the outer class basically just works like a namespace. So for instance, using your example, you could create a new object of the nested class without ever creating one of the outer class:
Dim x As New FirstClass.SecondClass()
The most obvious benefit is the structural organization of the code, much like namespaces and code files. So, for instance, it's not uncommon to use nested classes for constants, to help better organize them:
Public Class Urls
Public Class Processing
Public Const Submit As String = "..."
Public Const Cancel As String = "..."
End Class
Public Class Reporting
Public Const Daily As String = "..."
Public Const Weekly As String = "..."
End Class
End Class
' ...
Dim url As String = Urls.Reporting.Daily
However, outside of the narrow set of situations where things like that are useful, most people would prefer to not nest public classes at all.
However, as others have mentioned, the one place where you really will see nested classes used fairly regularly is for Private ones. If you need some small helper class which will have no use to code outside of your class, there's no reason to expose it. Even if you set it's accessibility to Friend, it will still be visible to all the other classes in the same project. Therefore, if you really want to hide it from everything else, you'll want to make it a nested private class. For instance:
Public Class MyClass
Public Function GetTheIdOfSomething() As Integer
Dim d As Details = GetDetailsAboutSomething()
If d.Value Is Nothing Then
Return d.Id
Else
Throw New Exception()
End If
End Sub
Private Function GetDetailsAboutSomething() As Details
' ... return a Details object
End Function
Private Class Details
Public Property Id As Integer
Public Property Value As String
End Class
End Class

Access a class property only from a certain class

Suppose I have the following class:
Public Class lbMenu
Inherits Form
...
Public Sub AddItem(header As String)
Dim item As New lbMenuItem()
item.Owner = Me
item.Header = header
Controls.Add(item)
End Sub
...
End Class
and also the following class:
Public Class lbMenuItem
Inherits Control
Private _Owner As lbMenu
Public Property Owner As lbMenu
Get
Return _Owner
End Get
Set(value As lbMenu)
_Owner = value
End Set
End Property
Private _Header As String
Public Property Header As String
Get
Return _Header
End Get
Set(value As String)
_Header = value
End Set
End Property
...
End Class
As you can see, I access the Owner property of lbMenuItem from the class lbMenu.
What I want is that nobody else can access the Owner property, or maybe just to read the value of the property. I think there is no way to do that but maybe there is a solution to reach a similar situation.
Any suggestions?
Edit:
I already considered passing the owner as a parameter in the constructor, but I want that anyone can access the lbMenuItem class. Anyone can create a new instance of the class, read and write many properties, etc. The only thing I don't want is that someone can write the Owner property. Someone can create a new instance of the class without having to pass an ownerMenu parameter to the New method (he can't know what ownerMenu is for). For the same reason, lbMenuItem can't be a nested class of lbMenu.
I'm trying to get the same result as with ContextMenuStrip and ToolStripMenuItem. ToolStripMenuItem has an Owner property (which in this case has read/write access) and an OwnerItem which is read-only.
So, is there a way to make the Owner property read-only but writable from the lbMenu class?
You could just have the Owner property private or read-only if you want to allow reading its value and set it in the constructor.
Edit:
Someone can create a new instance of the class without having to pass
an ownerMenu parameter to the New method
Well, you can have two constructors, one that accepts a lbMenu parameter, and one without. So, your code would look something like this:
Public Class lbMenu
Public Sub AddItem(header As String)
Dim item As New lbMenuItem(Me)
item.Header = header
Controls.Add(item)
End Sub
End Class
Public Class lbMenuItem
' Use this instead if you don't want the property to be accessible at all.
'Private Property Owner As lbMenu
Public ReadOnly Property Owner As lbMenu
Public Property Header As String
' Allow creating an instance of the class without having to pass/set the owner.
Public Sub New()
End Sub
Public Sub New(ownerMenu As lbMenu)
Owner = ownerMenu
End Sub
End Class
One last recommendation:
Based on the names of your classes, you might consider having a parent/child relationship between them (i.e., the lbMenuItem class would be a nested class within lbMenu). This will give you the advantage of being able to access the private members of lbMenu from a lbMenuItem instance.
Hope that helps.
Addressing the new points in your edit:
ToolStripMenuItem has an Owner property ... and an OwnerItem which is read-only.
That's exactly what I proposed (i.e., to use a read-only property). Note that the OwnerItem property isn't exposed as writable to any other class.
So, is there a way to make the Owner property read-only but writable
from the lbMenu class?
No, that's not possible. A read-only property is read-only, period. You can't expose its setter to a specific class. You can, however, limit the accessibility to its setter by using the keywords Friend, Private, or Protected:
Friend Set --> will make the setter accessible (make the property writable) inside the current project and read-only outside the project.
Private Set --> will make the setter accessible from the child classes.
Protected Set --> will make the setter accessible from the derived classes.
Take a look at this question to get a better idea on how to limit the accessibility of a property setter. It's about C#, but the concept is the same.

Calling method directly without instance variable

Is there a way to call directly a method without creating instance of specific class like it is in C# so apart from that way:
Dim myclass as New ClassX
myclass.MyMethod()
is there a way to use soemthing like:
New ClassX.MyMethod
i found this way and seems to be working but not sure if its correct:
(New ClassX).MyMethod
If your method is an instance-level method you can only access it by using an instance of the class. By using
(New ClassX).MyMethod
you implicitly create a new instance that you access only once.
An alternative is to change the method's signature and mark it as a Shared method:
Public Class ClassX
Public Shared Sub MyMethod()
' ...
End Sub
End Class
Shared is the VB.NET way of creating a static method as it is called in C#. This way, you can access the method by only specifying the class name without creating an instance:
ClassX.MyMethod()

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.

How to load a class into the current instance within Sub New

Long term lurker, first time poster here.
I have written a class to model an object in vb.net, using vs2008 and framework 2.0. I am serializing the class to an XML file for persistent storage. I do this with a method in the class like this:
Public Sub SaveAs(ByVal filename As String)
Dim writer As New Xml.Serialization.XmlSerializer(GetType(MyNamespace.MyClass))
Dim file As New System.IO.StreamWriter(filename)
writer.Serialize(file, Me)
file.Close()
End Sub
I now want to do a similar thing but reading the class from file to the current instance, like this:
Public Sub New(ByVal filename As String)
Dim reader = New Xml.Serialization.XmlSerializer(GetType(MyNamespace.MyClass))
Dim file = New System.IO.StreamReader(FullPath)
Me = CType(reader.Deserialize(file), MyNamespace.MyClass)
End Sub
However, I cannot assign anything to “Me”. I’ve tried creating a temporary object to hold the file contents then copying each property and field over to the current instance. I iterated over the properties (using Reflection), but this soon gets messy, dealing with ReadOnly collection properties, for example. If I just copy each property manually I will have to remember to modify the procedure whenever I add a property in the future, so that sounds like a recipe for disaster.
I know that I could just use a separate function outside the class but many built-in .NET classes can instantiate themselves from file e.g. Dim bmp As New Bitmap(filename As String) and this seems very intuitive to me.
So can anyone suggest how to load a class into the current instance in the Sub New procedure? Many thanks in advance for any advice.
I'd put a shared load function on the class, that returned the newly de-serialised object.
e.g.
Public Class MyClass
...
Public shared Function Load(ByVal filename As String) as MyClass
Dim reader = New Xml.Serialization.XmlSerializer(GetType(MyNamespace.MyClass))
Dim file = New System.IO.StreamReader(FullPath)
Return CType(reader.Deserialize(file), MyNamespace.MyClass)
End Sub
End Class
...
Dim mine as MyClass = MyClass.Load("MyObject.Xml");
Hope this helps
Alternatively,
Encapsulate the data of your class in an inner, private class.
The properties on your outer visible class delegate to the inner class.
Then Serialising and De-serialising happens on the inner class, you can then have a ctor that takes the file name, de-serialises the inner hidden object, and assigns it to the classes data store.
The "New" method in VB.Net is a constructor for the class. You can't call it for an existing instance, as the whole purpose of the method is to create new instances; it's just not how the language works. Try naming the method something like "ReadFrom" or "LoadFrom" instead.
Additionally, given those methods, I would try to implement them using a Factory Pattern. The ReadFrom method would be marked Shared and return the new instance. I would also make the method more generic. My main ReadFrom() method would accept an open textreader or xmlreader or even just a stream, rather than a file name. I would then have overloads that converts a file name into a stream for reading and calls the main method.
Of course, that assumes I use that pattern in the first place. .Net already has great support for xml serialization built into the platform. Look into the System.Xml.Serialization.XmlSerializer class and associated features.