关于.net:由未知英雄保存的List(Of T)?

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 to Integer.MaxValue and add a new item?


DR:

正如您在查看源代码时看到的,没有针对潜在算术溢出的保护。但有一件事是,如果该值为最大值,则将该值设置为Integer.MinValue

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))

现在开始有趣的部分;出于好奇,我设置了一个测试,在这里我使用反射将内部_version字段设置为max。

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中的算术运算不会引发溢出异常。相反,正如您所注意到的,它们会悄悄地将值包装起来。

这一决定可能是出于效率的考虑,并非所有人都同意。有关更多信息,请参阅以下问题:

  • 为什么在默认情况下不使用算术溢出检查?
  • 为什么在默认情况下,语言不会在整数溢出时引发错误?