Are there any difference in these 2 ways to declare array? - arrays

Dim a As String()
Dim b() As String
One says that a is a type of string array
Another isn't very clear. Are they completely equivalent?
Why the 2 ways?
I supposed the first way is saying a is a String array instead of just string.
I am not so sure about the reasoning of Dim b() As String
I give it a try. The result is that b is a String() that points to nothing.
a is an integer whose value is -1
I am confused.
Why?
In functions we declare that a function accepts an array with something like this
Protected Overrides Async Function createNormalLimitOrderMultiple(orderList As BasicSimpleOrder()) As Task
Here, BasicSimpleOrder() simply tell that orderList is of type BasicSimpleOrder() and not BasicSimpleOrder.
So why doesn't Dim a As String() work

In most situations you could treat them the same, however there are some differences.
Array size: You cannot specify the upper-bounds of an array if you put the parenthesis after the type, otherwise you will receive an error.
Dim foo As String(1) ' Compilation Error: Array bounds cannot appear in type specifiers.
Property Definitions: You cannot define an array if you put the parenthesis after the name of a property. The reason for this is because you can optionally include parenthesis after a property name definition and then optionally include a parameter list inside the parenthesis.
Public Class MyClass
Public Property Property1() As String ' not an array
Public Property Property2 As String() ' this is an array
End Class
Here is the documentation on property statements in Visual Basic .NET: https://learn.microsoft.com/en-us/dotnet/visual-basic/language-reference/statements/property-statement

Related

Searching a multi-dimensional array with VB.Net to count occurrences of a string

I have Dictionary defined as:
Private cdEmailsUploaded As New ConcurrentDictionary(Of String, Boolean)
The string contains the fullpath to an email file (MSG) and I want to create a function that returns a count of those that contain a given string in their path e.g "ProjectX".
I have assumed, possibly incorrectly, that it would be best to convert the Dictionary into an Array and then search it for the string and count the results. If there is a better way then please let me know.
The problem is that I can't get my head around the syntax to get the embedded function to search the string part of the array and not the Boolean.
What I have at the moment is:
Function QtyUploadedToLocation(sFind As String) As Integer
Dim emailsArr = cdEmailsUploaded.ToArray
Dim result As String() = Array.FindAll(emailsArr, Function(s) s.Key.StartsWith(sFind))
Return result.Length
End Function
The error I'm getting is:
Value of type 'KeyValuePair(Of String, Boolean)()' cannot be converted
to 'String()' because 'KeyValuePair(Of String, Boolean)' is not
derived from 'String'.
The signature of Array.FindAll is:
Public Shared Function FindAll(Of T) (array As T(), match As Predicate(Of T)) As T()
That means that for a dictionary type, it's working in KeyValuePair(Of TKey, TValue) throughout, and in particular, the result is an array of key/value pairs, not an array of string.
If you just want the count, you could a) fix the intermediate type, b) use type inference to avoid having to say what the type is (if you have Option Infer On), or c) just return the count directly, i.e.
Return Array.FindAll(emailsArr, Function(kvp) kvp.Key.StartsWith(sFind)).Length
If you want the array of strings for further processing, you can use the Linq Select function to get that sequence, i.e.
Dim matchingKeys = _
Array.FindAll(emailsArr, Function(kvp) kvp.Key.StartsWith(sFind)) _
.Select(Function(kvp) kvp.Key)
This will be an IEnumerable(Of String), you could use ToArray if you need it as an array or just leave it as-is if you want to do some other processing on it.

Definitions of property procedures for the same property are inconsistent [using arrays]

I am having trouble with this pesky error message (see title)
I have seen it on a lot of posts on this site and others and it is usually some dumb mistake and try as I might I cannot see what dumb thing I am doing.
Public Property Get Contents() As Variant
Contents() = pContents()
End Property
Public Property Let Contents(Values() As Variant)
pContents = Values()
End Property
Public Property Get Content(Index As Integer) As String
Content = pContents(Index)
End Property
Public Property Let Content(Index As Integer, Value As String)
...
The aim being that the pair of get, let two allow the entire array to be read/written and the second allows read/writing to individual elements of the array by index.
According to this: https://msdn.microsoft.com/en-us/library/office/gg251357.aspx
Let and Get property statement thingies (Yes, I am fairly new to this) for a class in a class module have to meet certain requirements (which I am curious as to why exactly?). As far as I can tell, mine meet those requirements:
My Let has one more parameter (2) than my Get (1) - I'm assuming here that the "as string" outside of the brackets doesn't count as an argument.
Also I use the same types (integer and string) in both the Let and Get, I have no need for a Set so that shouldn't be a problem. I have also tried changing the names of the parameter variables to no avail.
So please help me, what is wrong and how can I avoid this mistake in the future?
Well I seem to have resolved the issue myself, posting the answer here in case it helps others! :)
Precautions to make when setting class properties to arrays (and reading them)
1) be careful with brackets(): don't use them in the Let statement
Public Property Let ArrayProperty (newArray as variant)
as opposed to the tempting
Public Property Let ArrayProperty (newArray() as variant)
Similarly, don't use them when using this property e.g:
Class1.ArrayProperty = myArray
no brackets after myArray
2) Check that your arrays are not empty in property (myArray) Let statements
Link here VBA: Don't go into loop when array is empty to the answer that helped me accomplish this, it seems you have to build your own function or use error handling to do it,
IsEmpty(myArray)
won't work.
and the code snippet adapted for my purposes (credit to original snippet goes to CreamyEgg):
Public Function IsEmptyArray(TestArray) As Boolean
Dim lngUboundTest As Long
lngUboundTest = -1
On Error Resume Next
lngUboundTest = UBound(TestArray)
On Error GoTo 0
If lngUboundTest >= 0 Then
IsEmptyArray = False
Else
IsEmptyArray = True
End If
End Function
3) remember that you might need to redim your property array to the Ubound(newArray), e.g.
Public Property Let Contents (newArray as variant)
'redim pArray to size of proposed array
ReDim pArray(1 To UBound(newArray))
'above line will throw an exception if newArray is empty
pArray = newArray
end Property
4) to deliberately make a class property an empty array, I used a temporary array variable, publicly declared outside of a sub at the start of a standard module
Public TempArray() As Variant
'outside of Sub ^^
Sub SetClass1ArrayToEmpty
Erase TempArray
class1.ArrayProperty = TempArray
End Sub
the erase method will make an array empty, i used it since I occasionally used TempArray to make arrays of size 1 and wanted to make sure it was empty
5) good news, setting a range to a class property seems to work the same way as setting a range to an array, I used application.transpose(myRange) to avoid problems with one column becoming a 2 dimensional array
class1.ArrayProperty = Application.Transpose(Range(myRange))
and there you have it, here is the class that works (no compile error)
Public Property Get Contents() As Variant
Contents() = pContents()
End Property
Public Property Let Contents(Values As Variant)
'checks for an empty array being passed to it first
If IsEmptyArray(Values) Then
Erase pContents
Else
'redim pContents to size of proposed array
ReDim pContents(1 To UBound(Values))
pContents = Values
End If
End Property
Public Property Get Content(Index As Integer) As String
Content = pContents(Index)
End Property
Public Property Let Content(Index As Integer, Value As String)
Select Case Index
Case Is < 0
'Error Handling
Case Is > UBound(pContents) + 1
'Error Handling
Case Is = UBound(pContents) + 1 'append to end of array
ReDim Preserve pContents(UBound(pContents) + 1)
pContents(Index) = Value
Case Else 'replace some middle part of array
pContents(Index) = Value
End Select
End Property
Hope this helps some peeps!

Array declaration: Parenthesis after Name or Type [duplicate]

This may sound trivial, but what is the difference between
Dim v As String()
and
Dim v() As String
in VB.NET?
No difference. From the VB.NET Language Specification on Arrays:
Array types are specified by adding a modifier to an existing type name. The modifier consists of a left parenthesis, a set of zero or more commas, and a right parenthesis.
...
A variable may also be declared to be of an array type by putting an array type modifier or an array initialization modifier on the variable name. In that case, the array element type is the type given in the declaration, and the array dimensions are determined by the variable name modifier. For clarity, it is not valid to have an array type modifier on both a variable name and a type name in the same declaration.
Originally, in Basic, you had to define arrays, but not variables. And the types of variables were defined by a suffix character: A$ was a string, while A% was an integer and A# was double precision. (and all three were distinct and could be used at the same time) (For single-precision, you could use A!, but that was the default if you just used A)
Eventually, programmers came to realize that those were incredibly bad design choices.
To rectify this, Microsoft added "Option Explicit" which required you to predefine every variable. To lessen the effect on the language, they hijack the "DIM" command, which was used to define arrays, to define scalar variables as well.
So originally:
DIM A(50) ' define 51-element single-precision array
Then
DIM A(50) ' define 51-element single-precision array
DIM A$ ' define a string
Then to get rid of the suffixes, they added the "As {type} syntax"
DIM A(50) ' define 51-element single-precision array
DIM B as String
DIM C(50) as String ' define 51-element string array.
Then they made array size variable.
DIM A() ' define single-precision array
DIM B as String
DIM C() as String ' define string array.
This left a conflict in definition style, so they allowed both:
DIM A() ' define single-precision array
DIM B as String
DIM C() as String ' define string array.
DIM D as String() ' define string array.
There is no difference.
Both Dim v As String() and Dim v() As String will create a string array
Traditionally, in Basic, you would put the parenthesis after the variable name. In VB.Net it is allowed to put them after the type instead if you wish. The result is the same so there is no difference using either syntax. The reason for this addition though is because how you can constuct an array. Consider the following code:
Public Sub MethodThatExpectsAnArray(ByVal arr() As String)
'...
End Sub
Public Sub Main()
Me.MethodThatExpectsAnArray(New String() {"Hello", "World"})
End Sub
In the call I construct the array "on the fly" without any assignment except directly to the method argument. Since there are no variable here I must set the paranthesis after the type. To allow this syntax Microsoft had the choice to either change how you traditionally declare arrays in Basic or allow for both syntaxes. They of course opted for the latter.
There is no difference.
It's mostly semantics. The first reads as create variable "v" of type string array and the 2nd reads as create array "v" of type string. Either way the result is the same array of strings.
There is no difference in the meaning of the two.
If you like to declare several variables in one dim statement, the second form provides more flexibility:
dim v(),v2 as string allows you to declare array types and non array types in the same statement.

Why using a constructor for a .NET numeric datatype?

I learn arrays and collections at the moment and the author uses the following declaration
Dim locIntegerArray() As Integer
locIntegerArray = New Integer() {1I, 2I, 3I, 4I}
I have never seen a Constructor in front of Integer yet. I would have declared it this way:
Dim locIntegerArray() As Integer = {1I, 2I, 3I, 4I}
Actually I would have said it is redundant but
Dim locIntegerArray() As Integer
locIntegerArray = Integer() {1I, 2I, 3I, 4I}
give me "Integer is a type and cannot be used as an expression". Could anyome explain to me why we use the Constructor here?
I have never seen a Constructor in front of Integer yet
That’s not a constructor, it’s the New keyword. It is used to call the constructor of a type. And sure, this also works for value types (including Integer).
But what you are actually seeing here is something else: it’s not New Integer, it’s New Integer() – an important distinction. You are calling the array constructor, not the Integer constructor.
Unfortunately, VB’s syntax is muddled here because the parentheses are also used to call a constructor – so you’re right that generally you call a constructor on a type by saying New T() (although VB allows leaving off the parentheses as well, causing even more confusion). However, the subsequent array initialiser { … } disambiguates this expression and causes it to be an array constructor call rather than a call to the Integer constructor. To wit:
Dim a = New Integer() ' a = 0
Dim b = New Integer ' same
Dim c = New Integer() { } ' c = array of integers (length 0)
Dim d = New List(Of Integer)() ' d = list of integers (length 0)
Dim e = New List(Of Integer) ' same
Your second code also works because newer versions of VB.NET have introduced a shorthand notation for the New Integer() { … } syntax. The effect is the same. Nowadays there is not really a reason to use the old, explicit syntax any more, because it is entirely redundant.
You can reduce the redundancy even more with Option Infer On:
Dim x = { 1, 2, 3 }
This is the most concise form. The MSDN lists all possible syntax forms for initialising an array in VB.
If you're confused why you need to use constructor for numeric data type like Integer, you should not. Realize that that is not constructor for Integer, that is expression to "say" array of Integer instead.
Unlike C#, this is a bit confusing in VB. In C# it is clear because array of integer would be expressed with square brackets, like :
var a = new int[]{1,2,3};
...which is equivalent to VB :
Dim a = New Integer(){1,2,3}
From above comparison you can clearly see that Integer() is equivalent to int[] which expresses an array of integer.

Check if property is an array

I want to check if a property from a class is an array (only concerned about numerical arrays here, NOT character arrays [i.e. strings]). I then want to iterate through the array (i.e. 'do something' with each element). See my attempt below. Thanks!!
edit:
So, a little more info... neither IsArray nor my method shown has worked thus far to check for an array. MSDN suggestions "typeof(Array).IsAssignableFrom(type)", but I wasn't sure how to make that work with the property info here. But maybe someone else knows how to use them and I just didn't use properly.
Within the "Class3" I define an array but to not dimension it. I use "redim" when I instantiate it in another thread and load it up prior to passing it to this function. When I insert a breakpoint in the code here, I can look at "myobject" and see the array elements and values, but really I'm looking to cleanly use the propertyinfo type to generalize this method. I also need to be able to index into the array once I've determined that it is an array...again using propertyinfo, not "myobject" directly.
Public Class Class2
Private Shared filelock As New Object
Public Shared Sub write2file(ByVal myobject As Class3)
SyncLock filelock
Dim sb As New StringBuilder
Using sw As StreamWriter = New StreamWriter(File.Open(newfilename, FileMode.Append, FileAccess.Write, FileShare.None))
'Dim pinfo() As PropertyInfo = GetType(Class3).GetProperties
Dim pinfo() As PropertyInfo = CType(myobject.GetType.GetRuntimeProperties, PropertyInfo())
sb.Clear()
For Each p As PropertyInfo In pinfo
If Not p.GetIndexParameters.Length > 0 Then 'if property is not an array
sb.Append(p.GetValue(myobject)).Append(",")
Else ' if property is an array
For x As Integer = 0 To p.GetIndexParameters.Length - 1
sb.Append(p.GetValue(myobject, New Object() {x})).Append(",") 'append each value from array to the stringbuilder in .CSV format
Next
End If
Next
sw.WriteLine(sb) 'write string to file
End Using
End SyncLock
End Sub
End Class
Well, this is not the prettiest thing to do (I somehow don't feel comfortable when comparing the type with a string... if anyone knows better please let me know), but I have tested it and it works:
For Each p As PropertyInfo In pinfo
Dim typeString As String = p.PropertyType.Name.ToString
If typeString = "Int32[]" Then 'if property is not an array
sb.Append(p.GetValue(myobject)).Append(",")
Else ' if property is an array
For x As Integer = 0 To p.GetIndexParameters.Length - 1
sb.Append(p.GetValue(myobject, New Object() {x})).Append(",") 'append each value from array to the stringbuilder in .CSV format
Next
End If
Next
Is it what you were looking for?
only concerned about numerical arrays here, NOT character arrays [i.e. strings]
If by "numerical" you mean that it could be any type (not just Integer), and you are 100% sure that the array it is either numerical or string (I mean, no boolean arrays for instance), then you can modify it to this:
If typeString.EndsWith("[]") And typeString <> "String[]" Then
I hope it helps...
Replace the If block with something like this and it seems to work with a trivial Class3 implementation with an Int32() array property:
If p.PropertyType.IsArray Then
' Untested loop code for array case
Else
' Untested code for scalar (non-array) case
End If
No one seems to have recommended the PropertyType property yet, but that's pretty crucial.

Resources