' Start off with a Console application project.
' Be sure these options are On:
' Option Strict On
' Option Infer On
' Imports:
' Imports System.Runtime.InteropServices
Structure Xyz
Dim x As Short
Dim y As Integer
Dim z As Long
Dim u As Single
Dim v As Double
End Structure
Sub Main()
Dim pdq0 As Xyz
Dim pdq1 As Xyz
Dim pdq2 As Xyz
' Inferencing is the only way to have a properly typed
' variable of Span(Of Xyz).
Dim refOf_Xyz = MemoryMarshal.CreateSpan(Of Xyz)(Nothing, 0)
' refOf_Xyz(0) is almost works like a Dim ByRef x As Xyz;
' a ref local, if you will.
' Alternative to refOf_Xyz(0) is MemoryMarshal.GetReference(refOf_Xyz).
' WARNING! VB can only be a consumer of ref types, and then
' only by way of a ByRef parameter to function or lambda!
' Also, avoid directly using such in a With statement!
' Reference to pdq0
refOf_Xyz = MemoryMarshal.CreateSpan(pdq0, 1)
Call (Sub(ByRef x As Xyz) x.x = 1)(refOf_Xyz(0))
' Reference to pdq1
refOf_Xyz = MemoryMarshal.CreateSpan(pdq1, 1)
Call Sub(ByRef x As Xyz)
With x
.x = 2
.y = 3
.z = 4
.u = 1.234
.v = 987654321.12345684
End With
End Sub(MemoryMarshal.GetReference(refOf_Xyz))
' Reference to pdq2
refOf_Xyz = MemoryMarshal.CreateSpan(pdq2, 1)
Call Sub(ByRef x As Xyz)
x.u = 2.468
x.v = 111222333444.55566
End Sub(refOf_Xyz(0))
' Now examine pdq0, pdq1, and pdq2
' Confirm they have had their fields asssigned as expected.
Stop
End Sub
For Each
upon a collection of Structure
(a.k.a. value) types that allows for mutations (state changes) of such. Here is one straight-forward approach, which can be used on any object which implements IList(Of T) - a demonstration of the ByIndex extensions.' Start off with a Console application project.
' Be sure these options are On:
' Option Strict On
' Option Infer On
' Imports:
' Imports System.Runtime.CompilerServices
Structure Xyz
Dim x As Short
Dim y As Integer
Dim z As Long
Dim u As Single
Dim v As Double
End Structure
Sub Main()
Dim arrayOf_Xyz(9) As Xyz
For Each item In arrayOf_Xyz.ByIndex
' item.List would be the IList(Of Xyz)
' item.Index is the index into the above IList
' item.Value is a property to item.List(item.Index);
' however, it is still stuck as a pass by value
' situation. Hence the use of a lambda, like so:
Dim itemIndex = item.Index
item.Call(Sub(ByRef x)
' This is the only reliable way, in pure VB,
' short of somehow using Span(Of T), to get at
' a reference to a Structure rather than its copy.
x.x = CShort(itemIndex And 3)
x.y = itemIndex And 7
x.z = (x.x * x.y) Xor itemIndex
x.u = CSng(itemIndex / 100)
x.v = itemIndex / 10
End Sub)
Next
' Now examine elements in arrayOf_Xyz
' Confirm they have had their fields asssigned as expected.
Stop
For Each item In arrayOf_Xyz.ByIndex(2, 3)
' ByIndex can do ranges; here we start with arrayOf_Xyz(2)
' and loop through arrayOf_Xyz(2 + 3 - 1), i.e. arrayOf_Xyz(4).
item.Call(Sub(ByRef x)
x.z *= 2
x.u *= 5
End Sub)
Next
' Now examine elements in arrayOf_Xyz
' Confirm only indicies 2 through 4 have had their
' fields asssigned as expected.
Stop
' Note that item.Call can also take AddressOf some
' applicable function.
For Each item In arrayOf_Xyz.ByIndex(7)
item.Call(AddressOf Foo)
Next
' Now examine elements in arrayOf_Xyz
' Confirm only indicies 7 through 9 have had their
' fields asssigned as expected.
Stop
End Sub
Sub Foo(ByRef x As Xyz)
x.v = 1 + Math.Sqrt(Math.Abs(x.v))
End Sub
Delegate Sub ByIndexAction(Of T)(ByRef Item As T)
Structure ByIndexItem(Of T)
Public ReadOnly List As IList(Of T)
Public ReadOnly Index As Integer
Public Sub New(List As IList(Of T), Index As Integer)
Me.List = List
Me.Index = Index
End Sub
Sub [Call](Action As ByIndexAction(Of T))
Action(List(Index))
End Sub
Public Property Value As T
Get
Return List(Index)
End Get
Set(value As T)
List(Index) = value
End Set
End Property
End Structure
<Extension> Iterator Function ByIndex(Of T)(List As IList(Of T)) _
As IEnumerable(Of ByIndexItem(Of T))
For i As Integer = 0 To List.Count - 1
Yield New ByIndexItem(Of T)(List, i)
Next
End Function
<Extension> Iterator Function ByIndex(Of T)(List As IList(Of T), Start As Integer) _
As IEnumerable(Of ByIndexItem(Of T))
If Start < 0 OrElse Start >= List.Count Then Exit Function
For i As Integer = Start To List.Count - 1
Yield New ByIndexItem(Of T)(List, i)
Next
End Function
<Extension> Iterator Function ByIndex(Of T)(List As IList(Of T),
Start As Integer, Length As Integer) _
As IEnumerable(Of ByIndexItem(Of T))
If Start < 0 OrElse Start >= List.Count Then Exit Function
If Length <= 0 OrElse (Start + Length) > List.Count Then Exit Function
For i As Integer = 0 To Length - 1
Yield New ByIndexItem(Of T)(List, Start + i)
Next
End Function
Structure
(a.k.a. value) types. Utilizing the Enumerator of Span(Of T), this is a handy way to access the array elements in a memory efficient manner, and allows for mutations (state changes). The hitch is one must work through a Delegate
(such as a lambda or AddressOf function).' Start off with a Console application project.
' Be sure these options are On:
' Option Strict On
' Option Infer On
' Imports:
' Imports System.Runtime.CompilerServices
' Imports System.Runtime.InteropServices
Structure Xyz
Dim x As Short
Dim y As Integer
Dim z As Long
Dim u As Single
Dim v As Double
End Structure
Sub Main()
Dim arrayOf_Xyz(9) As Xyz
Dim itemOrdinal = -1
arrayOf_Xyz.ForEachByRef(
Sub(ByRef x)
' ByRef parameter of lambda or function:
' This is the only way in VB to dereference a Span(Of Xyz),
' and get at a reference to a Structure rather than its copy.
itemOrdinal += 1
x.x = CShort(itemOrdinal And 3)
x.y = itemOrdinal And 7
x.z = (x.x * x.y) Xor itemOrdinal
x.u = CSng(itemOrdinal / 100)
x.v = itemOrdinal / 10
End Sub)
' Now examine elements in arrayOf_Xyz
' Confirm they have had their fields asssigned as expected.
Stop
arrayOf_Xyz.ForEachByRef(2, 3,
Sub(ByRef x)
' ForEachByRef can do slices; here we start with arrayOf_Xyz(2)
' and loop through arrayOf_Xyz(2 + 3 - 1), i.e. arrayOf_Xyz(4).
x.z *= 2
x.u *= 5
End Sub)
' Now examine elements in arrayOf_Xyz
' Confirm only indicies 2 through 4 have had their
' fields asssigned as expected.
Stop
arrayOf_Xyz.ForEachByRef(2, 3,
Sub(ByRef x)
' ForEachByRef can do slices; here we start with arrayOf_Xyz(2)
' and loop through arrayOf_Xyz(2 + 3 - 1), i.e. arrayOf_Xyz(4).
x.z *= 2
x.u *= 5
End Sub)
' Now examine elements in arrayOf_Xyz
' Confirm only indicies 2 through 4 have had their
' fields asssigned as expected.
Stop
' Note that ForEachByRef can also take AddressOf some
' applicable function.
arrayOf_Xyz.ForEachByRef(7, AddressOf Foo)
' Now examine elements in arrayOf_Xyz
' Confirm only indicies 7 through 9 have had their
' fields asssigned as expected.
Stop
End Sub
Sub Foo(ByRef x As Xyz)
x.v = 1 + Math.Sqrt(Math.Abs(x.v))
End Sub
Delegate Sub ForEachAction(Of T As Structure)(ByRef Item As T)
<Extension> Sub ForEachByRef(Of T As Structure)(
Array As T(),
Action As ForEachAction(Of T))
With Array.AsSpan().GetEnumerator()
Do While .MoveNext()
Action(.Current)
Loop
End With
End Sub
<Extension> Sub ForEachByRef(Of T As Structure)(
Array As T(),
Start As Integer,
Action As ForEachAction(Of T))
With Array.AsSpan().Slice(Start).GetEnumerator()
Do While .MoveNext()
Action(.Current)
Loop
End With
End Sub
<Extension> Sub ForEachByRef(Of T As Structure)(
Array As T(),
Start As Integer,
Length As Integer,
Action As ForEachAction(Of T))
With Array.AsSpan().Slice(Start, Length).GetEnumerator()
Do While .MoveNext()
Action(.Current)
Loop
End With
End Sub
<Extension> Sub ForEachByRef(Of T As Structure)(
List As IList(Of T),
Action As ForEachAction(Of T))
Dim itemSize As Integer = Marshal.SizeOf(Of T)()
For index As Integer = 0 To List.Count - 1
Dim itemSpan = MemoryMarshal.CreateSpan(List(index), itemSize)
Action(itemSpan(0))
Next
End Sub
<Extension> Sub ForEachByRef(Of T As Structure)(
List As IList(Of T),
Start As Integer,
Action As ForEachAction(Of T))
If Start < 0 Then Exit Sub
Dim itemSize As Integer = Marshal.SizeOf(Of T)()
For index As Integer = Start To List.Count - 1
Dim itemSpan = MemoryMarshal.CreateSpan(List(index), itemSize)
Action(itemSpan(0))
Next
End Sub
<Extension> Sub ForEachByRef(Of T As Structure)(
List As IList(Of T),
Start As Integer,
Length As Integer,
Action As ForEachAction(Of T))
If Start < 0 Then Exit Sub
If Length <= 0 Then Exit Sub
Dim itemSize As Integer = Marshal.SizeOf(Of T)()
For index As Integer = Start To Start + Length - 1
Dim itemSpan = MemoryMarshal.CreateSpan(List(index), itemSize)
Action(itemSpan(0))
Next
End Sub