List(Of T) saved by unknown hero?
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?
DR:
正如您在查看源代码时看到的,没有针对潜在算术溢出的保护。但有一件事是,如果该值为最大值,则将该值设置为
1 2 3 4 5 6 7 8 | <__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 |
详细的
由于list(of t)的内部项数组是私有的,我不会使用反射来访问它,所以我决定使用原始list(of t)源代码创建一个自定义列表。
但是,我注意到,如果版本达到integer.maxvalue,就没有针对潜在算术溢出的保护。
1 | Me._version += 1 |
所以我修改了代码:
1 | Me._version = If((Me._version = Integer.MaxValue), Integer.MinValue, (Me._version + 1I)) |
现在开始有趣的部分;出于好奇,我设置了一个测试,在这里我使用反射将内部
1 2 3 4 5 6 7 8 | 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)) |
结果令我震惊:
Count: 0, Version: 2147483647
Count: 1, Version: -2147483648
如果达到最大值,则源代码中没有将字段设置为最小值的内容。我甚至研究了dynamicallyInvokable属性,但据我所知,它并不相关。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | <__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 |
基类库是用C语言编写的,而不是用vb.net编写的。
默认情况下,C中的算术运算不会引发溢出异常。相反,正如您所注意到的,它们会悄悄地将值包装起来。
这一决定可能是出于效率的考虑,并非所有人都同意。有关更多信息,请参阅以下问题:
- 为什么在默认情况下不使用算术溢出检查?
- 为什么在默认情况下,语言不会在整数溢出时引发错误?