How to apply threading in vb.net with passing an object of a custom class for a method - vb.net

I am newbie here. I need your help. I am trying to develop a tool where I want to implement multi-threading in vb.net. I am stuck in a place which I could not resolve. Let me explain you.
I have created a custom class as below:
Public Class TestClass
'Few private Members of my class
Private a As String,b As String
Public Structure abc
Dim xx as object
Dim yy as object
Dim zz as object
End Structure
Public aa(10) As abc
'Now I needed to override the constructor as the initialization of
'instance of this class can be made in two different ways due to
'requirement
Public Sub New(ByVal xy As String,ByRef yz As Object)
'Some internal method to initialize the object
Me.a=xy
Me.b=xy
End Sub
'Another way to create the instance
Public Sub New(ByVal xy As String,ByVal yz As String)
'Some internal method to initialize the object
Me.a=xy
Me.b=yz
End Sub
'Now a public method which I want to call using threading
Public Sub TestSub()
'Do something with Me.a,Me.b and aa(some index)
End Sub
End Class
Now in a different module I am creating the instance of this class as below:
Dim X1 As New TestClass("Some String",<Some Object reference>)
Dim X2 As New TestClass("Some String","Some String")
Now I have declared a sub in the same module like below
Sub DoMyStuff(ByRef A1 As TestClass)
A1.TestSub()
End Sub
After all This I want to create a thread and run the sub "DoMyStuff" by Passing a reference of X1
To Do this I have Imported System Threading:
Imports System.Threading
Inside Module after initialization of X1 and X2 I have written:
Dim T1 as New Threading.Thread(AddressOf DoMyStuff)
T1.Start(X1)
Here I am getting error: Overload Resolution Failed because no 'New' can be called with this arguments: 'Public Sub New(Start As System.Threading.ParameterizedThreadStart, maxStackSize As Integer)': Method 'Public Sub DoMyStuff(ByRef A1 As TestClass)' does not have a signature compatible with delegate 'Delegate Sub ParameterizedThreadStart(obj As Object)'. 'Public Sub New(Start As System.Threading.ThreadStart, maxStackSize As Integer)': Method 'Public Sub DoMyStuff(ByRef A1 As TestClass)' does not have a signature compatible with delegate 'Delegate Sub ThreadStart()'.
If I am writing like this,
Dim T1 As Threading.Thread
T1 = (AddressOf DoMyStuff)
T1.Start(X1)
I am getting the error: 'AddressOf' expression can not be converted to 'System.Threading.Thread' because 'System.Threading.Thread' is not a delegate type.
Might be I am missing some thing, which I am unable to find. I am not so much good in Delegate type concept. Also this is my first project with implementation of Threading. Looked for answers in Google but unable to understand to sort out. Your help highly solicited.
Thanks
Edit:
I have checked your reference Michael Z. and its really helpful. First of all a big Thank you for that :)
However, I am unable to understand a thing in there. If you help me out there, I would be highly grateful. As per my understanding, I have changed my code as below:
Public Delegate Sub Test2(ByRef A1 as TestClass)
Public T1 as Test2 = New Test2(AddressOf DoMyStuff)
Public Sub DoMyStuff(ByRef A1 as TestClass)
A1.TestSub()
End Sub
Now I have Declared in my module as follows:
Dim T as Threading.Thread = New Threading.Thread(AddressOf DoStuff)
Here I am unable to understand how to write the DoStuff sub as in your example you were working with TextBox1 which is an object in UI, so you wrote:
Public Sub DoStuff()
If TextBox1.InvokeRequired Then
TextBox1.Invoke(T1,"Hello")
End If
End Sub
But here I need to work with an object which is custom made i.e. X1 or X2. Also I need to pass the reference of the object in the Thread so that the method TestSub can work with the created object either X1 or X2. Totally lost here :(
Please correct me if anywhere I am wrong to understand your reference, otherwise if correctly understood, can you Please help me out here. Appreciate your help in advance.
Thanks

Firstly, ensure you're calling this from a method:
Dim T1 as New Threading.Thread(AddressOf DoMyStuff)
T1.Start(X1)
Next remove the ByRef in the method parameter like so Sub DoMyStuff(A1 As TestClass)
Essentially make your second Module look like:
Module Module2
Dim X1 As New TestClass("Some String", "asd")
Dim X2 As New TestClass("Some String", "Some String")
Sub TestCall
Dim T1 as New Thread(AddressOf DoMyStuff)
T1.Start(X1)
End Sub
Private Sub DoMyStuff(A1 As TestClass)
A1.TestSub()
End Sub
End Module
And you can call it like so:
TestCall()

Try using a simple Call method;
Call New Threading.Thread(AddressOf DoMyStuff).Start(X1)
Looking at what you wanting, there seems no reason to assign this to a variable?
Also - remember, when working with multiple contructors, you need to ensure the [TYPES] you pass in are correct and expected (this could be why your current process isn't working).
See this link for a similar issue.

Related

Using a delegate for thread start inside another Sub

What I've got is something like this:
Private Sub GiantLegacySub()
... lots of variables and legacy code...
Dim somethingNew = New Func(of String, Boolean)(
Function(stringy as String) As Boolean
... new code that uses the legacy variables ...
End Function)
Dim t = New Thread(AddressOf somethingNew)
End Sub
I am getting an error indicating that somethingNew is being seen as variable name and not a method name and is thus unacceptable by AddressOf. ( I know that somethingNew is a variable, just one that happens to contain a pointer to a method).
Is there a way to do this? I need to leave it inside of GiantLegacySub because of the shear volume of variables in its scope.
Based on Craig's guidance, this was the answer:
Private Sub GiantLegacySub()
... lots of variables and legacy code...
Dim somethingNew = Sub(stringy as String)
... new code that uses the legacy variables ...
End Sub
Dim t = New Thread(somethingNew)
t.Start(someStringForStringy)
End Sub

Passing Data from one Form to another by Overloading Constructor

Ok, I am trying to pass data from one form to another form using the overload constructor method. I could just use public variables or properties, but would like to use the overload method.
Here is the code section from my first form that calls the second form...
Private Sub dgvAllWO_CellDoubleClick(sender As Object, e As System.Windows.Forms.DataGridViewCellEventArgs) Handles dgvAllWO.CellDoubleClick
Dim I As Integer
I = dgvAllWO.Rows(e.RowIndex).Cells.Item(0).Value
Dim frmWO(I) As New frmWorkOrder
End Sub
Here I have the code from the other form...
Public Class frmWorkOrder
Public Sub New(ByVal ID As Integer)
InitializeComponent()
End Sub
Public Sub New()
InitializeComponent()
End Sub
End Class
After doing that, i now get an error on 'NEW' on this line of code on my first form.
Dim frmWO(I) As New frmWorkOrder
Error 1 Arrays cannot be declared with 'New'
Why is this happening? After overloading the constructor my form class is turned into an array? Im not sure what is happening here. I am grateful for any help or direction you can give me. Thanks in advance.
In VB New is the constructor, so you need to pass the value to the constructor like this:
Dim frmWO As New frmWorkOrder(I)
If frmWO is an array, then something like this:
frmWO(I) = New frmWorkOrder(I)
This line:
Dim frmWO(I) As New frmWorkOrder
should be
Dim frmWO As New frmWorkOrder(I)

VB.NET Plugin Architecture

Hello I'm working on using the interface class in vb.net to make a plugin architecture. So far I've not found a step by step tutorial for a beginner. But I've gotten as far as this:
Main App
Public Class PluginHandler
Interface IApplications
Sub ChangeForms()
End Interface
Public Shared Sub GetDLLFromDir(ByVal TheDir)
For Each dll As String In System.IO.Directory.GetFiles(TheDir, "*.dll")
LoadPlugin(dll)
Next
End Sub
Public Shared Sub LoadPlugin(ByVal ThePlugin)
Dim Asm = Assembly.LoadFile(ThePlugin)
Dim type As Type = Asm.GetType("TestPlugin.Class_TestPlugin")
Dim method As MethodInfo = type.GetMethod("ChangeForms")
method.Invoke(Nothing, Nothing)
End Sub
End Class
TestPlugin.vb
Public Class Class_TestPlugin
Implements Plugin_Application.PluginHandler.IApplications
Sub ChangeForms() Implements Plugin_Application. _
PluginHandler.IApplications.ChangeForms
Dim NewForm As New Form_Test
NewForm.Show()
End Sub
End Class
My issue is it says for the method to invoke --> Non-static method requires a target.
I seen on another forum that the method may not be found. I found it says the methods name and void. But I'm not sure what to do. If somebody can mod my code to work or give me some ideas to make my code work. Thanks :)
Here is a link to my test project folder: Link
You have to create an object the method should be called on:
Dim ctor = type.GetConstructor({}) 'no parameters for constructor
Dim obj = ctor.Invoke({})
method.Invoke(obj, {})

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.

.net dynamic loading

I've seen some other responses about this and they talk about interfaces but I'm pretty sure you can do this with classes and base classes but I can't this to work.
Public Class Behavior
Private _name As String
Public ReadOnly Property Name As String
Get
Return _name
End Get
End Property
Public Property EditorUpdate As Boolean
Public Sub New(ByVal name As String)
_name = name
EditorUpdate = False
End Sub
Public Overridable Sub Update()
End Sub
' runs right away in editor mode. also runs when in stand alone game mode right away
Public Overridable Sub Start()
End Sub
' runs after game mode is done and right before back in editor mode
Public Overridable Sub Finish()
End Sub
' runs right when put into game mode
Public Overridable Sub Initialize()
End Sub
' runs when the game is complete in stand alone mode to clean up
Public Overridable Sub Destroy()
End Sub
End Class
Public Class CharacterController
Inherits Behavior.Behavior
Public Sub New()
MyBase.New("Character Controller")
End Sub
Public Overrides Sub Update()
' TODO: call UpdateController()
' THINK: how can UpdateController() get the controller entity it's attached to?
' Behaviors need a way to get the entity they are attached to. Have that set when it's assigned in the ctor?
End Sub
End Class
Dim plugins() As String
Dim asm As Assembly
plugins = Directory.GetFileSystemEntries(Path.Combine(Application.StartupPath, "Plugins"), "*.dll")
For i As Integer = 0 To plugins.Length - 1
asm = Assembly.LoadFrom(plugins(i))
For Each t As Type In asm.GetTypes
If t.IsPublic Then
If t.BaseType.Name = "Behavior" Then
behaviorTypes.Add(t.Name, t)
Dim b As Behavior.Behavior
b = CType(Activator.CreateInstance(t), Behavior.Behavior)
'Dim o As Object = Activator.CreateInstance(t)
End If
End If
Next
Next
When it tries to convert whatever Activator.CreateInstance(t) returns to the base class of type Behavior I'm getting invalid cast exception. That type should be of CharacterController which is defined as a child of Behavior so why wouldn't it let me cast that? I've done something like this before but I can't find my code. What am I missing?
This may not be an answer to your question (it also might resolve your exception -- who knows), but it is something that needs to be pointed out. These lines:
If t.IsPublic Then
If t.BaseType.Name = "Behavior" Then
Should really be changed to one conditional like this one:
If t.IsPublic AndAlso (Not t.IsAbstract) AndAlso _
GetType(Behavior.Behavior).IsAssignableFrom(t) Then
Otherwise, if somebody defines a random type called "Behavior" in their own assembly and derives it from another type, your code will think it is a plugin. Additionally, if someone derives your Behavior type and then derives that type (two levels of inheritance) this code will incorrectly skip over that type. Using the IsAssignableFrom method is a quick and easy way to ensure that one type does actually derive from the specific type you want (instead of any type that shares the same name), even if there is another type in between your types in the inheritance tree. The additional check against t.IsAbstract will also ensure that you don't try to instantiate an abstract subtype of your base plugin type.
This works for me:
Dim ctor As Reflection.ConstructorInfo = _
t.GetConstructor(New System.Type() {})
Dim o As Object = ctor.Invoke(New Object() {})
Dim plugin As Plugin = TryCast(o, Plugin)
(If I find t, I invoke the parameterless constructor.)
[I just realized this is probably what Activator.CreateInstance does, so I replaced my code with yours and it worked your way -- so this probably won't help you]