Please have a look at the code below:
Imports System.Data.Common
Imports System.Data.SqlClient
Imports System.Data.OracleClient
Public Class clsParameterValues
Implements IDisposable
Private paramValues(0) As DbParameter
Public Function AssignParameterValues(ByVal strParameterName As String, ByVal strParameterValue As String, ByVal intDatabaseType As Integer) As Integer
Dim intArrayBound As Integer
intArrayBound = UBound(paramValues)
If intArrayBound > 0 Then
ReDim paramValues(intArrayBound)
End If
If intDatabaseType = 1 Then
paramValues(intArrayBound) = New SqlParameter(strParameterName, strParameterValue)
ElseIf intDatabaseType = 2 Then
paramValues(intArrayBound) = New OracleParameter(strParameterName, strParameterValue)
'paramValues(intArrayBound) = New OracleParameter(":" & strParameterName, OracleType.Int32)
'paramValues(intArrayBound).Value = strParameterValue
End If
Return intArrayBound
End Function
Public Function getParameterValues() As DbParameter()
Return paramValues
End Function
Public Sub Dispose() Implements IDisposable.Dispose
Erase paramValues
paramValues = Nothing
End Sub
End Class
The webpage function looks like this:
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim objParameterValues As New clsParameterValues
Using objParameterValues
'Use the objParameterValues class here.
End Using
objParameterValues=nothing
End Using
End Sub
I am using IDisposable.Dispose to erase the array before setting it to Nothing. I believe this is bad practice because the Array class does not implement IDisposable. Is it even necessary to erase an array and set it to Nothing? (Does the garbage collector deal with this?)
Public Sub Dispose() Implements IDisposable.Dispose
Erase paramValues
paramValues = Nothing
End Sub
The Erase statement dates from old versions of Basic, the kind where manual memory management was useful. No more, memory management is automatic in .NET. It is still supported for compatibility reasons. All it does is set the array reference to Nothing. So your code is equivalent to:
Public Sub Dispose() Implements IDisposable.Dispose
paramValues = Nothing
paramValues = Nothing
End Sub
So no point to it. You should never implement IDisposible to set a variable to Nothing, that's not the interface's contract. A disposed object may never be used again. So no point in setting the array reference to null since that doesn't actually do anything to the real array object on the garbage collected heap. I cannot otherwise see a scenario where you would want to help, any clsParameterValues object should have a limited lifetime. It just isn't useful anymore when you null the array reference.
Just remove the IDisposable implementation.
Is it even necessary to erase an array and set it to nothing?
No. The GC does that in its own time. In fact, I’m not even sure whether Erase actually frees money or whether it simply points the array somewhere else.It just sets the variable to Nothing. Erase probably exists out of compatibility with VB6. C# for instance doesn’t have it (and neither does it have ReDim, although Array.Resize exists of course, albeit with slightly different semantics).
On the other hand, if your array contained IDisposable objects (which it doesn’t) you should dispose those in your Dispose method by iterating over the array and disposing them in turn. But again, the array itself doesn’t need to be erased.
As a general remark, your code reads very much like VB6. Change that. Don’t declare variables without initialising them. Don’t use UBound etc (there’s the .Length property instead). If you find yourself using ReDim very often use a System.Collections.Generic.List instead of an array.
Related
where does it go wrong?
my coding
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq
Imports System.Net
Public Class DigiposAJA
Private Sub CekPaket()
Dim json As String = (New WebClient).DownloadString("http://192.168.101.1:100/list_product?username=SIP12&category=ROAMING&to=0811&payment_method=LINKAJA&json=1")
Dim jarr As JArray = Linq.JArray.Parse(json)
Dim sKatagori As String
For Each jtk As JToken In jarr
sKatagori = jtk.SelectToken("kategori")
DgvDigipos.Rows.Add()
DgvDigipos.Rows(DgvDigipos.Rows.Count - 1).Cells("DgvKategori").Value = sKatagori
Next
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
CekPaket()
End Sub
End Class
after I debug the result is an error like this.
Newtonsoft.Json.JsonReaderException: 'Error reading JArray from JsonReader. Current JsonReader item is not an array: StartObject. Path '', line 1, position 1.'
Can you help me to get a great result
Most likely this is a result of your call to the web service not returning the result you expect.
This is actually a good example of the benefits of separation of concerns and strongly typed objects. Your sub CekPaket should be broken down into 3 parts. 1) get the string. This should be a function and should use some sort of configuration to get the end point and have appropriate guards for failure, 2) parse the string into a strongly typed object (IEnumerable of whatever), this should validation to make sure that the input is good. You might want to make this function public for easy testing. And finally 3) bind your results to your UI. It looks like you are doing this part by hand, whenever possible you should allow the frame work to do this for you by providing a data source and a template for the display.
I wrote a code to demonstrate the issue:
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
doSomething()
End Sub
Dim controlArr() As Object = {NumericUpDown1, NumericUpDown2, NumericUpDown3, NumericUpDown4, CheckBox1, CheckBox2, CheckBox3, CheckBox4}
Private Sub doSomething()
Dim testStr As String = ""
For Each control In controlArr
Select Case control.GetType
Case GetType(NumericUpDown)
control.value = 1
Case GetType(CheckBox)
control.checked = True
End Select
Next
End Sub
End Class
When I run the code I receive Null Referenece Exception "Object reference not set to an instance of an object", the error disapears when I declare the controlArr array inside DoSomething subroutine. Anyway I would prefer having it declared outside since I am using it in many functions. I would like to understand it better so if you provided me with a topic I could read up on I would be very grateful. Thank you very much for your help.
The issue is that declarations are processed before the constructor. That means that this line:
Dim controlArr() As Object = {NumericUpDown1, NumericUpDown2, NumericUpDown3, NumericUpDown4, CheckBox1, CheckBox2, CheckBox3, CheckBox4}
is executed before the code that creates all the controls on the form and assigns them to those fields. As such, all the fields are Nothing at the time that code is executed and so your array contains a whole lotta nothin'. There's no issue creating objects to initialise fields like that and that code does successfully create an array. It's just that you are implicitly setting every element of that array to Nothing so that's what you get to use later on.
If you want to reference any control then you have to wait until after the form's controls are created. That means, at the earliest, after the call to InitializeComponent in the constructor. More generally, you should do it in the Load event handler, e.g.
Dim controlArr As Object()
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Form1.Load
controlArr = {NumericUpDown1, NumericUpDown2, NumericUpDown3, NumericUpDown4, CheckBox1, CheckBox2, CheckBox3, CheckBox4}
End Sub
I'm working with a rather complex problem in VBA and I decided to structure it in different classes.
One of the classes containd an array and I need to modify the component individualy outside the class.
The problem can de reduced to the definition of the class
Public a As Variant
Private Sub Class_Initialize()
ReDim a(5) As Double
' Set some value
a(1) = 20
End Sub
And a sub to access the data:
Sub Test()
Dim b As New DummyTest
b.a(4) = 7
Debug.Print (b.a(1))
Debug.Print (b.a(4))
End Sub
The result on the debug window is 20 0. I've checked this result with an inspection and confirm that the property a cannot be modified from the exterior of the class.
Funny part is that the property can be modified if replaced by other array, for example:
Sub Test2()
Dim b As New DummyTest
b.a = Array(1, 2, 3, 4, 5)
Debug.Print (b.a(1))
Debug.Print (b.a(4))
End Sub
Behaves as expected. I'm confused as element access is allowed inside the class context and in any other situation, but in this specific case it doesn't wotk. There is no error msg, it simply refuses to change the content of the array.
I'll find work around solutions but missing this way to access data is being a really PITA.
Hope you can help me with this issue.
Thank you in advance!
Your "property" is breaking encapsulation, which pretty much defeats its entire purpose.
Public a As Variant
From a public interface standpoint, this is a read-write property (if you added a new class module with Implements DummyTest you'd have to add Property Get and Property Let members for it). From the class' standpoint, it's a public instance field.
When a class encapsulates an array, a collection, a dictionary, or any other data structure, the last thing you want is for that data structure to be publicly exposed, with anyone anywhere being able to just overwrite the class' entire internal state as it pleases - you're not getting any of the advantages of classes that way.
First step to proper encapsulation is to make the instance field Private. Next step is to expose actual Property accessors. You want the array data to be writable? Expose an indexed Property Let member for it.
Private a As Variant
Private Sub Class_Initialize()
ReDim a(0 To 10)
End Sub
Public Property Get Value(ByVal index As Long) As Variant
Value = a(index)
End Property
Public Property Let Value(ByVal index As Long, ByVal newvalue As Variant)
a(index) = newvalue
End Property
This code behaves exactly as advertised:
Public Sub Test()
Dim b As DummyTest
Set b = New DummyTest
b.Value(4) = 7
Debug.Print b.Value(1)
Debug.Print b.Value(4)
End Sub
Note that because the array is encapsulated, that client code is absolutely unable to re-assign the array itself: the array is abstracted away and only accessible through the means exposed by the class.
Reference variable in VBA must be declared, so that we know what type of object we are referring to
i.e. Dim b As DummyTest ' No new required in this step
Before using a declared reference to store information create an instance of the Class.
i.e. Set b = New DummyTest
This might seem confusing but is actually very logical. Because b is a Class the Dim statement sets up b to hold the address of a DummyTest object. At the time of the Dim statement no object has been created (no memory allocated) so the address of b is nothing (no value or null). The set statement tells VBA to create an instance of your DummyTest class (allocate the memory it needs) and puts the address of the instance (memory) into variable b. Then because VBA knows that b points to an instance of a class you never see the address of b when you use it, just the data to which b is pointing. You can get the the address b is holding using the function Objptr but this is only something you would do in very special/specific circumstances
I have a queue of strings:
Public Class QueueClient
Private gaoFiles As New Queue(Of String)
and in a property call a function with the intention to concatenate an array of strings to the queue of strings:
Public Property AddFiles As String()
...
Set(asValue As String())
AddFilesToQueue(asValue)
End Set
End Property
This is the called function, in which I attempt to perform the concatenation.
Private Sub AddFilesToQueue(asFiles() As String)
gaoFiles = CType(gaoFiles.Concat(asFiles), Queue(Of String))
End Sub
End Class
This gets me an InvalidCastException (I have Option Strict On).
I understand, that
gaoAudioFiles.Concat(asFiles)
tries an implicit conversion from a string array to a Queue(Of String), which is not done due to my setting.
How do I cast this correctly?
gives me
This doesn't work at all how you think.
The Concat() method on the Queue(Of T) type is inherited from IEnumerable(OF T), and it's using that implementation, meaning all you get back is an IEnumerable(Of T). This would be okay (though not very efficient) if you could cast IEnumerable(Of T) to a Queue(Of T), but that cast doesn't exist. Instead, you'll have to Enqueue each item:
Private Sub AddFilesToQueue(asFiles() As String)
For Each file As String in asFiles
gaoFiles.Enqueue(file)
Next file
End Sub
It's also kind of weird to use a Property in this way. When you use assignment semantics, the end user expects a variable to be entirely replaced with the a new value. Appending via assignment semantics is not normal. Better to settle for the just the method... but you can make the method more useful like this:
Public Sub AddFilesToQueue(asFiles As IEnumerable(Of String))
For Each file As String in asFiles
gaoFiles.Enqueue(file)
Next file
End Sub
You can still pass an array directly to that method. You can also pass a List(Of String) or any other sequence that inherits from IEnumerable (and there are many).
I am very new to VBS, but I am not able to implement even the simplest things, as it seems. I want to have a class which holds an array in a private member. Since I want to "inject" the array I tried to implement a "setter-method" using the Let functionality.
Class CPhase
Private m_AllowedTasks()
Public Property Let AllowedTasks(p_AllowedTasks)
m_AllowedTasks = p_AllowedTasks
End Property
Private Sub Class_Initialize()
ReDim m_AllowedTasks(0)
End Sub
End Class
This class is used as follows:
Dim allowed
allowed = Array("task1", "task2")
Dim phase
Set phase = New CPhase
phase.AllowedTasks = allowed
This results in a "Microsoft VBScript runtime error (...) : Type mismatch" in the Let-method. I also tried using different combinations of "ByVal", "ByRef", but since having absolutely no experience with VBS I couldn't find a solution. So what am I doing wrong?
Any hints or links to helpful ressources are very appreciated!
Thanks!
The culprit is
Private m_AllowedTasks()
which creates an abomination - a fixed array of no size. Just remove the ().
Private m_AllowedTasks
to create an (empty) Variant that may be set=let to an useful (redim-able) array.