vblang
vblang copied to clipboard
VB doesn't need a Range but a Ranger!
I don't like C# ranges, and I will never use them. Their roles are confusing, and I see no benefit of having a new syntax for such a trivial thing! I just wrote a Ranger class to work as a range wrapper for lists:
Public Class Ranger(Of T)
Public ReadOnly Property List As IList(Of T)
Private Const ErrMsg1 = "abs(index) can't be > the count of items."
Private Const ErrMsg2 = "index can't be >= the count of items."
Public Sub New(list As IList(Of T))
Me.List = list
End Sub
Default Public Overloads Property Item(index As Integer) As T
Get
Return List(FixIndex(index))
End Get
Set
List(FixIndex(index)) = Value
End Set
End Property
Default Public Overloads Property Item(range As Range) As T()
Get
Return Item(range.Start, range.End)
End Get
Set
Item(range.Start, range.End) = Value
End Set
End Property
Default Public Overloads Property Item(start As Integer, [end] As Integer) As T()
Get
fixRange(start, [end])
Dim L = [end] - start
Dim a(L) As T
For i = 0 To L
a(i) = List(start + i)
Next
Return a
End Get
Set
fixRange(start, [end])
Dim L = Value.Length - 1
For i = 0 To [end] - start
If i > L Then
List(start + i) = Nothing
Else
List(start + i) = Value(i)
End If
Next
End Set
End Property
Private Sub fixRange(ByRef start As Integer, ByRef [end] As Integer)
start = FixIndex(start)
[end] = FixIndex([end])
If start > [end] Then
Dim temp = start
start = [end]
[end] = temp
End If
End Sub
Private Function FixIndex(index As Integer) As Integer
If index < 0 Then index = List.Count + index
If index < 0 Then
Throw New IndexOutOfRangeException(ErrMsg1)
ElseIf index < List.Count Then
Return index
Else
Throw New IndexOutOfRangeException(ErrMsg2)
End If
End Function
End Class
Public Structure Range
Public ReadOnly Property Start As Integer
Public ReadOnly Property [End] As Integer
Public Sub New(start As Integer, [end] As Integer)
Me.Start = start
Me.End = [end]
End Sub
Public Shared Widening Operator CType(range As (Start As Integer, [End] As Integer)) As Range
Return New Range(range.Start, range.End)
End Operator
End Structure
So, we can do this:
Dim myList As New List(Of Integer) From {1, 2, 3, 4}
Dim r As New Ranger(Of Integer)(myList)
Dim range = r(1, -2)
r(-4, -2) = {5, 6}
Console.WriteLine(r(1)) ' 5
Console.WriteLine(r(-1)) ' 4
Console.WriteLine(r(4)) ' Error
Console.WriteLine(r(-5)) ' Error
I prefer r(-1) over r(^1) and r(1, 2) over r(1..3). C# came up with the most unnatural syntax and made it even more confusing by that strange +1 end role!!
Of course it will be be better if .NET Core updates its lists to have this as a built in, or we can provide a new NuGet with such Ranged Lists, so, we don't have to use a wrapper a one more line of code. Either way, I can live without any ranges. I survived without them for a quarter of a century :) .
Very nice it would be useful to have for Arrays also. Is there a way to Inherit from IList so you don't have to create the list first?
It works for arrays (was my first test), because Array implements the IList.
Is there a way to Inherit from IList so you don't have to create the list first?
In my first implementation, I inherited the List(Of T), so, it is doable, but this will exclude arrays and other types of IList types. A wrapper is a cheap generic solution, and if you don't need the List, you can create it on as an argument:
Dim r As New Ranger(Of Integer)({1, 2, 3, 4})
In fact, I hope that VB can infere the generic type param if there is no non-generic version of the class, so we can just write:
Dim r As New Ranger({1, 2, 3, 4})
In #556, I asked for allowing [] for arrays and indexer. After writing the Ranger, I am thinking that [ ] can differ from ( ) in that it always negative index and supports ranges. VB can just lower the [] to an instance of the Ranger class. So, we can keep the ( ) indexer intact not to break anything nor confusing anyone.