What prevents a
List(Of T)
from throwing an arithmetic overflow exception when I set the internal_version
field toInteger.MaxValue
and add a new item?
TL;DR:
As you can see when looking at the source code there's no protection against the potential arithmetic overflow. But some-one/thing is setting the value to Integer.MinValue
if the value is maximum.
<__DynamicallyInvokable()> _
Public Sub Add(ByVal item As T)
If (Me._size = Me._items.Length) Then
Me.EnsureCapacity((Me._size + 1))
End If
Me._items(Me._size++) = item
Me._version += 1
End Sub
Detailed
As the internal item-array of List(Of T) is private, and I wouldn't use reflection to access it, I decided to create a custom list using the original List(Of T) source code.
However, I noticed that there were no protection against a potential arithmetic overflow if the version ever reached Integer.MaxValue.
Me._version += 1
So I modified the code:
Me._version = If((Me._version = Integer.MaxValue), Integer.MinValue, (Me._version + 1I))
Now begins the fun part; Out of curiosity, I set up a test where I used reflection to set the internal _version
field to max.
Dim flags As BindingFlags = (BindingFlags.Instance Or BindingFlags.NonPublic)
Dim list As New List(Of String)
Dim _version As FieldInfo = GetType(List(Of String)).GetField("_version", flags)
_version.SetValue(list, Integer.MaxValue)
Debug.WriteLine("Count: {0}, Version: {1}", list.Count, _version.GetValue(list))
list.Add("str")
Debug.WriteLine("Count: {0}, Version: {1}", list.Count, _version.GetValue(list))
I was shocked by the results:
Count: 0, Version: 2147483647
Count: 1, Version: -2147483648
There's nothing in the source code that sets the field to minimum if maximum is reached. I even looked at the DynamicallyInvokable attribute, but as far as I can tell it's not relevant.
<__DynamicallyInvokable()> _
Public Sub Add(ByVal item As T)
If (Me._size = Me._items.Length) Then
Me.EnsureCapacity((Me._size + 1))
End If
Me._items(Me._size++) = item
Me._version += 1
End Sub
Private Sub EnsureCapacity(ByVal min As Integer)
If (Me._items.Length < min) Then
Dim num As Integer = IIf((Me._items.Length = 0), 4, (Me._items.Length * 2))
If (num > &H7FEFFFFF) Then
num = &H7FEFFFFF
End If
If (num < min) Then
num = min
End If
Me.Capacity = num
End If
End Sub
<__DynamicallyInvokable()> _
Public Property Capacity As Integer
<TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries"), __DynamicallyInvokable()> _
Get
Return Me._items.Length
End Get
<__DynamicallyInvokable()> _
Set(ByVal value As Integer)
If (value < Me._size) Then
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_SmallCapacity)
End If
If (value <> Me._items.Length) Then
If (value > 0) Then
Dim destinationArray As T() = New T(value - 1) {}
If (Me._size > 0) Then
Array.Copy(Me._items, 0, destinationArray, 0, Me._size)
End If
Me._items = destinationArray
Else
Me._items = List(Of T)._emptyArray
End If
End If
End Set
End Property
The base class library is written in C#, not in VB.NET.
By default, arithmetic operations in C# do not throw overflow exceptions. Instead, they silently wrap the value around, as you have noticed.
This decision was probably made for reasons of efficiency; not everyone agrees with it. More information can be found in the following questions:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With