Pass-through methods vs. accessing nested objects directly - vb.net

What I have is an object that contains a list of objects that each contain another list of objects that have properties and such.
Currently I use pass-through methods to be able to add to those nested objects, like in this extremely simplified example:
Public Class clsA
Private objB As List(Of clsB) = New List(Of clsB)
Public Sub New()
objB.Add(New clsB)
End Sub
Public Sub AddInt(ByVal BIndex As Int32, ByVal CIndex As Int32, ByVal Number As Int32)
objB(BIndex).AddInt(CIndex, Number)
End Sub
End Class
Public Class clsB
Private objC As List(Of clsC) = New List(Of clsC)
Public Sub New()
objC.Add(New clsC)
End Sub
Public Sub AddInt(ByVal CIndex As Int32, ByVal Number As Int32)
objC(CIndex).AddInt(Number)
End Sub
End Class
Public Class clsC
Private lstNum As List(Of Int32) = New List(Of Int32)
Public Sub AddInt(ByVal Number As Int32)
lstNum.Add(Number)
End Sub
End Class
It seems like proper coding standards would tell me this is correct compared to:
Public Class clsD
Public objE As List(Of clsE) = New List(Of clsE)
Public Sub New()
objE.Add(New clsE)
End Sub
End Class
Public Class clsE
Public objF As List(Of clsF) = New List(Of clsF)
Public Sub New()
objF.Add(New clsF)
End Sub
End Class
Public Class clsF
Public lstNum As List(Of Int32) = New List(Of Int32)
End Class
Are there some instances where either method would be acceptable? Or would the pass-through setup always be preferred?
Public Class Form1
Dim oA As clsA = New clsA
Dim oD As clsD = New clsD
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
oA.AddInt(0, 0, 3)
oD.objE(0).objF(0).lstNum.Add(3)
End Sub
End Class

Think about how it's done throughout the .NET Framework. The collection should be assigned to a private field and exposed via a public read-only property.
Public Class Thing
Private _stuff As New List(Of Thing)
Public ReadOnly Property Stuff() As List(Of Thing)
Get
Return _stuff
End Get
End Property
End Class
The caller can then access the collection directly to call its Add method, etc, but cannot assign a whole new collection. There are examples everywhere: Control.Controls, ListBox.Items, ComboBox.Items, ListView.Items, DataSet.Tables, DataSet.Relations, DataTable.Rows, Datatable.Columns, etc, etc, etc.

Related

How to raise an events when global variable changed in vb.net

I've global variables in module level with below COM-interface types.
Imports System.IO
Imports simpackcomslvLib
Imports simpackcompostLib
Module Globals
Public Srv As SpckCOMApp
Public Mdl As IScrModel
Public Post As PostComApp
Public Res As PostComProject
End Module
In another classes some of my procedures change their object values. I'd like to run some of my procedures which made some changes on my tool's GUI when the for example Mdl value is changed.
I tried with below method which made for an integer type parameter but i didnt succeded for my case, i think because of their object(I types belongs to COM-interface.
Public Class myVar
Private mValue As Integer
Public Event VariableChanged(ByVal mvalue As Integer)
Public Property Variable() As Integer
Get
Variable = mValue
End Get
Set(ByVal value As Integer)
mValue = value
RaiseEvent VariableChanged(mValue)
End Set
End Property
End Class
Usage of above code in an example
Public Class Form1
Private WithEvents test As New myVar
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
test.Variable = CInt(TextBox1.Text)
End Sub
Private Sub VariableChanged(ByVal NewValue As Integer) Handles test.VariableChanged
MessageBox.Show(NewValue)
End Sub
End Class
Is there anyway to implement my below variables in a module such a way also using in module level is wrong should i move them under the class?
Module Globals
Public Srv As SpckCOMApp
Public Mdl As IScrModel
Public Post As PostComApp
Public Res As PostComProject
End Module

how to get the Index of object in collection

I'm trying to make a application, in this application I have a List(of T) collection that holds an object.
When processing the object I need to know it's Index from the list.
Example:
Public Class
Public oList as New List(of TestObject)
Private Sub Test()
Dim NewObject As New TestObject
oList.add(NewObject)
Index(NewObject)
End Sub
Private Sub Index(Byval TestObject As TestObject)
debug.print(Testobject.index)
End Sub
End Class
Is something like this possible? Ive seen it available in a reference file I used some time ago, but now I would like to make this available within my own class.
Can someone provide a sample?
PS: I know I can get the index using the List(Of T).IndexOf Method (T) but for future possibilities I would like to make the call from the object itself.
What usually happen is that they have a custom list, they don't directly used List(Of T) and store the list inside the object when they add that item to the list.
Module Module1
Sub Main()
Dim someList As New CustomList
someList.Add(New CustomItem())
someList.Add(New CustomItem())
someList.Add(New CustomItem())
Console.WriteLine(someList(1).Index)
Console.ReadLine()
End Sub
End Module
Class CustomItem
' Friend since we don't want anyone else to see/change it.
Friend IncludedInList As CustomList
Public ReadOnly Property Index
Get
If IncludedInList Is Nothing Then
Return -1
End If
Return IncludedInList.IndexOf(Me)
End Get
End Property
End Class
Class CustomList
Inherits System.Collections.ObjectModel.Collection(Of CustomItem)
Protected Overrides Sub InsertItem(index As Integer, item As CustomItem)
If item.IncludedInList IsNot Nothing Then
Throw New ArgumentException("Item already in a list")
End If
item.IncludedInList = Me
MyBase.InsertItem(index, item)
End Sub
Protected Overrides Sub RemoveItem(index As Integer)
Me(index).IncludedInList = Nothing
MyBase.RemoveItem(index)
End Sub
End Class
It looks like this
Public oList As New List(Of TestObject)
Private Sub Test()
Dim NewObject As New TestObject(oList.Count)
oList.add(NewObject)
End Sub
Public Class TestObject
Public index As Integer
Public Sub New(IndxOfObj As Integer)
Me.index = IndxOfObj
End Sub
End Class
If you necessarily need to have it as a property on the object I would suggest the following:
Public Class Main
Public oList As New List(Of TestObject)
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Dim NewObject As New TestObject(Me)
oList.Add(NewObject)
Dim NewObject2 As New TestObject(Me)
oList.Add(NewObject2)
MsgBox(NewObject2.Index)
End Sub
Public Function Index(ByVal TestObject As TestObject) As Integer
Return oList.IndexOf(TestObject)
End Function
End Class
Public Class TestObject
Private _main As Main
Public ReadOnly Property Index() As Integer
Get
Return _main.Index(Me)
End Get
End Property
Public Sub New(RootClass As Main)
_main = RootClass
End Sub
End Class
If you happen to have the Main class as a Singleton you can skip the whole sending 'Me' into the constructor business. Then you can just call Main.Index without storing it as a property on all TestObjects.

Setting shared values in a for/next loop

I have a shared array that needs a calculation for each index. Right now, the array is initialized in New() and executes each time an object is created, which is redundant. How can I set it up so that the array is only initialized once, when it is created? Something like a static block in Java?
I didn't initially include the code because I thought this was just a simple question. The constructor is called 64 times (working with values of bits) and he relevant code is :
' Decimal value of each bit
Public Shared bitValue(63) As Long
Public Sub New()
' Other non-relevant code here.
For index = 0 To 63
bitValue(index) = 1L << index
Next
End Sub
Just in case you are wondering what the heck I'm doing, I'm rewriting a Java program that I did that works with casting and bitwise operators. As part of learning VB, I'm working on a translation. You can get an idea of where it is headed by going to http://sourceforge.net/projects/javabitwise/.
You can initialize it right away
Class Test1
Private Shared _list As New List(Of String) From {"1", "2"}
End Class
Or do it in a shared constructor
Class Test2
Private Shared _list As List(Of String)
Shared Sub New()
_list = New List(Of String)
_list.Add("1")
_list.Add("2")
End Sub
End Class
Or check if the value is initialized or not in the new
Class Test3
Private Shared _list As List(Of String)
Public Sub New()
' Use synclock
If _list Is Nothing Then
_list = New List(Of String)
_list.Add("1")
_list.Add("2")
End If
End Sub
End Class
Or put your list in a singleton
Here's a quick example of the Singleton approach. We access the class via its Instance() function:
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Debug.Print(KibblesAndBits.Instance.bitValue(10))
End Sub
End Class
Public Class KibblesAndBits
Private Shared _Kibbles As KibblesAndBits = Nothing
Public Shared Function Instance() As KibblesAndBits
If IsNothing(_Kibbles) Then
_Kibbles = New KibblesAndBits ' the private constructor will now fire and initialize
End If
Return _Kibbles
End Function
Public bitValue(63) As Long
Private Sub New() ' Private Constructor means you can't instantiate it directly
For index = 0 To bitValue.Length - 1
bitValue(index) = index
Next
End Sub
' ... other standard Class code ...
End Class

Removing an object from List (Of T)

I'm not able to remove an object from my List (of contact)
Here are my fields:
Public Class Contact
'Things to remember
Private m_firstName As String = String.Empty
Private m_lastName As String = String.Empty
Private m_address As Address
My list:
Public Class ContactManager
Private m_contactRegistry As List(Of Contact)
Public Sub New()
m_contactRegistry = New List(Of Contact)()
End Sub
My method in ContactManger Class. Here I'm getting error "Value of type 'Integer' cannot be converted to Assignment.Contact" on the index
Public Function DeleteContact(index As Integer) As Boolean
m_contactRegistry.Remove(index)
Return True
End Function
My delete button method on my Main class:
Private Sub btnRemove_Click(sender As Object, e As EventArgs) Handles btnRemove.Click
'listResults is my listbox
Dim list = listResults.SelectedIndex
'm_contact is an object of the Contact class
m_contacts.DeleteContact(list)
UpdateGUI()
End Sub
The problem is that I don't know how to do the method DeleteContact(index As Integer) without getting an error. Do you guys have a suggestion?
When using an index, you need RemoveAt() rather than Remove()

VB.NET Class with no constructor (no new instance can be declared)

I'm making my own message box class (called MessageBoxC, whatever), and like System.Windows.Forms.MessageBox, I want to make my class with no constructors and no possibility to declare a new instance of it.
E.g.:
Public Class MessageBoxC
Public Overloads Sub Show(ByVal message As String)
Me.Message = message
ProcessData() '(*)
Me.ShowDialog()
End Sub
End Class
Public Class Form1
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
System.Windows.Forms.MessageBox.Show("Hello World!") 'works fine
MessageBoxC.Show("Hello World!") 'works fine
Dim msgBox As New System.Windows.Forms.MessageBox 'and you'll get an error message here (**)
Dim msgBoxC As New MessageBoxC 'no error message
End Sub
End Class
(*) Not important. It just calculates text size (width and height in pixels) to correct form size if needed and the corresponding label gets value of Me.Message property.
(**) This one is what I mean. You cannot make a new instance of a MessageBox class, you'll get following error-message: "Type System.Windows.Forms.MessageBox has no constructors."
Well, my class has also no constructors, but it's possible to declare an instance of it. What's the trick here?
Thanks a lot!
Solved. Thanks to OneFineDay.
Public Class MessageBoxC
Private Sub New()
'Empty
End Sub
Public Overloads Shared Function Show(ByVal message As String) As System.Windows.Forms.DialogResult
Return Show(message, Constants.MyAppName, Constants.messageTitle, MessageBoxCButtons.OK, MessageBoxCIcon.Undefined)
End Function
Public Overloads Shared Function Show(ByVal message As String, _
ByVal caption As String, _
ByVal title As String, _
ByVal buttons As Library.MessageBoxCButtons, _
ByVal icon As Library.MessageBoxCIcon) As System.Windows.Forms.DialogResult
Dim msgBoxC As New CBox(message, caption, title, buttons, icon)
msgBoxC.ShowDialog()
Return msgBoxC.DialogResult
End Function
Private Class CBox
Inherits System.Windows.Forms.Form
Sub New(ByVal message As String, _
ByVal caption As String, _
ByVal title As String, _
ByVal buttons As Library.MessageBoxCButtons, _
ByVal icon As Library.MessageBoxCIcon)
MyBase.New()
InitializeComponent()
Me.Message = message
Me.Text = caption
Me.Title = title
Me.Buttons = buttons
Me.Icon64 = icon
Me.OptimizeMe()
End Sub
End Class
End Class
Public Class Form1
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Dim dialogResult As New DialogResult
dialogResult = MessageBoxC.Show("This is a simple message.")
MessageBox.Show(dialogResult.ToString)
End Sub
End Class
If you don't declare any constructors, a default constructor is automatically created (this is a public constructor with no parameters).
To prevent anyone creating an instance of your class, you can create a private constructor, like so:
Public Class MessageBoxC
Private Sub New()
' Prevents anyone creating an instance of this class.
End Sub
End Class
Note that your Show method will need to be declared Shared, otherwise you won't be able to call it. In fact, it would need to be Shared, even with the code you provided.
Here is one way to hide the constructor - mainly because the class in question is not accessible.
Public Class Form1
Private Sub meLoad() Handles Me.Load
'Usage
FooBar.Show("Hi")
End Sub
'...
End Class
Public Class FooBar
Private Sub New()
End Sub
Public Shared Sub Show(message As String)
Dim mbc As New MessageBoxC(message)
mbc.ShowDialog()
End Sub
'MessageBoxC is not exposed outside of Foobar which is the entry point
Private Class MessageBoxC : Inherits Form
'define cTor's as needed
Public Sub New(message As String)
Me.Text = message
End Sub
'define content
End Class
End Class