NullReferenceException
这是引发最多问题的例外之一。这是什么意思NullReferenceException
,如何解决?
对于NullReferenceException
C# 和 .Net,请参阅什么是 NullReferenceException 以及如何修复它?
NullReferenceException
这是引发最多问题的例外之一。这是什么意思NullReferenceException
,如何解决?
对于NullReferenceException
C# 和 .Net,请参阅什么是 NullReferenceException 以及如何修复它?
Visual Basic
NullReferenceException
中的那个与C#中的没有什么不同。毕竟,它们都报告了两种语言都基于的 .Net Framework 中定义的相同异常。可能是 Visual Basic 独有的原因很少(可能只有一个)。此答案将使用 Visual Basic 术语、语法和上下文。使用的示例取自 Stack Overflow 上的大量旧问题。通过这种方式,它的相关性被最大化,使用 帖子中经常出现的那种情况。还为可能需要它们的人附加了一些更多的解释。很可能在这里找到与您类似的示例。
成绩:
NullReferenceException
NRE 的原因、如何找到它、如何修复它以及如何避免它。NRE 可能有很多原因,所以不要指望在这里找到所有原因。基本含义
消息“对象引用未设置为对象的实例” 表示您正在尝试使用尚未初始化的对象。这可以缩小到以下原因之一:
寻找原因
假设问题是一个对象引用,即
Nothing
,答案是检查对象以找出问题所在,然后确定它未初始化的原因。将鼠标光标悬停在变量上,Visual Studio (VS) 将向您显示它们的值 - 罪魁祸首将是Nothing
.您还应该从代码的相关部分中删除任何 Try/Catch 块,尤其是那些具有空 Catch 块的部分。这将导致您的代码在尝试使用
Nothing
. 这就是您想要的,因为这将查明问题的确切位置,从而使您能够确定是哪个对象导致了它。显示
MsgBox
的 Catch 块中Error en...
的一个非常无用。这种捕获异常的方法也会在 Stack Overflow 上引发非常糟糕的问题,因为您无法描述实际的异常、导致它的对象是什么,甚至无法描述它发生的代码行。您还可以使用窗口
Variables Locales
(Debug -> Windows -> Locals)来检查您的对象。一旦您知道问题是什么以及问题出在哪里,通常比添加新问题更容易解决并且更快。
也可以看看:
示例和解决方案
类对象/创建实例
问题是
Dim
它没有创建类型的对象Registradora
;它只是声明了reg
该类型的命名变量。声明对象变量和创建它的实例是两件不同的事情。解决方案
运算符
New
是声明对象时通常用于创建实例的运算符:稍后应创建实例时:
注意:不要在过程中再次使用
Dim
,包括构造函数(Sub New
):这将创建一个局部变量,
reg
,它只存在于这个(子)上下文中。reg
您作为模块拥有的变量ámbito
以及您将在其他任何地方使用的变量仍然保留为Nothing
.为了清楚起见,
Dim
(oPrivate
) 只是声明了一个变量及其Type
( type )。变量的范围——它是否存在于整个模块/类或者它是否是一个过程的局部变量——取决于它的声明位置。Private | Friend | Public
定义访问级别,而不是其Scope。有关更多信息,请参阅:
数组
数组也必须实例化:
这个数组只是被声明了,没有被创建。有几种初始化数组的方法:
注意:从 VS 2010 开始,当使用文字 and 初始化本地数组时
Option Infer
,元素As <Type>
andNew
是可选的:数组大小的类型和大小是从分配的数据中推断出来的。类/模块级声明仍然
As <Type>
需要Option Strict
:示例:类对象数组
数组已创建,但其中的类型对象尚未创建
Foo
。解决方案
使用 one
List(Of T)
将使没有有效对象的元素变得非常困难:有关更多信息,请参阅:
列表和集合
.NET 中的集合(其中有很多种 - 列表、字典等)也必须实例化或创建。
由于相同的原因,您会得到相同的异常——它
miLista
只是被声明了,但没有创建实例。解决方法是一样的:一个常见的错误是使用
Type
集合类型()的类:这两个过程都会导致 NRE,因为
barList
它只是被声明的,没有被实例化。创建一个实例Foo
不会同时创建一个barList
内部实例。也许目的是在构造函数中做到这一点:和以前一样,这是不正确的:
有关详细信息,请参阅类
List<T>
。数据提供者对象
使用数据库意味着 NullReferenceException 有很多机会,因为可以同时使用许多对象 ( ,
Command
,Connection
,Transaction
,Dataset
.... )。 注意:你使用什么数据提供者并不重要——MySQL、SQL Server、OleDB 等。——概念是一样的。DataTable
DataRows
示例 1
和以前一样,已经声明了数据集
ds
,但从未创建过它的实例。他DataAdapter
填充一个DataSet
已经存在的,而不是创造一个。在这种情况下,由于它ds
是一个局部变量,IDE 会警告您可能会发生这种情况:当在模块/类级别声明为变量时
con
,编译器无法判断对象是否是由自下而上的过程创建的。不要忽视警告。解决方案
示例 2
错字是这里的问题:
Empleados
->Empleado
。没有DataTable
一个叫做“员工”的人,所以一个人NullReferenceException
试图访问它。另一个潜在的问题是假设它们将存在Items
,当 SQL 查询包含 WHERE 子句时,这可能不是真的。解决方案
由于示例中只使用了一个表,因此使用
Tables(0)
将避免拼写错误。浏览Rows.Count
也可以帮助:Fill
是一个返回Rows
受影响行数的函数(),也可以检查:示例 3
将
DataAdapter
提供TableNames
如上例所示,但不解析数据库表名。结果,它ds.Tables("TICKET_RESERVATION")
引用了一个不存在的表。解决方法是一样的,通过索引引用表:
另请参见DataTable 类。
对象路径(Object Paths)/嵌套(Nested)
代码只是在测试
Items
whilemyFoo
也Bar
可能是Nothing
. 解决方案是一次尝试整个链或对象的真正:AndAlso
这很重要。一旦满足第一个条件,后续测试将不会运行False
。这允许代码安全地逐级检查对象,myFoo.Bar
仅在确定对象myFoo
有效之后(并且如果)进行评估。处理复杂对象时,对象链或路径可能会变得很长:也不能引用空对象下面的任何内容。这也适用于控件:
在这里,它们可能是
myWebBrowser
,或者元素不存在。Document
Nothing
formfld1
用户界面控件
除其他事项外,此代码并未预期用户可能未在一个或多个控件中选择任何内容。
ListBox1.SelectedItem
很可能是Nothing
,所以它会ListBox1.SelectedItem.ToString
导致 NRE。解决方案
使用前验证数据(也推荐使用
Option Strict
和SQL参数):或者,您也可以使用
(ComboBox5.SelectedItem IsNot Nothing) AndAlso...
Visual Basic 窗体
这是获得 NRE 的一种非常常见的方式。在 C# 中,根据编程方式,IDE 会报告
Controls
它在当前上下文中不存在,或者“无法从静态范围访问非静态成员”。所以在某种程度上,这是一种只能在 VB 中发生的情况。这也是一个复杂的情况,因为它可能导致级联故障。数组和集合不能以这种方式初始化。此初始化代码将在构造函数创建表单或控件( )之前
Controls
执行。因此:Nothing
unaVariable
resultará en una NRE inmediata, porqueNothing
no tiene una propiedad.Text
Referenciar los elementos del array mas tarde resultará en una NRE. Si lo haces en el
Form_Load
, debido a un bug extraño, el IDE podría no reportar la excepción cuando ocurra. La excepción aparecerá mas tarde cuando tu código intent usar el array. Esta "excepción silenciosa" es detallada en este post (en inglés). Para nuestro propósito, la clave es que cuando algo catastrófico ocurre al crear un formulario (Sub New
o eventoForm Load
), las excepciones podrían no reportarse. El flujo sale del procedimiento y simplemente muestra el formulario.Dado que ningún código mas en tu
Sub New
o en tu eventoForm Load
se ejecutará después de la NRE, un montón de otras cosas podrían quedarse sin inicializar.Nota esto aplica para cualquiera y todas las referencias a controles y componentes haciendo esto ilegal donde se encuentre:
Solución parcial
Es curioso que VB no de al menos un aviso, pero la solución es declarar los contenedores a nivel de formulario, pero inicializarles en el evento form load cuando los controles realmente existen. Esto se puede hacer también en
Sub New
siempre y cuando tu código esté después de la llamada aInitializeComponent
:El código del array podría no estar a salvo todavía. Cualquier control que esté en un control contenedor (como un
GroupBox
oPanel
) no se encontrará enMe.Controls
; estarán en la colecciónControls
de ese Panel o GroupBox. Tampoco se devolverá ningun control si su nombre está mal escrito ("TeStBox2"
). En esos casos, en esos elementos del array se almacenaráNothing
y resultará en una NRE cuando intentes referenciarlos.Estos deberían ser fáciles de encontrar ahora que ya sabes lo que estás buscando:
"Button2" está contenido en un
Panel
Solución
En lugar de usar referencias indirectas por nombre usando la colección
Controls
del formulario, usa la referencia del control:Function que devuelve Nothing
Este es un caso donde el IDE mostrará un aviso de que 'no todas las rutas devuelve un valor lo que puede resultar en una
NullReferenceException
'. Puedes suprimir el aviso sustituyendoExit Function
conReturn Nothing
, pero eso no resuelve el problema. Cualquier cosa que intente usar el valor devuelto cuandosomeCondition = False
resultará en una NRE:Solución
Cambia
Exit Function
en la función porReturn bList
. Devolver una lista(List
) vacía no es lo mismo que devolverNothing
. Si existe la posibilidad de que un objeto devuelto pueda serNothing
, compruébalo antes de usarlo:Try/Catch mal implementado
Un Try/Catch mal implementado puede ocultar donde está el problema y provocar como consecuencia otros nuevos:
Este caso es el de un objeto que no ha sido creado como se esperaba, pero también demuestra la "extrema" utilidad de un
Catch
vacío.Hay una coma de más en el SQL (después de 'mailaddress') que causa una excepción en
.ExecuteReader
. Después de queCatch
no haga nada,Finally
intenta hacer limpieza, pero como no puedes cerrar(Close
) unDataReader
nulo, una nuevaNullReferenceException
es lanzada.Un bloque
Catch
vacío es muy peligroso y nada recomendable. Este OP estaba sorprendido intentando entender porqué estaba obteniendo una NRE en el bloqueFinally
. En otras situaciones, unCatch
vacío podría resultar en algo mucho mas grave y hacerte perder mucho tiempo probando cosas equivocadas en el lugar equivocado para encontrar el problema. (La "excepción silenciosa" descrita anteriormente proporciona el mismo entretenimiento añadido.)Solución
No uses bloques Try/Catch vacios - deja que el código falle para que puedas a) identificar la causa b) indetificar la localización y c) aplicar la solución correcta. Los bloques Try/Catch no se hicieron para ocultar las excepciones de la persona cualificada para solucionarlas - el desarrollador.
DBNull no es lo mismo que Nothing
La función
IsDBNull
se usa para comprobar si un valor es igual queSystem.DBNull
: De MSDN:Solución
Como antes, puedes comprobar primero
Nothing
, y después por un valor específico:Ejemplo 2
FirstOrDefault
devuelve el primer elemento o el valor por defecto, que en el caso de tipos de referencia esNothing
y nuncaDBNull
:Controles
Si un
CheckBox
conchkName
no se encuentra (o existe en unGroupBox
), entonceschk
seráNothing
y el intento de referenciar cualquier propiedad resultará en una excepción.Solución
El DataGridView
El DGV tiene algunos "caprichos" que se ven periódicamente:
Si
dgvBooks
tieneAutoGenerateColumns = True
, creará las columnas pero no las pondrá nombre, así que el código de arriba falla cuando trata de referenciarlas por nombre.Solución
Pon nombre a las columnas manualmente, o referéncialas por índice:
Ejemplo 2 — Cuidado con NewRow
Cuando tu
DataGridView
tieneAllowUserToAddRows
aTrue
(pro defecto), lasCells
en la fila vacía/nueva al final contendrán todasNothing
. Los intentos de usar los contenidos de ésta (por ejemplo,ToString
) resultarán en una NRE.Solución
Usa un bucle
For/Each
y pregunta por la propiedadIsNewRow
para determinar si es la última fila. Esto funciona aunqueAllowUserToAddRows
sea true o false:Si usas un bucle
For n
, modifica el contador de filas o usaExit For
cuandoIsNewRow
es true.My.Settings (StringCollection)
Bajo ciertas circunstancias, intentado usar un elemento de
My.Settings
que es unStringCollection
puede resultar en una NullReference la primera vez que lo usas. La solución es la misma, aunque no tan obvia. Considera:Ya que VB está gestionando Settings por ti, es razonable esperar que inicialice la colección. Lo hará, pero solo si previamente has añadido un elemento inicial a la colección (en el editor de Settings). Ya que la colección esta siendo (aparentemente) inicializada cuando un elemento es añadido, permanece a
Nothing
cuando no hay elementos en el editor de Settings para añadir.Solución
Inicializa la colección de settings en el manejador del evento
Load
del formulario, si es necesario:通常,该集合
Settings
只需要在应用程序第一次运行时进行初始化。另一种解决方案是在Project -> Settings |中为集合添加初始值。FooBars,保存项目,然后删除 false 值。关键点
您可能忘记了操作员
New
。任何一个
您认为可以完美返回已初始化对象的东西没有。
不要忽略编译器警告(从不)并使用
Option Strict On
(总是)。MSDN 类 NullReferenceException
这个答案是这个Stack Overflow答案的翻译