Dictionary.Add上的NullReferenceException,字典变量不是Null,key不是Null,没有多线程

时间:2013-06-12 08:27:27

标签: .net vb.net exception heisenbug

这里有很多类似的问题,但都没有解释我的情况;所以这里。

我有以下(简化)代码:

' row is a System.Data.DataRow
' _typeProperties is a Dictionary(Of String, PropertyInfo)

Dim data As New Dictionary(Of String, Object)
Dim elem As KeyValuePair(Of String, PropertyInfo)
Dim value As Object = Nothing
Try
    For Each elem In _typeProperties
        value = row.Item(elem.Key)
        data.Add(elem.Key, value)       ' NullReferenceException here
    Next
Catch ex As Exception When MyExceptionFilter(ex, data, elem, value)
End Try

有时我会在指定的行上得到NullReferenceException。这种例外非常罕见,我无法随意重现。但是,我可以修改我的应用程序,将其发送给客户,当然,在几天后它将自行复制。

调用堆栈不是很有帮助:

StackTrace: 
  XXX.RowToType(DataRow row) in C:\XXX.vb:line 645.

此外,正如您所看到的,我在Catch块中包含了一个异常过滤器。在那里我写了一个minidump(异常的调用堆栈完好无损)。这是minidump显示的调用堆栈的相关部分:

  ...
  App.exe!MyExceptionFilter( ex,  data,  elem,  value) Line 627
  App.exe!RowToType( row) Line 647 + 0x1f bytes
  [External Code]   
  App.exe!RowToType(System.Data.DataRow row) Line 645 + 0x112 bytes
  App.exe!SomeClass.get_Item(Integer index) Line 1141 + 0xe bytes   
  user32.dll!_InternalCallWinProc@20()  + 0x23 bytes    
  user32.dll!_UserCallWinProcCheckWow@32()  + 0x693 bytes   
  ...

异常发生在[外部代码]块中的某处;然后执行Catch块的过滤器(第2行和第1行)。

在异常时刻,这些是三个相关变量的值:

  data: Not Nothing; 
  elem: Not Nothing; 
  elem.Value: Not Nothing (Int32 ID)
  elem.Key: Not Nothing
  value: Nothing

所以似乎没有理由继续data.Add抛出NullReferenceException。

正如有些人在其他问题中建议的那样,可能存在一些线程问题。但是,根据定义,我所写的字典只能对一个线程可见。 (当然,我还检查了minidump,以确保没有线程执行相同的代码。)

我可能会默默地忽略这个异常,但我宁愿把它弄清楚。

对于那些感兴趣的人,

编辑。,这里是整个代码:

Private Function RowToType(ByVal row As DataRow) As DataSourceRow
    Dim o = _typeActivator({})
    Dim data As New Dictionary(Of String, Object)
    Dim elem As KeyValuePair(Of String, PropertyInfo) = Nothing
    Dim value As Object = Nothing
    Try
        For Each elem In _typeProperties
            value = row.Item(elem.Key)
            If DBNull.Value.Equals(value) Then value = Nothing
            elem.Value.SetValue(o, value, Nothing)
            data.Add(elem.Key, value)           ' NullReferenceException here
        Next
    Catch ex As Exception When RowToTypeExceptionFilter(ex, row, data, elem, value)
    End Try

    o.Data = data
    Return o
End Function

仅供参考:_typeActivator创建动态类型的实例o;不要认为这与问题有任何关系。

5 个答案:

答案 0 :(得分:0)

value: Nothing是获得Null Reference异常的正当理由。 DataRow中的字符串可能是Nothing(如果列不存在,则会出现异常)。

答案 1 :(得分:0)

无所事事地看着doco。

  
    

将Nothing分配给对象变量时,它不再引用任何对象实例。如果变量先前已引用实例,则将其设置为Nothing不会终止实例本身。实例终止,只有在垃圾收集器(GC)检测到没有活动引用后,才会释放与之关联的内存和系统资源。

  

系统可能认为对象在将其设置为“Nothing”之前具有有效引用。

这可能发生在字典值的设置之外,或者在字典本身内部。链接到键的对象可能会被释放它被设置。

答案 2 :(得分:0)

验证要添加的对象的内容会很有用,它可能是DBNull。

' row is a System.Data.DataRow'
' _typeProperties is a Dictionary(Of String, PropertyInfo)'
Dim data As New Dictionary(Of String, Object)

For Each pair As KeyValuePair(Of String, PropertyInfo) In _typeProperties

    'Validates that _typeProperties has no empty or nothing value as key'
    If String.IsNullOrEmpty(pair.Key) Then Continue For

    'Validates that the row contains the specified column'
    If Not row.Table.Columns.Contains(pair.Key) Then Continue For

    data.Add(pair.Key, If(IsDBNull(row(pair.Key)), "", row(pair.Key)))
    'data.Add(pair.Key, CheckNull(Of String)(row(pair.Key)))'
Next

    ''' <summary>
    ''' Evaluates the entry parameter to <c>DBNull</c>, and returns the default value of data type
    ''' </summary>
    ''' <typeparam name="T">Generic Type</typeparam>
    ''' <param name="Value">Object to evaluate</param>
    ''' <returns>If Value contains data, returns the value casted, otherwise returns the default value of type</returns>
    Public Shared Function CheckNull(Of T)(ByVal Value As Object) As T
        If Value Is Nothing OrElse System.Convert.IsDBNull(Value) Then
            If GetType(T) = GetType(String) Then
                Return DirectCast(DirectCast(String.Empty, Object), T)
            End If
        Else
            Return DirectCast(Value, T)
        End If
    End Function

答案 3 :(得分:0)

我认为你的问题就在这里,row.Item(elem.Key),elem.key不存在,它是抛出null异常的原因。您可以通过检查row.Table.Columns.Contains(elem.key)来检查列。

答案 4 :(得分:0)

正如其他人所建议的那样,调用堆栈中的行号(虽然构建之间是一致的)总是引用错误的行。函数参数(row)实际上是NULL值..神秘解决了。