McBasic™Get McBasic™ ↗
Build 881
Language reference

Arrays, collections & dictionaries

Choose a container by how you need to find, order and change its values.

Three different access models#

ContainerUse it forIndex / key
ArrayA typed set with a declared shapeDeclared lower and upper bounds
CollectionAn ordered group that growsOne-based positions and optional keys
DictionaryLookup by a meaningful keyCheck Exists before Item when absence is expected

Collection and Dictionary items must be scalar values, not controls or class instances. Use typed object arrays for those references. A control’s Items or a Recordset is not interchangeable with a plain array. The object defines both its mutation operations and its indexing rules.

Read the actual array bounds#

LanguageWorkbench / Main.bas · SumReadings
Public Function SumReadings() As Long
    Dim readings(-1 To 1) As Long
    Dim index As Long
    Dim total As Long
    readings(-1) = 10
    readings(0) = 20
    readings(1) = 30
    For index = LBound(readings) To UBound(readings)
        total = total + readings(index)
    Next index
    SumReadings = total
End Function

This array runs from -1 through 1. The total is 60. A loop that assumes the first element is zero would skip a valid value.

Grow a dynamic array deliberately#

LanguageWorkbench / Main.bas · GrowReadings
Public Function GrowReadings() As Long
    Dim readings() As Long
    ReDim readings(0 To 1)
    readings(0) = 4
    readings(1) = 5
    ReDim Preserve readings(0 To 2)
    readings(2) = 6
    GrowReadings = readings(0) + readings(1) + readings(2)
End Function

The example expands the upper bound and keeps the existing 4 and 5 with Preserve, then adds 6. Its result is 15. Declare the array with empty parentheses before ReDim.

Pass resizable storage to a resizing procedure#

An array declared with bounds, such as Dim readings(0 To 1) As Long, is fixed-size even if a later ReDim would request the same bounds. A typed array parameter is ByRef; declaring it with empty parentheses does not turn a fixed caller array into a dynamic one. From build 834, Check reports MCB2102 at a fixed argument when the callee resizes that parameter with ReDim, ReDim Preserve or whole-array assignment. Forwarding the parameter to another resizing procedure counts too.

Two complete procedures in LanguageWorkbench / Main.bas
Public Sub FillReadings(ByRef readings() As Long)
    ReDim readings(0 To 2)
    readings(0) = 20
    readings(1) = 30
    readings(2) = 40
End Sub


Public Function ReadingsFromProcedure() As Long
    Dim readings() As Long
    FillReadings readings
    ReadingsFromProcedure = readings(0) + readings(1) + readings(2)
End Function

The caller declares empty parentheses. FillReadings allocates all three elements before the caller reads them; the result is 90. If the caller needs elements before making the call, it must ReDim first. A fixed array remains valid for a routine that only reads/writes elements or uses Erase to clear them. The new check is not a ban on passing fixed arrays.

Reproduce the Check error, then repair it#

Use a disposable copy of LanguageWorkbench.

  1. Run Check and Test; all nine tests should pass.
  2. In ReadingsFromProcedure only, replace Dim readings() As Long with Dim readings(0 To 1) As Long.
  3. Run Check. Expect MCB2102 on FillReadings readings, explaining that the fixed-size argument is passed to a resizing parameter.
  4. Restore the declaration with empty parentheses. Do not remove the callee’s ReDim: it initializes the data.
  5. Repeat Check and Test. Confirm the result is 90 and the bounds test passes.

Error 10 remains a runtime guard where static analysis cannot establish the storage contract. See fixed versus locked arrays; catching error 10 is not a substitute for correcting the declaration.

Keep order and identity separate#

LanguageWorkbench / Main.bas · CollectionCount
Public Function CollectionCount() As Long
    Dim names As Collection
    Set names = New Collection
    names.Add "Ada", "first"
    names.Add "Grace", "second"
    names.Remove "first"
    CollectionCount = names.Count
End Function

The two entries have keys “first” and “second”. Removing “first” leaves one item. A list position can change after removal, while an application record’s identity should remain stable.

Check a key before reading it#

LanguageWorkbench / Main.bas · LookupName
Public Function LookupName() As String
    Dim names As Dictionary
    Set names = New Dictionary
    names.Add "lead", "Ada"
    If names.Exists("lead") Then
        LookupName = CStr(names.Item("lead"))
    Else
        LookupName = "Unknown"
    End If
End Function

The known key returns “Ada”; an absent key follows the explicit fallback. Convert the stored Variant only after you know it is present and compatible.

Mutate the control’s collection, not its reference#

ListBox, ComboBox, ImageCombo and ListView expose read-only Items references. Clear or add through that collection; do not assign a string to the Items reference in BASIC. The Inspector’s one-item-per-line field supplies the initial list. Menu, ContextMenu, ToolBar and TabStrip retain their separate String Items contract.

For named control arrays, shared handlers and Me.Controls, use the control-array guide. For typed object arrays and Preserve, follow the class-array lesson. For live selection and associated IDs, follow ChoiceBinding. For full signatures, consult collection services.

Source baseline 20f4bfed6b · Build manifest
Sources & maintenance

Original web explanation, reviewed against these repository sources. API tables and example files follow the implementation.

  • docs/manual/21-language-reference.md
  • docs/manual/11-language-toolkit.md
  • site/examples/LanguageWorkbench/Main.bas
  • src/McBasic.Compiler/ArrayResizeAnalysis.cs
  • src/McBasic.Compiler/Binder.cs
  • src/McBasic.Compiler/CallArguments.cs
  • src/McBasic.Runtime/BasicRuntime.cs
  • site/examples/LanguageWorkbench/LanguageTests.bas