8

NullReferenceException es una de las excepciones que más preguntas suscita. ¿Qué significa NullReferenceException y cómo se puede solucionar?

Para el NullReferenceException en C# y .Net, véase ¿Qué es una NullReferenceException y cómo solucionarla?

Pikoh
  • 17,305
  • 9
  • 38
  • 54

1 Answers1

12

La NullReferenceException en Visual Basic no es diferente a la de C#. Después de todo, ambos reportan la misma excepción definida en el .Net Framework en la que ambos lenguajes están basados. Las causas que pueden ser exclusivas de Visual Basic son pocas (quizá solo una).

Esta respuesta usará los términos, sintaxis y contexto de Visual Basic. Los ejemplos utilizados estan extraidos de un gran número de antiguas preguntas en Stack Overflow. De esta manera se maximiza su relevancia, utilizando la clase de situaciones que a menudo surgen en los posts. También se adjunta algunas explicaciones mas para aquellos que quizá las necesiten. Un ejemplo similar al tuyo es muy probable que se encuentre aqui.

Notas:

  1. Esto es una respuesta conceptual: no hay nigún código que puedas copiar y pegar en tu proyecto. De lo que se trata es de ayudarte a entender que causa una NullReferenceException (NRE), cómo encontrarla, cómo solucionarla, y cómo evitarla. Una NRE puede tener muchas causas así que no esperes encontrar aquí todas ellas.
  2. Los ejemplos (de los posts en Stack Overflow) no siempre muestran la mejor manera de hacer algo para empezar.
  3. Normalmente usaremos la solución mas sencilla.

Significado Básico

El mensaje "Referencia a objeto no establecida como instancia de un objeto" significa que estás intentando utilizar un objeto que no ha sido inicializado. Esto se puede reducir a una de estas causas:

  • En tu código has declarado una variable de objeto, pero no la has inicializado (no has creado una instancia o 'instanciado')
  • Algo que en tu código se asumía que inicializaría un objeto, no lo hizo
  • Quizá otra parte del código invalidó un objeto que todavía estaba en uso prematuramente

Encontrando la causa

Partiendo de que el problema es una referencia de objeto que es Nothing, la respuesta es examinar los objetos para encontrar cual de ellos es el problema, para a continuación determinar porqué no esta inicializado. Mantén el cursor del ratón encima de las variables y Visual Studio (VS) te mostrará sus valores - el culpable será Nothing.

IDE debug display

Además deberías eliminar cualquier bloque Try/Catch de las partes relevantes de tu código, especialmente aquello que tienen vacío el bloque Catch. Esto hará que tu código falle cuando trate de usar un objeto que es Nothing. Esto es lo que quieres, ya que así identificarás la localización exacta del problema, permitiéndote identificar que objeto es el causante.

Un MsgBox en el bloque Catch que muestre Error en... es de muy poca ayuda. Este método de capturar excepciones provoca ademas muy malas preguntas en Stack Overflow, ya que no puedes describir la excepción real, que objeto la provoca y ni siquiera la línea de código donde ha ocurrido.

También puedes utilizar la ventana Variables Locales (Depurar -> Ventanas -> Variables locales) para examinar tus objetos.

Una vez sepas cual es el problema y donde está, normalmente es muy sencillo de solucionarlo y es más rápido que añadir una nueva pregunta.

Ver también:

Ejemplos y soluciones

Objetos de Clase / Creando una Instancia

Dim reg As Registradora
...
TextBox1.Text = reg.Cantidad         ' NRE

El problema es que Dim no crea un objeto de tipo Registradora; sólo declara una variable llamada reg de ese Tipo. Declarar una variable de objeto y crear una instancia son dos cosas diferentes.

Solución

El operador New es el que se usa normalmente para crear la instancia cuando declaras el objeto:

Dim reg As New Registradora        ' [New] crea la instancia, invoca el constructor

' Otra forma mas larga y explícita:
Dim reg As Registradora = New Registradora        

Cuando la instancia debe crearse mas tarde:

Private reg As Registradora         ' Declaración
  ...
reg = New Registradora()            ' Creamos la instancia

Nota: No uses Dim de nuevo en un procedimiento, incluyendo el constructor (Sub New):

Private reg As Registradora
'...

Public Sub New()
   '...
   Dim reg As New Registradora
End Sub

Esto creará una variable local, reg, que existe sólo en este contexto (sub). La variable reg que tiene como ámbito el módulo y que usarás en todos los demás sitios permanece como Nothing.

Olvidarse del operador New es la causa nº 1 de las preguntas sobre NullReferenceExceptions en la preguntas de Stack Overflow revisadas.

Visual Basic intenta hacer que el proceso sea claro usando repetidamente New: Usando el operador New creas un nuevo objeto y llama a Sub New -- el constructor -- donde tu objeto puede realizar cualquier otra inicialización.

Para dejar esto claro, Dim (o Private) sólo declara una variable y su Type(tipo). El alcance de la variable - si existe para todo el modulo/clase o si es local en un procedimiento - se determina por donde es declarada. Private | Friend | Public define el nivel de acceso, no su Alcance.

Para más información,ver:


Arrays

Los Arrays también deben ser instanciados:

Private arr as String()

Este array solo ha sido declarado, no creado. Hay varias maneras de inicializar un array:

Private arr as String() = New String(10){}
' or
Private arr() As String = New String(10){}

' Para un array local (en un procedimiento) y usando 'Option Infer':
Dim arr = New String(10) {}

Nota: A partir de VS 2010, cuando se inicializa un array local usando un literal y Option Infer, los elementos As <Type> y New son opcionales:

Dim myDbl As Double() = {1.5, 2, 9.9, 18, 3.14}
Dim myDbl = New Double() {1.5, 2, 9.9, 18, 3.14}
Dim myDbl() = {1.5, 2, 9.9, 18, 3.14}

El tipo y tamaño del array size se infieren de los datos que son asignados. Las declaraciones a nivel de Clase/Modulo todavía requieren As <Type> con Option Strict:

Private misDoubles As Double() = {1.5, 2, 9.9, 18, 3.14}

Ejemplo: Array de objetos de clase

Dim arrFoo(5) As Foo

For i As Integer = 0 To arrFoo.Count - 1
   arrFoo(i).Bar = i * 10       ' Excepción
Next

El array ha sido creado, pero los objetos de tipo Foo en el no lo ha sido.

Solución

For i As Integer = 0 To arrFoo.Count - 1
    arrFoo(i) = New Foo()         ' Crear instancia de Foo
    arrFoo(i).Bar = i * 10
Next

Usando una List(Of T) hará que sea bastante difícil tener un elemento sin un objeto válido:

Dim FooList As New List(Of Foo)     ' Lista creadad, pero está vacia
Dim f As Foo                        ' Variable temporal para el bucle
For i As Integer = 0 To 5
    f = New Foo()                    ' Instancia de Foo creada
    f.Bar =  i * 10
    FooList.Add(f)                   ' objeto de tipo Foo añadido a la lista
Next

Para mas información, ver:


Listas y Colecciones

Las colecciones en .NET (de las cuales hay muchas variedades - Listas, Diccionarios, etc.) también deben ser instanciadas o creadas.

Private miLista As List(Of String)
..
miLista.Add("prueba")           ' NullReference

Obtienes la misma excepción por la misma razón - miLista fue únicamente declarada, pero no se creó ninguna instancia. La solución es la misma:

miLista = New List(Of String)

' O crear la instancia al declararla:
Private miLista As New List(Of String)

Un error común es una clase que usa un tipo(Type) de colección :

Public Class Foo
    Private barList As List(Of Bar)

    Friend Function BarCount As Integer
        Return barList.Count
    End Function

    Friend Sub AddItem(newBar As Bar)
        If barList.Contains(newBar) = False Then
            barList.Add(newBar)
        End If
    End Function

Ambos procedimientos provocarán una NRE, porque barList solo fue declarada, no instanciada. Crear una instancia de Foo no creará también una instancia de la barList interna. Puede que la intención fuera hacerlo en el constructor:

Public Sub New         ' Constructor
    ' Cosas que hacer cuando se crea un nuevo Foo...
    barList = New List(Of Bar)
End Sub

Igual que antes,esto no es correcto:

Public Sub New()
    ' Crea otra barList local a este procedimiento
     Dim barList As New List(Of Bar)
End Sub

Para mas información, ver Clase List<T>.


Objetos Proovedores de Datos

Trabajar con bases de datos hace que existan muchas oportunidades para una NullReferenceException ya que puede haber muchos objetos (Command, Connection, Transaction, Dataset, DataTable, DataRows....) en uso al mismo tiempo. Nota: Da igual que proovedor de datos utilices -- MySQL, SQL Server, OleDB, etc. -- los conceptos son los mismos.

Ejemplo 1

Dim da As OleDbDataAdapter
Dim ds As DataSet
Dim MaxRows As Integer

con.Open()
Dim sql = "SELECT * FROM tblfoobar_List"
da = New OleDbDataAdapter(sql, con)
da.Fill(ds, "foobar")
con.Close()

MaxRows = ds.Tables("foobar").Rows.Count      ' Error

Como antes, el Dataset ds ha sido declarado, pero nunca se creó una instancia del mismo. El DataAdapter llena un DataSet que ya existe, no crea uno. En este caso, ya que ds es una variable local, el IDE te lanza un aviso de que esto podría ocurrir:

img

Cuando es declarada como una variable a nivel de módulo/clase, como parece ser el caso con con, el compilador no puede saber si el objeto fue creado por un procedimiento ascendente. No ignores las advertencias.

Solución

Dim ds As New DataSet

Ejemplo 2

ds = New DataSet
da = New OleDBDataAdapter(sql, con)
da.Fill(ds, "Empleados")

txtID.Text = ds.Tables("Empleado").Rows(0).Item(1)
txtID.Name = ds.Tables("Empleado").Rows(0).Item(2)

Un error tipográfico es el problema aquí: Empleados -> Empleado. No existe ningún DataTable que se llame "Empleado", así que una NullReferenceException resulta de intentar acceder a ella. Otro problema potencial es asumir que existirán Items, lo cual puede no ser verdad cuando la consulta SQL contienen una cláusula WHERE.

Solución

Ya que solo se está usando una tabla en el ejemplo, usar Tables(0) evitará errores de ortografía. Examinar Rows.Count puede ayudar también:

If ds.Tables(0).Rows.Count > 0 Then
    txtID.Text = ds.Tables(0).Rows(0).Item(1)
    txtID.Name = ds.Tables(0).Rows(0).Item(2)
End If

Fill es una función que devuelve el numero de filas(Rows) afectadas que tambien puede ser comprobada:

If da.Fill(ds, "Empleados") > 0 Then...

Ejemplo 3

Dim da As New OleDb.OleDbDataAdapter("SELECT TICKET.TICKET_NO,
        TICKET.CUSTOMER_ID, ... FROM TICKET_RESERVATION AS TICKET INNER JOIN
        FLIGHT_DETAILS AS FLIGHT ... WHERE [TICKET.TICKET_NO]= ...", con)
Dim ds As New DataSet
da.Fill(ds)

If ds.Tables("TICKET_RESERVATION").Rows.Count > 0 Then

El DataAdapter proporcionará TableNames como se ve en el ejemplo anterior, pero no analiza los nombres de la tabla de la base de datos. A resultas de esto, ds.Tables("TICKET_RESERVATION") referencia una tabla que no existe.

La solución es la misma, referencia la tabla por su indice:

If ds.Tables(0).Rows.Count > 0 Then

Ver también Clase DataTable.


Rutas de objetos(Object Paths) / Anidados(Nested)

If myFoo.Bar.Items IsNot Nothing Then
   ...

El código solo esta probando Items mientras que myFoo y Bar también podrían ser Nothing. La solución es probar la cadena entera o la tura del los objetos de uno en uno:

If (myFoo IsNot Nothing) AndAlso
    (myFoo.Bar IsNot Nothing) AndAlso
    (myFoo.Bar.Items IsNot Nothing) Then
    ....

AndAlso es importante. Los tests subsecuentes no se ejecutarán una vez se encuentre la primera condición False. Esto permite al código ir comprobando los objetos de nivel en nivel de forma segura, evaluando myFoo.Bar solo después (y si) se determina que myFoo es valido. Las cadenas o rutas de objetos pueden llegar a ser bastante largas cuando se trata con objetos complejos:

myBase.myNodes(3).Layer.SubLayer.Foo.Files.Add("somefilename")

Tampoco es posible referenciar nada por debajo de un objeto nulo. Esto se aplica también a los controles:

myWebBrowser.Document.GetElementById("formfld1").InnerText = "un valor"

Aquí, myWebBrowser o Document podrían ser Nothing, o el elemento formfld1 podría no existir.


UI Controles

Dim cmd5 As New SqlCommand("select Cartons, Pieces, Foobar " _
     & "FROM Invoice where invoice_no = '" & _
     Me.ComboBox5.SelectedItem.ToString.Trim & "' And category = '" & _
     Me.ListBox1.SelectedItem.ToString.Trim & "' And item_name = '" & _
     Me.ComboBox2.SelectedValue.ToString.Trim & "' And expiry_date = '" & _
     Me.expiry.Text & "'", con)

Entre otras cosas, este código no prevee que el usuario podría no haber seleccionado nada en uno o mas controles. ListBox1.SelectedItem podría perfectamente ser Nothing, así que ListBox1.SelectedItem.ToString provocará una NRE.

Solución

Valida los datos antes de usarlos (es recomendable también usar Option Strict y parámetros SQL):

Dim expiry As DateTime         ' para validar un texto de una fecha    If (ComboBox5.SelectedItems.Count > 0) AndAlso
    (ListBox1.SelectedItems.Count > 0) AndAlso
    (ComboBox2.SelectedItems.Count > 0) AndAlso
    (DateTime.TryParse(expiry.Text, expiry) Then

    '... hacer cosas
Else
    MessageBox.Show(...mensaje de error...)
End If

De forma alternativa, podrías usar también (ComboBox5.SelectedItem IsNot Nothing) AndAlso...


Formularios Visual Basic

Public Class Form1

    Private NameBoxes = New TextBox(5) {Controls("TextBox1"), _
                   Controls("TextBox2"), Controls("TextBox3"), _
                   Controls("TextBox4"), Controls("TextBox5"), _
                   Controls("TextBox6")}

    ' lo mismo en un formato diferente:
    Private boxList As New List(Of TextBox) From {TextBox1, TextBox2, TextBox3 ...}

    ' NRE Inmediato:
    Private unaVariable As String = Me.Controls("TextBox1").Text

Esta es una forma muy común de obtener una NRE. En C#, dependiendo de cómo esté programado, el IDE reportaría que Controls no existe en el contexto actual, o "No se puede tener acceso a un miembro no estático desde un ámbito estático". Así que, en cierto modo esta es una situación que sólo puede darse en VB. Es también una situación compleja, porque puede causar un fallo en cascada.

Los arrays y las colecciones no se pueden inicializar de esta manera. Este código de inicialización se ejecutará antes de que el constructor cree el Formulario o los controles(Controls). Como resultado:

  • Las Listas y Colecciones estarán simplemente vacías
  • El Array contendrá cinco elementos de Nothing
  • La asignación a unaVariable resultará en una NRE inmediata, porque Nothing 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 evento Form 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 evento Form Load se ejecutará después de la NRE, un montón de otras cosas podrían quedarse sin inicializar.

Sub Form_Load(..._
   '...
   Dim name As String = NameBoxes(2).Text        ' NRE
   ' ...
   ' Mas código (que probablemente no se ejecutará)
   ' ...
End Sub

Nota esto aplica para cualquiera y todas las referencias a controles y componentes haciendo esto ilegal donde se encuentre:

Public Class Form1

    Private myFiles() As String = Me.OpenFileDialog1.FileName & ...
    Private dbcon As String = OpenFileDialog1.FileName & ";Jet Oledb..."
    Private studentName As String = TextBox13.Text

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 a InitializeComponent:

' Declaración a nivel de módulo
Private NameBoxes as TextBox()
Private studentName As String

' Form Load, Form Shown or Sub New:
'
' (ilegal usando OPTION STRICT)
NameBoxes = New TextBox() {Me.Controls("TextBox1"), Me.Controls("TestBox2"), ...)
studentName = TextBox32.Text           

El código del array podría no estar a salvo todavía. Cualquier control que esté en un control contenedor (como un GroupBox o Panel) no se encontrará en Me.Controls; estarán en la colección Controls 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: VS te enseña el error

"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:

' Declaración
Private NameBoxes As TextBox()

' Inicializacion -  simple y fácil de leer, difícil de "chapucear":
NameBoxes = New TextBox() {TextBox1, TextBox2, ...)

' Inicializar una lista
NamesList = New List(Of TextBox)({TextBox1, TextBox2, TextBox3...})
' o
NamesList = New List(Of TextBox)
NamesList.AddRange({TextBox1, TextBox2, TextBox3...})

Function que devuelve Nothing

Private bars As New List(Of Bars)        ' Declarado y creado

Public Function BarList() As List(Of Bars)
    bars.Clear
    If someCondition Then
        For n As Integer = 0 to someValue
            bars.Add(GetBar(n))
        Next n
    Else
        Exit Function
    End If

    Return bars
End Function

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 sustituyendo Exit Function con Return Nothing, pero eso no resuelve el problema. Cualquier cosa que intente usar el valor devuelto cuando someCondition = False resultará en una NRE:

bList = myFoo.BarList()
For Each b As Bar in bList      ' EXCEPCION
      ...

Solución

Cambia Exit Function en la función por Return bList. Devolver una lista(List) vacía no es lo mismo que devolver Nothing. Si existe la posibilidad de que un objeto devuelto pueda ser Nothing, compruébalo antes de usarlo:

 bList = myFoo.BarList()
 If bList IsNot Nothing Then...

Try/Catch mal implementado

Un Try/Catch mal implementado puede ocultar donde está el problema y provocar como consecuencia otros nuevos:

Dim dr As SqlDataReader
Try
    Dim lnk As LinkButton = TryCast(sender, LinkButton)
    Dim gr As GridViewRow = DirectCast(lnk.NamingContainer, GridViewRow)
    Dim eid As String = GridView1.DataKeys(gr.RowIndex).Value.ToString()
    ViewState("username") = eid
    sqlQry = "select FirstName, Surname, DepartmentName, ExtensionName, jobTitle,
             Pager, mailaddress, from employees1 where username='" & eid & "'"
    If connection.State <> ConnectionState.Open Then
        connection.Open()
    End If
    command = New SqlCommand(sqlQry, connection)

    'Mas código

    dr = command.ExecuteReader()
    If dr.Read() Then
        lblFirstName.Text = Convert.ToString(dr("FirstName"))
        ...
    End If
    mpe.Show()
Catch

Finally
    command.Dispose()
    dr.Close()             ' <-- NRE
    connection.Close()
End Try

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 que Catch no haga nada, Finally intenta hacer limpieza, pero como no puedes cerrar(Close) un DataReader nulo, una nueva NullReferenceException 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 bloque Finally. En otras situaciones, un Catch 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

For Each row As DataGridViewRow In dgvPlanning.Rows
    If Not IsDBNull(row.Cells(0).Value) Then
        ...

La funciónIsDBNull se usa para comprobar si un valor es igual que System.DBNull: De MSDN:

El valor System.DBNull indica que el objeto representa datos que faltan o no existen. DBNull no es lo mismo que Nothing, que indica que una variable aun no ha sido inicializada.

Solución

If row.Cells(0) IsNot Nothing Then ...

Como antes, puedes comprobar primero Nothing, y después por un valor específico:

If (row.Cells(0) IsNot Nothing) AndAlso (IsDBNull(row.Cells(0).Value) = False) Then

Ejemplo 2

Dim getFoo = (From f In dbContext.FooBars
               Where f.something = something
               Select f).FirstOrDefault

If Not IsDBNull(getFoo) Then
    If IsDBNull(getFoo.user_id) Then
        txtFirst.Text = getFoo.first_name
    Else
       ...

FirstOrDefault devuelve el primer elemento o el valor por defecto, que en el caso de tipos de referencia es Nothing y nunca DBNull:

If getFoo IsNot Nothing Then...

Controles

Dim chk As CheckBox

chk = CType(Me.Controls(chkName), CheckBox)
If chk.Checked Then
    Return chk
End If

Si un CheckBox con chkName no se encuentra (o existe en un GroupBox), entonces chk será Nothing y el intento de referenciar cualquier propiedad resultará en una excepción.

Solución

If (chk IsNot Nothing) AndAlso (chk.Checked) Then ...

El DataGridView

El DGV tiene algunos "caprichos" que se ven periódicamente:

dgvBooks.DataSource = loan.Books
dgvBooks.Columns("ISBN").Visible = True       ' NullReferenceException
dgvBooks.Columns("Title").DefaultCellStyle.Format = "C"
dgvBooks.Columns("Author").DefaultCellStyle.Format = "C"
dgvBooks.Columns("Price").DefaultCellStyle.Format = "C"

Si dgvBooks tiene AutoGenerateColumns = 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:

dgvBooks.Columns(0).Visible = True

Ejemplo 2 — Cuidado con NewRow

xlWorkSheet = xlWorkBook.Sheets("sheet1")

For i = 0 To myDGV.RowCount - 1
    For j = 0 To myDGV.ColumnCount - 1
        For k As Integer = 1 To myDGV.Columns.Count
            xlWorkSheet.Cells(1, k) = myDGV.Columns(k - 1).HeaderText
            xlWorkSheet.Cells(i + 2, j + 1) = myDGV(j, i).Value.ToString()
        Next
    Next
Next

Cuando tu DataGridView tiene AllowUserToAddRows a True (pro defecto), las Cells en la fila vacía/nueva al final contendrán todas Nothing. 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 propiedad IsNewRow para determinar si es la última fila. Esto funciona aunque AllowUserToAddRows sea true o false:

For Each r As DataGridViewRow in myDGV.Rows
    If r.IsNewRow = False Then
         ' puedes usar esta fila

Si usas un bucle For n, modifica el contador de filas o usa Exit For cuando IsNewRow es true.


My.Settings (StringCollection)

Bajo ciertas circunstancias, intentado usar un elemento de My.Settings que es un StringCollection puede resultar en una NullReference la primera vez que lo usas. La solución es la misma, aunque no tan obvia. Considera:

My.Settings.FooBars.Add("ziggy")         ' foobars es una string collection

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:

If My.Settings.FooBars Is Nothing Then
    My.Settings.FooBars = New System.Collections.Specialized.StringCollection
End If

Típicamente, la colección Settings solo necesitaría ser inicializada la primera vez que se ejecute la aplicación. Una solución alternativa es añadir un valor inicial a la colección en Proyecto -> Settings | FooBars, guardar el proyecto y posteriormente eliminar el valor falso.


Puntos Clave

Probablemente olvidaste el operador New.

o

Algo que asumías que funcionaría perfectamente para devolver un objeto inicializado, no lo ha hecho.

No ignores los avisos del compilador (nunca) y usa Option Strict On (siempre).


MSDN Clase NullReferenceException


Esta respuesta es una traducción de esta respuesta de Stack Overflow

Pikoh
  • 17,305
  • 9
  • 38
  • 54