VB .NET - how can I create an array of references to doubles? - vb.net

I have a bunch of type Double variables in my program, say for example
Dim Area as Double = 0
Dim Perimeter as Double = 0
Somewhere in my program I want to calculate these values, so I define
Public Sub TheSquare(ByRef TheArea as Double, ByRef ThePerim as Double, ByVal TheSide as Double)
TheArea = TheSide^2
ThePerim = 4 * TheSide
End Sub
and somewhere in the program I'm collecting side lengths and calculating the area and perimeter; say
While True
S = GetSideValueFromSomewhere()
TheSquare(Area, Perimeter, S)
End
In my real program, I have, say, 20 quantities that I want to calculate. Obviously each one has a different equation. But in the end I want to output all 20 to a file, so to save typing, I create an array of the quantities, like this:
Dim TypingSaver() as Double = {Area, Perimeter}
so that I can dump values to file with a three-line for-loop instead of copying and pasting 20 variable names.
This does exactly what I want if Area and Perimeter were reference types like Objects. But since they are Doubles, TypingSaver contains only their values, not references to the actual variables. So after I use my TheSquare function the values of Area and Perimeter are correctly updated but TypingSaver just constains whatever the values of Area and Perimeter were when I declared the array.
So the question is: how can I create an array of references to doubles in VB.NET?

With approach that you are doing you can't do this, since as soon as you created array you copied all variables to the array and any changes that you are doing on variables are not reflected on array variables (like you pointed out).
What I would recommend create another class that will contain all your variables (20 variables name) as properties (get and set) and then override ToString method which will return list of all your variables. So when you need to dump those variables you will call ToString() method and it will return current values of all your parameters.

There is a nasty way to do this. All numeric types are value types, but arrays are reference types. So I can change all my Doubles to arrays of doubles, like this:
Dim Area(0) as Double
Dim Perimeter(0) as Double
So now Area and Perimeter are 1-element arrays of double. My "looping array" then becomes
Dim TypingSaver() as Array = {Area, Perimeter}
Now TypingSaver stores references to Area and Perimeter. For me this was an easy change because I could search-and-replace for the Double declaration, change the type for TypingSaver, then in two other places I had to change direct access of this form:
TypingSaver(1) = 7
to
TypingSaver(1).SetValue(7, 0)
Not pretty, but it keeps my code consistent in that I have other "looping arrays" for other objects that are all related to each other.
Although it is not clear in my question the real solution is to restructure everything so instead of storing everything in arrays I crate a class which has all the objects I need and create a single array of that, as suggested in part by Blezden.

The single-element array idea was actually terrible. The code became horrendously slow. There is another work around a friend suggested: create a wrapper class like this:
Public Class DoubleWrapper
Public Value As Double
End Class
Then when an array of DoubleWrappers is created it will be by reference, of course.
I understand this is not ideal but what I was looking for was a workaround until I have the time to rewrite the code from scratch.

Fairly similar to your most recent answer, you'll want to wrap your types as "Nullable", which basically makes it an object that could be null, but also a reference type.
Ie. Dim testDouble as Nullable(Of Double) or Dim testDouble2 as Double?
See:
Nullable(Of T) Structure - MSDN
What is the integer reference type in C#?

Related

i want to make a new variable that says shot1 shot2 shot3 so on and forth how do i do this?

Here is what i have tried so far , but no new shot variable is being declared
Module Module1
Dim shotlist As New List(Of Boolean)
Dim shono As Integer = 0
Dim shonos As String
Dim shotname As String
Dim fshot As Boolean
Dim shots As String
Sub Main()
For i As Integer = 0 To 1000
Dim ("shots" & i) as String = "shots" & i
fshot = Convert.ToBoolean(shots)
Next
End Sub
End Module
You can't do things this way. The variable names you see when you write a program are lost, turned into memory addresses by the compiler when it compiles your program. You cannot store any information using a variable's name - the name is just a reference for you, the programmer, during the time you write a program (design time)
Think of variables like buckets, with labels on the outside. Buckets only hold certain kinds of data inside
Dim shotname as String
This declares a bucket labelled shotname on the outside and it can hold a string inside. You can put a string inside it:
shotname = "Shot 1"
You can put any string you like inside this bucket, and thus anything you can reasonably represent as a string can also be put inside this bucket:
shotname = DateTime.Now.ToString()
This takes the current time and turns it into a string that looks like a date/time, and puts it in the bucket. The thing in the bucket is always a string, and lots of things (nearly anything actually) can be represented as a string, but we don't type all our buckets as strings because it isn't very useful - if you have two buckets that hold numbers for example, you can multiply them:
Dim a as Integer
a=2
Dim b as Integer
b=3
Dim c as Integer
c=a*b
But you can't multiply strings, even if they're strings trust look like numbers; strings would have to be converted to numbers first. Storing everything as a string and converting it to some other type before working on it then converting it back to a string to store it would be very wearisome
So that's all well and good and solves the problem of storing varying information in the computer memory and giving it a name that you can reference it by, but it means the developer has to know all the info that will ever be entered into the program. shotname is just one bucket storing one bit of info. Sure you could have
Dim shotname1 as String
Dim shotname2 as String
Dim shotname3 as String
But it would be quite tedious copy paste exercise to do this for a thousand shotname, and after you've done it you have to refer to all of them individually using their full name. There isn't a way to dynamically refer to bucket labels when you write a program, so this won't work:
For i = 0 To 1000
shotname&i = "This is shotname number " & i
Next i
Remember, shotname is lost when compiling, and i is too, so shotname&i just flat out cannot work. Both these things become, in essence, memory addresses that mean something to the compiler and you can't join two memory addresses together to get a third memory address that stores some data. They were only ever nice names for you to help you understand how to write the program, pick good names and and not get confused about what is what
Instead you need the mechanism that was invented to allow varying amounts of data not known at the design-time of the program - arrays
At their heart, arrays are what you're trying to do. You're trying to have load of buckets with a name you can vary and form the name programmatically.
Array naming is simple; an array has a name like any other variable (what I've been calling a bucket up to now - going to switch to calling them variables because everyone else does) and the name refers to the array as a whole, but the array is divided up into multiple slots all of which store the same type of data and have a unique index. Though the name of the array itself is fixed, the index can be varied; together they form a reference to a slot within the array and provide a mechanism for having names that can be generated dynamically
Dim shotnames(999) as String
This here is an array of 1000 strings, and it's called shotnames. It's good to use a plural name because it helps remind you that it's an array, holding multiple something. The 999 defines the last valid slot in the array - arrays start at 0, so with this one running 0 to 999 means there are 1000 entries in it
And critically, though the shotnames part of the variable name remains fixed and must be what you use to refer to the array itself(if you want to do something with the entire thing, like pass it to a function), referring to an individual element/slot in the array is done by tacking a number onto the end within brackets:
shotnames(587) = "The 588th shotname"
Keep in mind that "starts at 0 thing"
This 587 can come from anything that supplies a number; it doesn't have to be hard coded by you when you write the program:
For i = 0 to 999
shotnames(i) = "The " & (i+1) & "th shotname"
Next i
Or things that generate numbers:
shotnames(DateTime.Now.Minute) = "X"
If it's 12:59 when this code runs, then shotnames(59), the sixtieth slot in the array, will become filled with X
There are other kinds of varying storage; a list or a dictionary are commonly used examples, but they follow these same notions - you'll get a variable where part of the name is fixed and part of the name is an index you can vary.
At their heart they are just arrays too- if you looked inside a List you'd find an array with some extra code surrounding it that, when the array gets full, it makes a new array of twice the size and copies everything over. This way it provides "an array that expands" type functionality - something that arrays don't do natively.
Dictionaries are worth mentioning because they provide a way to index by other things than a number and can achieve storage of a great number of things not known at design time as a result:
Dim x as New Dictionary(Of String, Object)
This creates a dictionary indexed by a string that stores objects (I.e. anything - dates, strings, numbers, people..)
You could do like:
x("name") = "John"
x("age") = 32
You could even realize what you were trying to do earlier:
For i = 0 to 999
x("shotname"&i) = "The " & (i+1) & "th shotname"
Next i
(Though you'd probably do this in a Dictionary(Of string, string), ie a dictionary that is both indexed by string and stores strings.. and you probably wouldn't go to this level of effort to have a dictionary that stores x("shotname587") when it's simpler to declare an array shotname(587))
But the original premise remains true: your variable has a fixed part of the name (ie x) and a changeable part of the name (ie the string in the brackets) that is used as an index.
And there is no magic to the indexing by string either. If you opened up a dictionary and looked inside it, you'd find an array, together with some bits of code that take the string you passed in as an index, and turn it into a number like 587, and store the info you want in index 587. And there is a routine to deal with the case where two different strings both become 587 when converted, but other than that it's all just "if you want a variable where part of the name is changeable/formable programmatically rather than by the developer, it's an array"

VBA Assign value (only) to variable

I've hit a snag, and my searching hasn't helped so far.
I have a variant being passed into a function which I then intend to copy, perform some calculations, take another copy, perform some other calculations then compare the results of the two copies...
However, when I perform the calculations on one copy, the original variant is also manipulated... so after two copies, and two calculations I end up with 3 variants that are equal to each other and different to the original... Not what I intended.
I expect this is happening because when I use NewVar = OldVar I'm actually taking a reference to the original object. What I actually want, is to make an independent duplicate of the original object - i.e. copy the value of the variable similar to byval in a function delcaration.
My code is linked here: https://1drv.ms/x/s!AiPgb0BH-YZ_ga956eMmJbSdihGjyg.
If you put a break on line 67 of modMain, then watch CutList(1).QTY (the original variable), and CutTrial.RemainingCuts(1).QTY you'll see that both the QTY values decrement when you step through line 67... I want CutList(1).QTY to remain unchanged, and CutTrial.RemainingCuts(1).QTY to decement only.
Any suggestions?
Make sure the functions definition is as follows
Public function DoMagic(ByVal variable as something)
Don't use ByRef or as you found it will modify the reference.
If you are using an object, array or collection you will need to first copy it before using it.
Eg something like the following:
Public Function Clone() As Class1
Set Clone = New Class1
Clone.prop1 = prop1
Clone.PrivateThing = PrivateThing
End Function

Integer in VB.net turns into String in VBA

I have a list of objects that are "keys" into a table - the keys are simply the items in the first column. They can be Integers or Strings, depending on what DB table we read it from. Since we use them a lot, we cache that column in an ArrayList called "Keys".
We wrote cover methods to return Row, one that uses strings and the other integers. If you call the integer version it simply returns that row by index. If you call the string, it looks down Keys for a match, and uses that index to pull out the row.
Ok, so now I pass Keys to Excel, pull out one of them in a loop, and ask it what it is...
TypeName(DB.Keys(i))
And the object returns...
Long
Great, the keys must have been integers! So now I'll try to get the row for that key, by calling the accessor method, Row...
DB.Row(DB.Keys(i))
And it calls into the version that takes a String.
Whoa!
Can anyone think of a reason that VBA calling back into our VB.net DLL ends up calling the wrong accessor?
ADDED CODE: I can't figure out how to put the code in as a reply, so I'm editing this. Here is the code in the VB.net class:
Public Function Row(ByVal K As String) As DataRow
Dim R As DtaRow = DB.Tables(0).Rows(K)
Return New DataRow(R)
End Function
Public Function Accounts(ByVal K As Integer) As DataRow
Dim R As DtaRow = DB.Tables(0).Rows(K)
Return New DataRow(R)
End Function
If you're wondering, there's two versions of Rows, which take strings or ints.
This code works perfectly from VB.net. You can ask for a row by key string or by the row number, that invokes the proper Row, which calls the proper Rows, and out comes the proper answer.
But in VBA, it always calls the method with the string input. If I rename it to RowIHATEYOUALL then I can call the Integer version just fine. But when there are two of them, differing only in signature, no such luck.
And the A and i (see comments) was my typo.
The interop layer does not support overloaded methods. Whenever you call Row, the first declared method with that name is used. The .NET overload resolution algorithm does not apply here.
Other overloads are exposed to VBA as Row_2, Row_3, etc. Thus, the following code should do what you expect:
DB.Row_2(DB.Keys(i))
This implicit dependency on the order of declaration has a high potential for error. Thus, I would suggest to either
give the methods unique names if they are called from VBA,
or, if you want to retain the "nice" overloaded version for .NET, add compatibility methods for VBA:
<Obsolete("Compatibility method for VBA, use Row instead.")>
Public Function RowByKeyVBA(ByVal K As String) As DataRow
Return Row(K)
End Function
<Obsolete("Compatibility method for VBA, use Row instead.")>
Public Function RowByNumberVBA(ByVal K As Integer) As DataRow
Return Row(K)
End Function
Further information can be found in the following question:
COM->.NET - can't access overloaded method
Following Heinzi's notes (above) I fixed this by making three method signatures for each call, one takes an Object and then attempts to figure out what it is, the others take the String and Integers. Within VB/C#/etc the proper String or Integer methods get called as expected, from VBA the Object version is called, as Heinzi noted
This causes the very minor issue that a user may have a "number like value" that is actually a String. For instance, the array keys might be "User3232" or "3232", both of which are String objects in the table. So you have to be careful, simply asking if the Object can be converted to an Int32 is not enough. This is unlikely to be something that effects most users.

Difference between Long and Object data type in VBA

In VBA, the Long and Object data type are both 4-bytes, which is the size of a memory address. Does this mean that, technically, the Object data type doesn't do anything that a Long couldn't do? If yes, then is it safe to say that the Object data type exists simply to make it easier for the programmer to distinguish between the purpose of the variable?
This question came up as I was considering Win32 API function declarations. They are often times declared as Long, and, unless I am mistaken, their return value is simply a memory address. Seems like defining these functions as Object would have been more appropriate, then.
Am I totally off? Thanks in advance.
Based on VBA/MSDN help:
Long (long integer) variables are stored as signed 32-bit (4-byte)
numbers ranging in value from -2,147,483,648 to 2,147,483,647.
and the other definition:
Object variables are stored as 32-bit (4-byte) addresses that refer to
objects. Using the Set statement, a variable declared as an Object can
have any object reference assigned to it.
From practical point of view they are both different and used in different situation. Which are essential: Long >> refers to numbers and Object >> refers to object.
Look into the following VBA code (for Excel) where I added comments which is allowed and which is not:
Sub test_variables()
Dim A As Object
Dim B As Long
'both below are not allowed, throwing exceptions
'A = 1000
'Set B = ActiveSheet
'both are appropriate
Set A = ActiveSheet
B = 1000
End Sub
Finally, in terms of API it's better to stay with original declaration and not manipulate with that to avoid any risk on unexpected behaviour of API functions.

Dynamically create variables in VB.NET

I have been trying to figure this out for some time now and can't seem to figure out an answer to it. I don't see why this would be impossible. I am coding in VB.NET.
Here is my problem:
I need to dynamically create variables and be able to reference them later on in the code.
More Details:
The number of variables comes from some math run against user defined values. In this specific case I would like to just create integers, although I foresee down the road needing to be able to do this with any type of variable. It seems that my biggest problem is being able to name them in a unique way so that I would be able to reference them later on.
Simple Example:
Let's say I have a value of 10, of which I need to make variables for. I would like to run a loop to create these 10 integers. Later on in the code I will be referencing these 10 integers.
It seems simple to me, and yet I can't figure it out. Any help would be greatly appreciated. Thanks in advance.
The best way to do something like this is with the Dictionary(T) class. It is generic, so you can use it to store any type of objects. It allows you to easily store and retrieve code/value pairs. In your case, the "key" would be the variable name and the "value" would be the variable value. So for instance:
Dim variables As New Dictionary(Of String, Integer)()
variables("MyDynamicVariable") = 10 ' Set the value of the "variable"
Dim value As Integer = variables("MyDynamicVariable") ' Retrieve the value of the variable
You want to use a List
Dim Numbers As New List(Of Integer)
For i As Integer = 0 To 9
Numbers.Add(0)
Next
The idea of creating a bunch of named variables on the fly is not something you are likely to see in any VB.Net program. If you have multiple items, you just store them in a list, array, or some other type of collection.
'Dim an Array
Dim xCount as Integer
Dim myVar(xCount) as String
AddButton Event . . .
xCount += 1
myVar(xCount) = "String Value"
'You will have to keep Track of what xCount Value is equal to to use.
'Typically could be an ID in A DataTable, with a string Meaning