c# - 意味 - オブジェクト参照がオブジェクト インスタンスに設定されていません。 対処法




NullReferenceExceptionとは何ですか?どうすれば修正できますか? (20)

NullReference例外 - Visual Basic

Visual BasicNullReference Exceptionは、 C#の NullReference Exceptionと同じです。 結局のところ、彼らはどちらも両方の.NET Frameworkで定義されている同じ例外を報告しています。 Visual Basicに固有の原因はまれです(おそらく1つだけです)。

この回答は、Visual Basicの用語、構文、およびコンテキストを使用します。 使用された例は、多数の過去のスタックオーバーフローの質問から来ています。 これは、投稿でよく見られる種類の状況を使用して関連性を最大化することです。 それを必要とするかもしれない人たちのために、もう少し説明があります。 あなたのものに似た例がここにリストされています。

注意:

  1. これはコンセプトベースです。プロジェクトに貼り付けるコードはありません。 NullReferenceException (NRE)の原因、見つけ方、修正方法、回避方法を理解するのに役立ちます。 NREは多くの方法で発生する可能性があるため、これはあなたの唯一の遭遇ではありません。
  2. スタックオーバーフローの投稿の例は、最初に何かを行う最良の方法を常に示すわけではありません。
  3. 通常、最も簡単な方法が使用されます。

基本的な意味

「オブジェクトのインスタンスに設定されていません」というメッセージは、初期化されていないオブジェクトを使用しようとしていることを意味します。 これは次のいずれかに沸きます:

  • あなたのコードオブジェクト変数を宣言しましたが、それを初期化しませんでした(インスタンスを作成するか、インスタンスしました)
  • あなたのコードがオブジェクトを初期化すると仮定したものは、
  • おそらく、他のコードがまだ使用中のオブジェクトを早期に無効化しました

原因を見つける

問題はNothingオブジェクト参照であるため、答えはどのオブジェクトかを調べることです。 次に、それが初期化されていない理由を特定します。 さまざまな変数の上にマウスを置くと、Visual Studio(VS)に値が表示されます。原因はNothingです。

また、関連するコードからTry / Catchブロックを削除する必要があります。特に、Catchブロックに何もないブロックを削除する必要があります。 これは、 Nothingあるオブジェクトを使用しようとするとコードがクラッシュする原因になります。 これは 、問題の正確な位置を特定し、それを引き起こすオブジェクトを識別できるようにするために必要なものです。

Error while...を表示するMsgBox内のMsgBoxはほとんど役に立ちません。 このメソッドはまた、実際の例外、関連するオブジェクト、または発生する行のコードを記述することができないため、 非常に重大スタックオーバーフローの問題つながります。

また、 Locals Windowデバッグ - >ウィンドウ - >ローカル )を使用してオブジェクトを検査することもできます。

問題の内容と場所が分かれば、通常、新しい質問を投稿するよりも修正するのが簡単で、速くなります。

参照:

例と救済

クラスオブジェクト/インスタンスの作成

Dim reg As CashRegister
...
TextBox1.Text = reg.Amount         ' NRE

問題は、 DimがCashRegister オブジェクトを作成しないことです。 その型のregという名前の変数だけを宣言します。 オブジェクト変数を宣言してインスタンスを作成することは、2つの異なることです。

対処法

New演算子は、宣言時にインスタンスを作成するためによく使用されます。

Dim reg As New CashRegister        ' [New] creates instance, invokes the constructor

' Longer, more explicit form:
Dim reg As CashRegister = New CashRegister

後でインスタンスを作成するのが適切な場合のみ:

Private reg As CashRegister         ' Declare
  ...
reg = New CashRegister()            ' Create instance

注意:コンストラクタ( Sub New )を含むプロシージャで、再度Dim使用しないでください

Private reg As CashRegister
'...

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

これにより、そのコンテキスト(sub)にのみ存在するローカル変数regが作成されます。 他の場所で使用するモジュールレベルのScopeを持つreg変数は、 Nothingも残っていNothing

New演算子が見つからない場合は 、Stack Overflowの質問で確認されたNullReference Exceptions #1原因となります。

Visual Basicでは、 Newを使用してプロセスを繰り返しクリアするよう試みます。New演算子を使用すると、 新しいオブジェクトが作成され、 Sub New - コンストラクターが呼び出され、オブジェクトは他の初期化を実行できます。

明確にするため、 Dim (またはPrivate )は変数とそのTypeだけを宣言します。 変数のスコープは、モジュール/クラス全体に存在するか、プロシージャに対してローカルであるかにかかわらず、宣言された場所によって決まります。 Private | Friend | Public Private | Friend | Publicは、 スコープではなくアクセスレベルを定義します。

詳細については、以下を参照してください。

配列

配列もインスタンス化する必要があります。

Private arr as String()

この配列は宣言されているだけで、作成されていません。 配列を初期化するには、いくつかの方法があります。

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

' For a local array (in a procedure) and using 'Option Infer':
Dim arr = New String(10) {}

注意:VS 2010以降、リテラルとOption Inferを使用してローカル配列を初期化するとき、 As <Type>およびNew要素はオプションです。

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}

データタイプと配列サイズは、割り当てられるデータから推測されます。 クラス/モジュールレベルの宣言では、 As <Type>Option Strictする必要があります。

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

例:クラスオブジェクトの配列

Dim arrFoo(5) As Foo

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

配列は作成されていますが、 Fooオブジェクトには含まれていません。

対処法

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

List(Of T)を使用すると、有効なオブジェクトがない要素を持つことが非常に困難になります。

Dim FooList As New List(Of Foo)     ' List created, but it is empty
Dim f As Foo                        ' Temporary variable for the loop

For i As Integer = 0 To 5
    f = New Foo()                    ' Foo instance created
    f.Bar =  i * 10
    FooList.Add(f)                   ' Foo object added to list
Next

詳細については、以下を参照してください。

リストとコレクション

.NETコレクション(その中には、リスト、ディクショナリなど)もインスタンス化または作成する必要があります。

Private myList As List(Of String)
..
myList.Add("ziggy")           ' NullReference

同じ理由で同じ例外が発生しますmyListは宣言されただけですが、インスタンスは作成されませんでした。 治療法は同じです:

myList = New List(Of String)

' Or create an instance when declared:
Private myList As New List(Of String)

一般的な見落としは、コレクションを使用するクラスです。 Type

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

barListはインスタンス化されておらず宣言されているだけなので、どちらのプロシージャでもNREがbarListます。 Fooのインスタンスを作成しても、内部のbarListインスタンスは作成されません。 コンストラクタでこれを行うことが意図されている可能性があります:

Public Sub New         ' Constructor
    ' Stuff to do when a new Foo is created...
    barList = New List(Of Bar)
End Sub

これまでのように、これは間違っています:

Public Sub New()
    ' Creates another barList local to this procedure
     Dim barList As New List(Of Bar)
End Sub

詳細については、 List(Of T)クラスを参照してください。

データプロバイダオブジェクト

一度に多くのオブジェクト( CommandConnectionTransactionDatasetDataTableDataRows ....)が使用できるので、データベースを使った作業はNullReferenceの多くの機会をDataRowsます。 注:使用しているデータプロバイダ(MySQL、SQL Server、OleDBなど)は問わず、 概念は同じです。

例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

以前のように、 ds Datasetオブジェクトは宣言されましたが、インスタンスは決して作成されませんでした。 DataAdapterは、既存のDataSetを作成し、作成しません。 この場合、 dsはローカル変数であるため、IDEはこれが起こる可能性があること警告します

conの場合のように、モジュール/クラスレベルの変数として宣言された場合、コンパイラはオブジェクトが上流プロシージャによって作成されたかどうかを知ることができません。 警告を無視しないでください。

対処法

Dim ds As New DataSet

例2

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

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

タイプミスはここでは問題です: EmployeesEmployee 。 「Employee」という名前のDataTableが作成されていないため、 NullReferenceExceptionそのNullReferenceExceptionにアクセスしようとします。 もう1つの潜在的な問題は、SQLにWHERE句が含まれている場合にそうでないItemsがあると想定することです。

対処法

これは1つのテーブルを使用するため、 Tables(0)を使用するとスペルミスが回避されます。 Rows.Countを調べることも役立ちます:

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は影響を受けたRowsの数を返す関数であり、テストすることもできます。

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

例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

DataAdapterは前の例のようにTableNamesを提供しますが、SQLまたはデータベーステーブルの名前は解析しません。 その結果、 ds.Tables("TICKET_RESERVATION")存在しないテーブルを参照します。

対処法は同じで、索引で表を参照してください。

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

DataTableクラスも参照してください。

オブジェクトパス/入れ子

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

コードはItemsをテストするだけですが、 myFooBar両方がNothingでもmyFooません。 対処法は、オブジェクトのチェーン全体またはパス全体を一度に1つずつテストすることです。

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

AndAlso重要です。 最初のFalse条件に遭遇した場合、その後のテストは実行されません。 これにより、コードは一度に1つのオブジェクトに「ドリル」することができ、 myFoo.Bar評価した後(およびif) myFooが有効であると判定されます。 オブジェクトのチェーンやパスは、複雑なオブジェクトをコーディングするときにかなり長くなることがあります。

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

nullオブジェクトの「下流」を参照することはできません。 これはコントロールにも適用されます。

myWebBrowser.Document.GetElementById("formfld1").InnerText = "some value"

ここでは、 myWebBrowserまたはDocumentがNothingであるか、 formfld1要素が存在しない可能性があります。

UIコントロール

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)

とりわけ、このコードでは、ユーザーが1つまたは複数のUIコントロールで何かを選択していないことが予想されます。 ListBox1.SelectedItemNothingなのでListBox1.SelectedItem.ToStringはNREになります。

対処法

それを使用する前にデータを検証します( Option StrictパラメータとSQLパラメータも使用します)。

Dim expiry As DateTime         ' for text date validation
If (ComboBox5.SelectedItems.Count > 0) AndAlso
    (ListBox1.SelectedItems.Count > 0) AndAlso
    (ComboBox2.SelectedItems.Count > 0) AndAlso
    (DateTime.TryParse(expiry.Text, expiry) Then

    '... do stuff
Else
    MessageBox.Show(...error message...)
End If

または、 (ComboBox5.SelectedItem IsNot Nothing) AndAlso...使用することもできます(ComboBox5.SelectedItem IsNot Nothing) AndAlso...

Visual Basicフォーム

Public Class Form1

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

    ' same thing in a different format:
    Private boxList As New List(Of TextBox) From {TextBox1, TextBox2, TextBox3 ...}

    ' Immediate NRE:
    Private somevar As String = Me.Controls("TextBox1").Text

これは、NREを取得するためのかなり一般的な方法です。C#では、どのようにコード化されているかに応じて、IDEはControls現在のコンテキストに存在しないか、または「非静的メンバーを参照できません」と報告します。だから、ある程度、これはVBのみの状況です。それは失敗カスケードにつながる可能性もあるため、複雑です。

この方法で配列とコレクションを初期化することはできません。この初期化コードは、コンストラクタがまたはを作成するに実行されます。結果として:FormControls

  • リストとコレクションは単に空です
  • 配列には、Nothingの5つの要素が含まれます
  • somevar何も持っていませんので、割り当てはすぐにNREになります.Textプロパティを

後で配列要素を参照すると、NREが返されます。これを行うForm_Loadと、奇妙なバグのために、IDE 例外が発生したときに例外を報告しないことがあります。コードが配列を使用しようとすると、後で例外がポップアップします。この「静かな例外」はこの記事で詳しく述べられています。私たちの目的のために、キーは、フォーム(Sub NewまたはForm Loadイベント)の作成中に何か致命的な事態が発生したときに、例外が報告されず、コードがプロシージャを終了し、フォームのみを表示するということです。

あなたSub Newや他のコードForm LoadはNREの後には実行されないので、他の多くのものは未初期化のままにすることができます。

Sub Form_Load(..._
   '...
   Dim name As String = NameBoxes(2).Text        ' NRE
   ' ...
   ' More code (which will likely not be executed)
   ' ...
End Sub

なお、これは、彼らがどこにあるか、これらは違法作る任意およびすべてのコントロールとコンポーネントの参照に適用されます。

Public Class Form1

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

部分的な救済

VBが警告を出さないのは興味深いですが、フォームレベルでコンテナを宣言し、コントロール存在するときにフォームロードイベントハンドラで初期化するという方法があります。これはSub New、あなたのコードがInitializeComponent呼び出しの後である限り、実行できます:

' Module level declaration
Private NameBoxes as TextBox()
Private studentName As String

' Form Load, Form Shown or Sub New:
'
' Using the OP's approach (illegal using OPTION STRICT)
NameBoxes = New TextBox() {Me.Controls("TextBox1"), Me.Controls("TestBox2"), ...)
studentName = TextBox32.Text           ' For simple control references

配列コードはまだ森の外に出ていないかもしれません。コンテナコントロール内にあるコントロール(a GroupBoxやのようなPanel)は見つかりませんMe.Controls。それらは、そのPanelまたはGroupBoxのControlsコレクションにあります。コントロール名のスペルが間違っていると、コントロールは返されません("TeStBox2")。そのような場合、Nothingそれらの配列要素に再度格納され、参照しようとするとNREが発生します。

あなたが探しているものが分かったので、これは簡単に見つけられるはずです:

「Button2」は、 Panel

対処法

フォームのControlsコレクションを使用した名前による間接参照ではなく、コントロール参照を使用します。

' Declaration
Private NameBoxes As TextBox()

' Initialization -  simple and easy to read, hard to botch:
NameBoxes = New TextBox() {TextBox1, TextBox2, ...)

' Initialize a List
NamesList = New List(Of TextBox)({TextBox1, TextBox2, TextBox3...})
' or
NamesList = New List(Of TextBox)
NamesList.AddRange({TextBox1, TextBox2, TextBox3...})

戻り値なし

Private bars As New List(Of Bars)        ' Declared and created

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

これは、IDEは「ことを警告しますケースであるすべてのパスが値を返さないとNullReferenceExceptionなることがあり」。あなたは交換することにより、警告を抑制することができExit FunctionReturn Nothing、それは問題を解決していません。someCondition = FalseNREが発生したときにリターンを使用しようとするもの:

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

対処法

Exit Function関数をで置換するReturn bList空を 返すことListは返すことと同じではありませんNothing。返されたオブジェクトを返す可能性がある場合は、Nothing使用する前にテストします。

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

実装が悪いTry / Catch

ひどく実装されたTry / Catchは、問題がどこにあるかを隠すことができ、新しい問題が発生します。

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)

    'More code fooing and barring

    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

これは、期待どおりに作成されていないオブジェクトの場合ですが、空のカウンタの有用性も示していますCatch

SQLに「mailaddress」の後に余分なカンマがあると、例外が発生し.ExecuteReaderます。Catch何もしなかった後、Finallyクリーンアップを実行しようとするが、あなたはCloseヌルDataReaderオブジェクトではないので、まったく新しいNullReferenceException結果である。

空のCatchブロックは悪魔の遊び場です。このOPは、なぜ彼がFinallyブロック内でNREを取得していたのか、困惑していました。他の状況では、空Catchになっていると他の何かが後ろに這い回ってしまい、問題の間違った場所で間違ったものを見て時間を費やすことになります。(上記の「サイレント例外」は、同じエンターテイメント価値を提供します)。

対処法

空のTry / Catchブロックを使用しないでください。コードをクラッシュさせ、a)原因を特定します。b)場所を特定し、c)適切な救済方法を適用します。Try / Catchブロックは、開発者を修正するための一意のユーザーからの例外を隠すためのものではありません。

DBNullはNothingと同じではありません

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

IsDBNull関数は、かどうかをテストするために使用されている値が等しいSystem.DBNullMSDNから:

System.DBNull値は、オブジェクトが存在しないか存在しないデータを表すことを示します。DBNullは変数がまだ初期化されていないことを示すNothingと同じではありません。

対処法

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

前と同じように、Nothingをテストしてから特定の値をテストすることができます。

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

例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最初の項目またはデフォルト値を返します。これはNothing参照型のもので、決して決してありませんDBNull

If getFoo IsNot Nothing Then...

コントロール

Dim chk As CheckBox

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

場合CheckBoxchkName見つからない(または内に存在するGroupBox)、次にchk何もなり、例外が発生する任意のプロパティを参照しようとします。

対処法

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

DataGridView

DGVには定期的に見られるいくつかの欠点があります:

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"

場合dgvBooksがあるAutoGenerateColumns = True、それは名前によってそれらを参照するときに、上記のコードが失敗し、それが列を作成しますが、それは彼らを指していません。

対処法

手動で列に名前を付けるか、索引で参照してください。

dgvBooks.Columns(0).Visible = True

例2 - 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

あなたがする場合DataGridViewがあるAllowUserToAddRowsTrue(デフォルト)、Cells下部の空白/新しい行にはすべて含まれていますNothing。コンテンツ(たとえば、ToString)を使用しようとするほとんどの試みは、NREをもたらすでしょう。

対処法

For/Eachループを使用し、IsNewRowプロパティが最後の行かどうかをテストします。これAllowUserToAddRowsは本当かどうかにかかわらず動作します:

For Each r As DataGridViewRow in myDGV.Rows
    If r.IsNewRow = False Then
         ' ok to use this row

あなたが使用する場合はFor nループを、行数を変更したり、使用Exit For時にIsNewRow真です。

My.Settings(StringCollection)

特定の状況下で、アイテムMy.Settingsがaであるアイテムを使用しようとするとStringCollection、最初にNullReferenceが使用される可能性があります。解決策は同じですが、明らかではありません。検討してください:

My.Settings.FooBars.Add("ziggy")         ' foobars is a string collection

VBはあなたのために設定を管理しているので、コレクションの初期化を期待するのは妥当です。これは以前のエントリを(設定エディタの)コレクションに追加した場合に限ります。アイテムが追加されるとコレクションは(明らかに)初期化されるためNothing、設定エディタに追加するアイテムがない場合はそのまま残ります。

対処法

Load必要に応じて、フォームのイベントハンドラで設定コレクションを初期化します。

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

通常、Settingsコレクションはアプリケーションの初回実行時にのみ初期化する必要があります。別の方法として、[プロジェクト] - [設定] - [設定] - [ FooBars、プロジェクトを保存し、偽の値を削除します。

キーポイント

あなたはおそらくNewオペレータを忘れてしまったでしょう

または

初期化されたオブジェクトをコードに返すために完璧に実行すると仮定したものはそうしませんでした。

コンパイラの警告(これまで)を無視してOption Strict On(常に)使用しないでください。

MSDN NullReference例外

いくつかのコードがあり、実行すると、 NullReferenceExceptionスローされ、次のようになります。

オブジェクト参照がオブジェクトインスタンスに設定されていません。

これはどういう意味ですか、このエラーを修正するために何ができますか?


原因は何ですか?

ボトムライン

null (またはVB.NETのNothing )を使用しようとしています。 これは、 nullに設定するか、何も設定しないことを意味します。

他のものと同様に、 nullは渡されます。 メソッド "A"でnull場合、メソッド "B"がメソッド "A"にnull 渡した可能性がありnull

nullは異なる意味を持つことができます:

  1. 初期化さ れておらず 、したがって何も指して いないオブジェクト変数 この場合、そのようなオブジェクトのプロパティまたはメソッドにアクセスすると、 NullReferenceException発生します。
  2. 開発者は、意味のある価値がないことを意図的に示すためにnullを使用していnull C#には変数のnullableデータ型の概念があることに注意してください(データベース表にはNULL可能なフィールドを持つことができます)。nullを代入すると、値は格納されませんint? a = null; int? a = null; 疑問符は、変数a nullを格納することが許可されていることを示します。 if (a.HasValue) {...}またはif (a==null) {...}を使用してチェックできif (a==null) {...} 。 この例のようにa a.Valueな変数は、 a.Valueを介して明示的に値にアクセスするかaを介して通常通りに値にアクセスすることを許可します。
    a.Valueを介してアクセスすると、 anull場合はNullReferenceException代わりにInvalidOperationExceptionスローされます。事前にチェックする必要がありint b; if (a.HasValue) { b = a.Value; }ような代入を行う必要がありますif (a.HasValue) { b = a.Value; } if (a.HasValue) { b = a.Value; }またはそれより短いif (a != null) { b = a; } if (a != null) { b = a; }

この記事の残りの部分では、 NullReferenceExceptionつながる可能性のある多くのプログラマーがしばしば行う間違いを詳細に説明します。

すなわち

NullReferenceExceptionスローするランタイムは、 常に同じことを意味します。参照を使用しようとしていて、参照が初期化されていない(または初期化されたが初期化されていない )。

これは、参照がnullであり、 null参照によってメンバー(メソッドなど)にアクセスできないことを意味します。 最も単純なケース:

string foo = null;
foo.ToUpper();

null指すstring参照でインスタンスメソッドToUpper()を呼び出すことができないため、2行目にNullReferenceExceptionがスローされnull

デバッグ

どのようにNullReferenceExceptionのソースを見つけるのですか? Visual Studioでのデバッグの一般的なルールが適用されます:戦略的なブレークポイントを配置し、変数検査します。マウスを名前の上に置くか、(クイック)ウォッチウィンドウ、または地方自治体や自動車などのさまざまなデバッグパネルを使用します。

参照が設定されているかどうかを調べるには、その名前を右クリックして[すべての参照を検索]を選択します。 見つかった場所にブレークポイントを置き、デバッガを接続してプログラムを実行することができます。 デバッガがそのようなブレークポイントでブレークするたびに、参照がnullでないと予想するかどうかを判断し、変数を検査し、期待したときにインスタンスを指すことを確認する必要があります。

このようにしてプログラムフローを実行すると、インスタンスをnullにしてはならない場所と、それが正しく設定されていない理由を見つけることができます。

例外がスローされる一般的なシナリオ:

ジェネリック

ref1.ref2.ref3.member

ref1またはref2またはref3がnullの場合、 NullReferenceExceptionます。 問題を解決したい場合は、式を簡単な同等物に書き換えることで、どちらがヌルであるかを調べることができます。

var r1 = ref1;
var r2 = r1.ref2;
var r3 = r2.ref3;
r3.member

具体的には、 HttpContext.Current.User.Identity.Nameでは、 HttpContext.Currentがnullになるか、 Userプロパティがnullになるか、 Identityプロパティがnullになる可能性があります。

間接

public class Person {
    public int Age { get; set; }
}
public class Book {
    public Person Author { get; set; }
}
public class Example {
    public void Foo() {
        Book b1 = new Book();
        int authorAge = b1.Author.Age; // You never initialized the Author property.
                                       // there is no Person to get an Age from.
    }
}

子(Person)null参照を避けたい場合は、親(Book)オブジェクトのコンストラクタで初期化することができます。

ネストされたオブジェクトの初期化子

ネストされたオブジェクトイニシャライザにも同じことが適用されます。

Book b1 = new Book { Author = { Age = 45 } };

これは

Book b1 = new Book();
b1.Author.Age = 45;

newキーワードが使用されている間は、 Book新しいインスタンスだけを作成しますが、 Person新しいインスタンスは作成しません。したがって、プロパティのAuthorはまだnullです。

ネストされたコレクションの初期化子

public class Person {
    public ICollection<Book> Books { get; set; }
}
public class Book {
    public string Title { get; set; }
}

ネストされたコレクション初期化子は同じように動作します。

Person p1 = new Person {
    Books = {
        new Book { Title = "Title1" },
        new Book { Title = "Title2" },
    }
};

これは

Person p1 = new Person();
p1.Books.Add(new Book { Title = "Title1" });
p1.Books.Add(new Book { Title = "Title2" });

new PersonPersonインスタンスのみを作成しますが、 Booksコレクションはまだnullです。 コレクションイニシャライザの構文では、 p1.Booksコレクションは作成されず、 p1.Books.Add(...)文にのみ変換されます。

アレイ

int[] numbers = null;
int n = numbers[0]; // numbers is null. There is no array to index.

配列要素

Person[] people = new Person[5];
people[0].Age = 20 // people[0] is null. The array was allocated but not
                   // initialized. There is no Person to set the Age for.

ギザギザの配列

long[][] array = new long[1][];
array[0][0] = 3; // is null because only the first dimension is yet initialized.
                 // Use array[0] = new long[2]; first.

コレクション/リスト/ディクショナリー

Dictionary<string, int> agesForNames = null;
int age = agesForNames["Bob"]; // agesForNames is null.
                               // There is no Dictionary to perform the lookup.

範囲変数(間接/遅延)

public class Person {
    public string Name { get; set; }
}
var people = new List<Person>();
people.Add(null);
var names = from p in people select p.Name;
string firstName = names.First(); // Exception is thrown here, but actually occurs
                                  // on the line above.  "p" is null because the
                                  // first element we added to the list is null.

イベント

public class Demo
{
    public event EventHandler StateChanged;

    protected virtual void OnStateChanged(EventArgs e)
    {        
        StateChanged(this, e); // Exception is thrown here 
                               // if no event handlers have been attached
                               // to StateChanged event
    }
}

不正な命名規則:

地方と違う名前のフィールドを作成した場合は、フィールドを初期化していないことに気づいたかもしれません。

public class Form1 {
    private Customer customer;

    private void Form1_Load(object sender, EventArgs e) {
        Customer customer = new Customer();
        customer.Name = "John";
    }

    private void Button_Click(object sender, EventArgs e) {
        MessageBox.Show(customer.Name);
    }
}

これは、フィールドの先頭にアンダースコアを付けるという規則に従って、解決できます。

private Customer _customer;

ASP.NETページライフサイクル:

public partial class Issues_Edit : System.Web.UI.Page
{
    protected TestIssue myIssue;

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            // Only called on first load, not when button clicked
            myIssue = new TestIssue(); 
        }
    }

    protected void SaveButton_Click(object sender, EventArgs e)
    {
        myIssue.Entry = "NullReferenceException here!";
    }
}

ASP.NETセッション値

// if the "FirstName" session value has not yet been set,
// then this line will throw a NullReferenceException
string firstName = Session["FirstName"].ToString();

ASP.NET MVCの空のビューモデル

ASP.NET MVCビューで@Modelプロパティを参照するときに例外が発生した場合は、ビューをreturnときに、アクションメソッドでModelが設定されていることを理解する必要があります。 コントローラから空のモデル(またはモデルのプロパティ)を返すと、そのビューがビューにアクセスすると例外が発生します。

// Controller
public class Restaurant:Controller
{
    public ActionResult Search()
    {
         return View();  // Forgot the provide a Model here.
    }
}

// Razor view 
@foreach (var restaurantSearch in Model.RestaurantSearch)  // Throws.
{
}

<p>@Model.somePropertyName</p> <!-- Also throws -->

WPFコントロールの作成順序とイベント

WPFコントロールは、ビジュアルツリーに表示される順序でInitializeComponentの呼び出し中に作成されます。 NullReferenceExceptionは、初期作成されたイベントハンドラなどのコントロールの場合に生成されます。このイベントハンドラは、後で作成されたコントロールを参照するInitializeComponent中に発生します。

例えば ​​:

<Grid>
    <!-- Combobox declared first -->
    <ComboBox Name="comboBox1" 
              Margin="10"
              SelectedIndex="0" 
              SelectionChanged="comboBox1_SelectionChanged">
        <ComboBoxItem Content="Item 1" />
        <ComboBoxItem Content="Item 2" />
        <ComboBoxItem Content="Item 3" />
    </ComboBox>

    <!-- Label declared later -->
    <Label Name="label1" 
           Content="Label"
           Margin="10" />
</Grid>

comboBox1label1前に作成されます。 comboBox1_SelectionChangedが `label1を参照しようとすると、まだ作成されていません。

private void comboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    label1.Content = comboBox1.SelectedIndex.ToString(); // NullReference here!!
}

XAMLの宣言の順序を変更する(つまり、 label1前にlabel1リストし、デザイン哲学の問題を無視して、少なくともここではNullReferenceException解決します)。

と一緒にキャスト

var myThing = someObject as Thing;

InvalidCastExceptionはスローされませんが、キャストが失敗した場合(someObject自体がnullの場合)はnull返します。 だからそれに注意してください。

LINQ FirstOrDefault()およびSingleOrDefault()

プレーン版First()Single()は、何もないときに例外をスローします。 この場合、 "OrDefault"バージョンはnullを返します。 だからそれに注意してください。

foreach

nullコレクションを反復しようとするとforeachスローされます。 通常は、コレクションを返すメソッドの予期しないnull結果が原因です。

 List<int> list = null;    
 foreach(var v in list) { } // exception

より現実的な例 - XML文書からノードを選択する。 ノードが見つかりませんが、最初のデバッグではすべてのプロパティが有効であることが示されます。

 foreach (var node in myData.MyXml.DocumentNode.SelectNodes("//Data"))

回避する方法

明示的にnullをチェックし、 null値を無視します。

参照がnullになることがあると予想される場合は、インスタンスメンバーにアクセスする前にnullであることを確認できます。

void PrintName(Person p) {
    if (p != null) {
        Console.WriteLine(p.Name);
    }
}

明示的にnullをチェックし、デフォルト値を指定します。

メソッドはnullを返すことができるインスタンスを返すと呼びます。たとえば、探しているオブジェクトが見つからない場合などです。 この場合、デフォルト値を返すように選択できます。

string GetCategory(Book b) {
    if (b == null)
        return "Unknown";
    return b.Category;
}

メソッド呼び出しからnullを明示的にチェックし、カスタム例外をスローします。

カスタム例外をスローすることもできます。カスタム例外は、呼び出しコードでキャッチするだけです。

string GetCategory(string bookTitle) {
    var book = library.FindBook(bookTitle);  // This may return null
    if (book == null)
        throw new BookNotFoundException(bookTitle);  // Your custom exception
    return book.Category;
}

例外が発生するよりも早く問題をキャッチするために、値がnullでない場合はDebug.Assert使用します。

開発中に、メソッドがnullを返すことはできないかもしれないことがわかっている場合、 Debug.Assert()を使用して、 Debug.Assert()します。

string GetTitle(int knownBookID) {
    // You know this should never return null.
    var book = library.GetBook(knownBookID);  

    // Exception will occur on the next line instead of at the end of this method.
    Debug.Assert(book != null, "Library didn't return a book for known book ID.");

    // Some other code

    return book.Title; // Will never throw NullReferenceException in Debug mode.
}

このチェックはあなたのリリースビルドは終わりませんが 、releaseモードで実行時にbook == null場合、 NullReferenceException再びスローするようになります。

null値型の場合は、 GetValueOrDefault()を使用して、 null場合にデフォルト値を指定しnull

DateTime? appointment = null;
Console.WriteLine(appointment.GetValueOrDefault(DateTime.Now));
// Will display the default value provided (DateTime.Now), because appointment is null.

appointment = new DateTime(2022, 10, 20);
Console.WriteLine(appointment.GetValueOrDefault(DateTime.Now));
// Will display the appointment date, not the default

ヌル結合演算子を使用します。 ?? [C#]またはIf() [VB]。

nullに遭遇したときにデフォルト値を提供することの略語:

IService CreateService(ILogger log, Int32? frobPowerLevel)
{
    var serviceImpl = new MyService(log ?? NullLog.Instance);

    // Note that the above "GetValueOrDefault()" can also be rewritten to use
    // the coalesce operator:
    serviceImpl.FrobPowerLevel = frobPowerLevel ?? 5;
}

null条件演算子: ?.使用します?. または?[x]配列(C#6とVB.NET 14で利用可能):

これはセーフナビゲーションまたはエルヴィス(形状の後に)演算子と呼ばれることもあります。 演算子の左側の式がnullの場合、右側は評価されず、代わりにnullが返されます。 これは、次のようなケースを意味します。

var title = person.Title.ToUpper();

その人にタイトルがない場合、null値のプロパティでToUpperを呼び出そうとしているので例外がスローされます。

C#5以降では、これは次の方法で保護されます。

var title = person.Title == null ? null : person.Title.ToUpper();

タイトル変数は例外をスローする代わりにnullになります。 C#6ではこれより短い構文が導入されています。

var title = person.Title?.ToUpper();

これによりtitle変数がnullになり、 person.Titlenull場合にperson.TitleへのToUpperは行われません。

もちろん、ヌルのtitleをチェックするか、null条件演算子をヌル併合演算子( ?? )とともに使用してデフォルト値を指定する必要があります。

// regular null check
int titleLength = 0;
if (title != null)
    titleLength = title.Length; // If title is null, this would throw NullReferenceException

// combining the `?` and the `??` operator
int titleLength = title?.Length ?? 0;

同様に、配列の場合は?[i]を次のように使用できます?[i]

int[] myIntArray=null;
var i=5;
int? elem = myIntArray?[i];
if (!elem.HasValue) Console.WriteLine("No value");

これは次のことを行います:myIntArrayがnullの場合、式はnullを返し、安全にチェックできます。 配列が含まれている場合は、次のようになりますelem = myIntArray[i]; i 番目の要素を返します。

イテレータでのヌルデレフのデバッグと修正のための特別なテクニック

C#は "イテレータブロック"(他の一般的な言語では "ジェネレータ"と呼ばれています)をサポートしています。 遅延実行のためにイテレータブロックでデバッグするには、Null逆参照例外が特にトリッキーになります。

public IEnumerable<Frob> GetFrobs(FrobFactory f, int count)
{
    for (int i = 0; i < count; ++i)
      yield return f.MakeFrob();
}
...
FrobFactory factory = whatever;
IEnumerable<Frobs> frobs = GetFrobs();
...
foreach(Frob frob in frobs) { ... }

結果がnull場合、 MakeFrobはスローします。 今、正しいことがこれだと思うかもしれません:

// DON'T DO THIS
public IEnumerable<Frob> GetFrobs(FrobFactory f, int count)
{
    if (f == null) 
      throw new ArgumentNullException("f", "factory must not be null");
    for (int i = 0; i < count; ++i)
      yield return f.MakeFrob();
}

なぜこれは間違っていますか? イテレータブロックはforeachまで実際には実行されないので! GetFrobs呼び出すと、反復処理時に反復子ブロックが実行れるオブジェクトが返されます。

このようにヌルチェックを書くことで、ヌル参照解除を防ぎますが、ヌル引数の例外を呼び出しのポイントではなく、 反復のポイントに移動するデバッグ非常に混乱します。

正しい解決策は次のとおりです。

// DO THIS
public IEnumerable<Frob> GetFrobs(FrobFactory f, int count)
{
    // No yields in a public method that throws!
    if (f == null) 
      throw new ArgumentNullException("f", "factory must not be null");
    return GetFrobsForReal(f, count);
}
private IEnumerable<Frob> GetFrobsForReal(FrobFactory f, int count)
{
    // Yields in a private method
    Debug.Assert(f != null);
    for (int i = 0; i < count; ++i)
      yield return f.MakeFrob();
}

つまり、イテレータブロックロジックを持つプライベートヘルパメソッドと、nullチェックを行い、イテレータを返すpublicサーフェスメソッドを作成します。 GetFrobsが呼び出されると、すぐにヌルチェックが行われ、シーケンスが反復されるとGetFrobsForReal実行されます。

LINQ to Objectsの参照元を調べると、このテクニックが一貫して使用されていることがわかります。 書き込むのはちょっと面倒ですが、それは無効エラーのデバッグをはるかに簡単にします。 著者の利便性ではなく、発信者の便宜のためにコードを最適化します

安全でないコードのヌル逆参照に関する注釈

C#は、名前が示すように、メモリの安全性と型の安全性を提供する通常の安全メカニズムが強制されないため、非常に危険です。 メモリの仕組みを徹底的に理解していない限り、安全でないコードを書くべきではありません

安全でないモードでは、2つの重要な事実に気づくべきです:

  • NULL ポインタの参照を解除すると、null 参照の逆参照と同じ例外が生成されます
  • 無効なNULLでないポインタの参照を解除すると、状況によってはその例外生成されることがあります

その理由を理解するために、まず.NETがどのようにnull参照解除例外を生成するかを理解することが役立ちます。 (これらの詳細はWindows上で実行される.NETに適用され、他のオペレーティングシステムでも同様のメカニズムが使用されます)。

メモリはWindowsで仮想化されています。 各プロセスは、オペレーティングシステムによって追跡される多くの「ページ」のメモリの仮想メモリ空​​間を取得します。 メモリの各ページには、どのように使用されるかを決定するフラグが設定されています。読み込み、書き込み、実行されます。 一番下のページには「これまでどおりにエラーが発生した場合」とマークされます。

C#のヌルポインタとヌル参照の両方が内部的に数字の0で表されているため、対応するメモリストレージに逆参照しようとすると、オペレーティングシステムによってエラーが発生します。 .NETランタイムはこのエラーを検出し、それをnull参照解除例外に変換します。

そのため、ヌルポインタとヌル参照の両方を参照解除すると同じ例外が発生します。

第二のポイントはどうですか? 仮想メモリの最下位ページにある無効なポインタを参照解除すると、同じオペレーティングシステムエラーが発生し、同じ例外が発生します。

なぜこれは理にかなっていますか? さて、2つのintを含む構造体とnullに等しいアンマネージドポインタがあるとします。 構造体の2番目のintを逆参照しようとすると、CLRは場所0のストレージにアクセスしようとしません。 場所4のストレージにアクセスします。 しかし、論理的には、null を介してそのアドレスに到達しているため、これはヌルデ参照です。

あなたが安全でないコードを扱っていて、nullの参照解除例外が発生した場合は、問題のポインタがnullである必要はないことに注意してください。 最下位ページの任意の場所にすることができ、この例外が生成されます。


あなたはそれについて何ができますか?

ヌルリファレンスが何であるか、そしてそれをどのようにデバッグするのかを説明する多くの良い答えがここにあります。しかし、この問題を防ぐ方法や、少なくともキャッチするのを簡単にする方法はほとんどありません。

引数をチェックする

たとえば、メソッドは、異なる引数をチェックして、それらがnullであるかどうかを調べることができますArgumentNullException。例外は、この正確な目的のために明示的に作成されます。

ArgumentNullException偶数用のコンストラクターはパラメーターの名前とメッセージを引数として取り、開発者に問題の内容を正確に伝えることができます。

public void DoSomething(MyObject obj) {
    if(obj == null) 
    {
        throw new ArgumentNullException("obj", "Need a reference to obj.");
    }
}

ツールを使用する

助けることができるライブラリもいくつかあります。たとえばResharperでは、コードを記述しているときに警告を表示できます。特に、その属性を使用する場合は、NotNullAttribute

Contract.Requires(obj != null)ランタイムとコンパイルチェックを提供するような構文を使用する「Microsoft Code Contracts」があります。コード契約の紹介

また、次のような属性を使うことができる "PostSharp"もあります:

public void DoSometing([NotNull] obj)

これを実行し、ビルドプロセスのPostSharp objに実行時にnullがないかどうかをチェックします。参照:ポストシャープヌルチェック

プレーンコードソリューション

あるいは、あなたはいつも古いコードを使って独自のアプローチをコード化することができます。たとえば、null参照をキャッチするために使用できる構造体がここにあります。それは同じコンセプトの後にモデル化されていNullable<T>ます:

[System.Diagnostics.DebuggerNonUserCode]
public struct NotNull<T> where T: class
{
    private T _value;

    public T Value
    {
        get
        {
            if (_value == null)
            {
                throw new Exception("null value not allowed");
            }

            return _value;
        }
        set
        {
            if (value == null)
            {
                throw new Exception("null value not allowed.");
            }

            _value = value;
        }
    }

    public static implicit operator T(NotNull<T> notNullValue)
    {
        return notNullValue.Value;
    }

    public static implicit operator NotNull<T>(T value)
    {
        return new NotNull<T> { Value = value };
    }
}

Nullable<T>ちょうど反対を達成するという目標を除いて、あなたが使用するのと同じように使用するでしょうnull。ここではいくつかの例を示します。

NotNull<Person> person = null; // throws exception
NotNull<Person> person = new Person(); // OK
NotNull<Person> person = GetPerson(); // throws exception if GetPerson() returns null

NotNull<T>暗黙的にキャストされているTので、必要な場所であればどこでも使用できます。たとえば、次のPersonようなメソッドにオブジェクトを渡すことができますNotNull<Person>

Person person = new Person { Name = "John" };
WriteName(person);

public static void WriteName(NotNull<Person> person)
{
    Console.WriteLine(person.Value.Name);
}

上記のnullableの場合と同様に、Valueプロパティを介して基本値にアクセスします。あるいは、明示的または暗黙的なキャストを使用することができます。以下の戻り値の例を参照してください。

Person person = GetPerson();

public static NotNull<Person> GetPerson()
{
    return new Person { Name = "John" };
}

または、メソッドがキャストを行うだけでT(この場合Person)戻ってきたときにも使用できます。例えば、以下のコードは上記のコードを好きにします:

Person person = (NotNull<Person>)GetPerson();

public static Person GetPerson()
{
    return new Person { Name = "John" };
}

エクステンションと結合する

NotNull<T>拡張メソッドと組み合わせると、より多くの状況をカバーできます。拡張メソッドがどのように見えるかの例を次に示します。

[System.Diagnostics.DebuggerNonUserCode]
public static class NotNullExtension
{
    public static T NotNull<T>(this T @this) where T: class
    {
        if (@this == null)
        {
            throw new Exception("null value not allowed");
        }

        return @this;
    }
}

次に、その使用方法の例を示します。

var person = GetPerson().NotNull();

GitHub

参考までに、上記のコードをGitHubで入手できました。

https://github.com/luisperezphd/NotNull

関連言語機能

C#6.0では、これを少し助ける「ヌル条件付き演算子」が導入されました。この機能を使用すると、ネストされたオブジェクトを参照できnullますnull

これにより、いくつかのケースで行う必要があるヌルチェックの回数が減ります。構文は、各ドットの前に疑問符を付けることです。たとえば、次のコードを実行します。

var address = country?.State?.County?.City;

それcountryが、CountryというStateようなプロパティを持つ型のオブジェクトであると想像してください。もしcountryStateCounty、またはCityであるnull、その後address will beはnull . Therefore you only have to check whetherアドレスisnull`なので。

それは素晴らしい機能ですが、それはより少ない情報を提供します。4のどれがヌルであるかは明らかではありません。

Nullableのような組み込みですか?

C#は素早く簡略化されていNullable<T>ます。そのような型の後に疑問符を置くことで、何かをnullableにすることができますint?

C#にNotNull<T>上記のようなものがあって、同様の省略形、恐らく感嘆符(!)を付けて、次のようなものを書くことができればうれしいです:public void WriteName(Person! person)


NullReferenceExceptionまたはオブジェクト参照がオブジェクトのインスタンスに設定されていない場合は、使用しようとしているクラスのオブジェクトがインスタンス化されていない場合に発生します。 例えば:

学生という名前のクラスがあるとします。

public class Student
{
    private string FirstName;
    private string LastName;
    public string GetFullName()
    {
        return FirstName + LastName;
    }
}

今、あなたが学生のフルネームを取得しようとしている別のクラスを考えてみましょう。

public class StudentInfo
{      
    public string GetStudentName()
    {
        Student s;
        string fullname = s.GetFullName();
        return fullname;
    }        
}

上記のコードに見られるように、ステートメントStudentは、タイプStudentの変数のみを宣言しますが、この時点ではStudentクラスはインスタンス化されていません。したがって、ステートメントs.GetFullName()が実行されると、NullReferenceExceptionがスローされます。


あなたは、c#6のヌル条件付き演算子を使って、クリーンな方法でNullReferenceExceptionを修正し、ヌルチェックを処理するコードを少なく書くことができます。

これは、メンバーアクセス(?)またはインデックス(?)操作を実行する前にnullをテストするために使用されます。

  var name = p?.Spouse?.FirstName;

次のものと同等です。

    if (p != null)
    {
        if (p.Spouse != null)
        {
            name = p.Spouse.FirstName;
        }
    }

その結果、pがnullのとき、またはp.Spouseがnullのときに名前がnullになります。

それ以外の場合は、変数名にp.Spouse.FirstNameの値が割り当てられます。

詳細:無条件演算子


この例外がスローされる可能性のある一般的なシナリオを検討した場合、上部にオブジェクトを持つプロパティにアクセスします。

例:

string postalcode=Customer.Address.PostalCode; 
//if customer or address is null , this will through exeption

ここで、addressがnullの場合、NullReferenceExceptionが返されます。

したがって、実際には、そのようなオブジェクトのプロパティにアクセスする前に常にnullチェックを使用する必要があります(特に汎用)

string postalcode=Customer?.Address?.PostalCode;
//if customer or address is null , this will return null, without through a exception

これは、nullに設定された(つまり、実際のオブジェクトインスタンスを参照していない)オブジェクト参照変数がコードで使用されたことを意味します。

エラーを回避するには、nullになる可能性のあるオブジェクトを使用する前にヌルをテストする必要があります。

if (myvar != null)
{
    // Go ahead and use myvar
    myvar.property = ...
}
else
{
    // Whoops! myvar is null and cannot be used without first
    // assigning it to an instance reference
    // Attempting to use myvar here will result in NullReferenceException
}

まあ、簡単な言葉で言えば:

作成されていないオブジェクトまたは現在メモリに存在しないオブジェクトにアクセスしようとしています。

だからこれに取り組む方法:

  1. デバッグして、デバッガが壊れるようにする...壊れている変数に直接移動します...これで問題は解決します。適切な場所に新しいキーワードを使用します。

  2. オブジェクトが存在しないために一部のデータベースコマンドで発生する場合は、nullチェックを行い、それを処理するだけです。

    if (i == null) {
        // Handle this
    }
    
  3. GCがオブジェクトを既に収集していた場合は、最も難しいものです...これは一般に、文字列を使用してオブジェクトを検索しようとしている場合に発生します。つまり、オブジェクトの名前でそのオブジェクトを検索すると、それをきれいにしました...これは見つけるのが難しく、かなりの問題になるでしょう...これに取り組むより良い方法は、開発プロセス中に必要な場合はいつでもヌルチェックです。これはあなたに多くの時間を節約します。

名前で見つけることによって、私はいくつかのフレームワークが文字列を使用してFIndObjectsを使用できるようになり、コードは次のようになります。FindObject( "ObjectName");


エンティティフレームワークで使用されるエンティティのクラス名がWebフォームのコードビハインドファイルのクラス名と同じである場合の追加。

CodebehindクラスがContactでWebフォームのContact.aspxがあり、エンティティ名Contactがあるとします。

context.SaveChanges()を呼び出すと、次のコードはNullReferenceExceptionをスローします。

Contact contact = new Contact { Name = "Abhinav"};
var context = new DataContext();
context.Contacts.Add(contact);
context.SaveChanges(); // NullReferenceException at this line

完全性のために、DataContextクラス

public class DataContext : DbContext 
{
    public DbSet<Contact> Contacts {get; set;}
}

Contactエンティティクラス。エンティティクラスは部分クラスなので、他のファイルでもエンティティクラスを拡張できます。

public partial class Contact 
{
    public string Name {get; set;}
}

エンティティとコードビハインドクラスが同じ名前空間にある場合にエラーが発生します。これを修正するには、Contact.aspxのエンティティクラスまたはコードビハインドクラスの名前を変更します。

理由はまだ分かりません。しかし、エンティティクラスのいずれかがSystem.Web.UI.Pageを拡張するたびに、このエラーが発生します。

議論のために、DbContext.saveChanges()のNullReferenceExceptionを見てください。


シナリオに関係なく、原因は.NETで常に同じであることに注意してください。

値がNothing/ である参照変数を使用しようとしていますnull。値がリファレンス変数のNothing/ nullである場合、ヒープ上に存在するオブジェクトのインスタンスへの参照を実際に保持していないことを意味します。

変数に何かを割り当てたり、変数に割り当てられた値のインスタンスを作成したり、変数をNothing/ null手動に等しく設定したり、変数をNothing/に設定した関数を呼び出すことnullはできません。


興味深いことに、このページの回答のいずれも2つの重要なケースについて言及していません。

エッジケース#1:ディクショナリへの同時アクセス

.NETの汎用辞書はスレッドセーフではなく、2つの並行スレッドからキーにアクセスしようとすると、時には a NullReferenceまたはさらに(頻繁に)aをスローすることがありKeyNotFoundExceptionます。この場合、例外はかなり誤解を招きます。

エッジケース#2:安全でないコード

a NullReferenceExceptionunsafeコードによってスローされた場合は、ポインタ変数を調べて、IntPtr.Zero何かをチェックするかもしれません。同じもの( "nullポインタ例外")ですが、安全でないコードでは、変数は値型/配列などにキャストされることが多く、壁に頭を当てて、値型が例外。

(あなたがそれを必要としない限り、非安全なコードを使用しない別の理由)


「私はそれについて何をすべきか」という問題に関して、多くの答えがあります。

開発中にこのようなエラー状態防止するためのより「正式な」方法は、コードで契約によって設計を適用することです。つまり、開発中にクラス不変式を設定したり、システム上の関数/メソッドの前提条件事後条件を設定する必要があります。

要するに、クラス不変条件は、通常の使用で違反しないようなクラスにいくつかの制約が存在することを保証します(したがって、クラスが矛盾した状態になることはありません)。前提条件とは、関数/メソッドへの入力として与えられたデータは、いくつかの制約の設定に従わなければならず、決してそれらに違反しないことを意味し、事後条件とは、関数/ バグのないプログラムの実行中に契約条件に違反すること決してありませ。したがって、契約による設計は実際にはデバッグモードでチェックされ、リリースで無効になります 開発されたシステム性能を最大限に引き出す。

このようにしてNullReferenceException、制約セットの違反の結果であるケースを回避できます。たとえばX、あるクラスでオブジェクトプロパティを使用し、後でそのメソッドの1つを呼び出しようとしXたときにnull値を持つ場合、これは次のようになりますNullReferenceException

public X { get; set; }

public void InvokeX()
{
    X.DoSomething(); // if X value is null, you will get a NullReferenceException
}

しかし、メソッドの前提条件として「プロパティXは決してnull値を持つことはできません」と設定した場合、前述のシナリオを防ぐことができます。

//Using code contracts:
[ContractInvariantMethod]
protected void ObjectInvariant () 
{
    Contract.Invariant ( X != null );
    //...
}

このため、.NETアプリケーション用のCode Contractsプロジェクトが存在します。

あるいは、assertionsを使用して契約による設計を適用することもできます。

アップデート:エッフェルのプログラミング言語のデザインに関連して、この言葉がBertrand Meyerによって作成されたことに言及する価値があります


この例外を受け取るかもしれない別の一般的なケースでは、単体テスト中にクラスをモックすることが含まれます。使用されている模擬フレームワークに関係なく、クラス階層のすべての適切なレベルが適切に偽装されていることを確認する必要があります。特に、HttpContextテスト中のコードによって参照されているすべてのプロパティは嘲笑されなければなりません。

多少冗長な例については、「カスタムAuthorizationAttributeのテスト時にNullReferenceExceptionがスローされる」を参照してください。


これは、問題の変数が何も指されていないことを意味します。私はこれを次のように生成することができます:

SqlConnection connection = null;
connection.Open();

変数を宣言している間はconnection何も指されていないので、エラーがスローされます。メンバーを呼び出そうとすると、Open解決するための参照がなく、エラーがスローされます。

このエラーを回避するには:

  1. 何かをしようとする前に、常にオブジェクトを初期化してください。
  2. オブジェクトがヌルかどうかわからない場合は、それをチェックしてくださいobject == null

JetBrainsのResharperツールは、ヌル参照エラーの可能性があるコード内のすべての場所を識別し、ヌルチェックを行うことができます。このエラーは、バグの原因の1つです.IMHO。


ヌル値参照を含むオブジェクトを使用しています。したがって、null例外が発生しています。この例では、文字列の値はnullであり、長さをチェックするときに例外が発生しました。

例:

string value = null;
if (value.Length == 0) // <-- Causes exception
{
    Console.WriteLine(value); // <-- Never reached
}

例外エラーは次のとおりです。

未処理の例外:

System.NullReferenceException:オブジェクト参照がオブジェクトのインスタンスに設定されていません。 Program.Main()


メソッドとオブジェクトのメンバを使用するには、まずそのオブジェクトを作成する必要があります。作成していない場合(オブジェクトを保持する変数は初期化されていません)、そのメソッドまたは変数を使用しようとすると、そのエラーが発生します。

時には初期化を忘れるかもしれません。

編集済み: newはnullを返すことはできませんが、失敗するとfireの例外が発生します。長い間、それはいくつかの言語では当てはまりましたが、それ以上はありませんでした。それを指摘してくれてありがとう@ジョンサンダース。


別のケースでNullReferenceExceptionsは、asオペレータの(誤った)使用が起こります。

class Book {
    public string Name { get; set; }
}
class Car { }

Car mycar = new Car();
Book mybook = mycar as Book;   // Incompatible conversion --> mybook = null

Console.WriteLine(mybook.Name);   // NullReferenceException

ここで、BookCarは互換性のない型です。a Carはa に変換/キャストできませんBook。このキャストに失敗すると、as返されますnull。このmybook後に使用すると、a NullReferenceException

一般的には、as次のようにcastまたはを使用する必要があります。

型変換が常に成功することを期待している場合(つまり、オブジェクトが前もって何であるべきかを知っている場合)、キャストを使うべきです:

ComicBook cb = (ComicBook)specificBook;

あなたはタイプが不明ですが、あなたがしたい場合はしようと、特定の型としてそれを使用するために、その後、使用as

ComicBook cb = specificBook as ComicBook;
if (cb != null) {
   // ...
}

別のシナリオは、nullオブジェクトを値型にキャストする場合です。たとえば、次のコードは次のとおりです。

object o = null;
DateTime d = (DateTime)o;

それはNullReferenceExceptionキャストに投げつけるでしょう。上記のサンプルではかなり分かりやすいようですが、所有していないコードからnullオブジェクトが返され、いくつかの自動システムによってキャストされた複雑なシナリオで発生する可能性があります。

これの1つの例は、Calendarコントロールを持つこの単純なASP.NETバインディングフラグメントです。

<asp:Calendar runat="server" SelectedDate="<%#Bind("Something")%>" />

ここでSelectedDateは、実際DateTimeにはCalendarWebコントロール型のプロパティの型ですが、バインディングは完全に何かを返すことができます。暗黙のASP.NETジェネレータは、上記のキャストコードと同等のコードを作成します。そして、これNullReferenceExceptionは、ASP.NETを生成したコードがうまくコンパイルされているために見つけることが非常に困難です。


参照型を初期化しておらず、そのプロパティの1つを設定または読み込みたい場合は、NullReferenceExceptionがスローされます。

例:

Person p = null;
p.Name = "Harry"; // NullReferenceException occurs here.

変数がnullでないかどうかをチェックするだけで、これを避けることができます:

Person p = null;
if (p!=null)
{
    p.Name = "Harry"; // Not going to run to this point
}

NullReferenceExceptionがスローされる理由を完全に理解するには、値型参照型の違いを知ることが重要です。

したがって、値の型を扱う場合、NullReferenceExceptions 発生しません参照型を扱うときは注意を払う必要がありますが!

名前が示唆しているように、参照型だけが参照を保持することも、文字通り何も指さないこと(または 'null')を指すこともできます。 値の型には常に値が含まれます。

参照型(これらはチェックする必要があります):

  • 動的
  • オブジェクト
  • 文字列

値の型(これらは無視することができます):

  • 数値型
  • 積分型
  • 浮動小数点型
  • 小数点以下の
  • ブール
  • ユーザー定義の構造体

文字通り、NullReferenceExeptionを修正する最も簡単な方法は2つあります。例えばGameObjectをスクリプトがついていて、変数がrb(rigidbody)の場合、この変数はゲームの開始時にnullを開始します。
このため、コンピュータにはその変数にデータが格納されていないため、NullReferenceExeptionが返されます。

私はRigidBody変数を例として使用します。
データを実際にいくつかの方法で簡単に追加することができます。

  1. AddComponent>物理学>剛体であなたのオブジェクトに剛体を追加します
    次に、あなたのスクリプトに移動して、入力しrb = GetComponent<Rigidbody>();
    たコードのこの行は、あなたの下で最適に動作Start()またはAwake()機能。
  2. プログラムでコンポーネントを追加し、同時に1行のコードで変数を割り当てることができます。 rb = AddComponent<RigidBody>();

さらに注意:オブジェクトにオブジェクトを追加したい場合は[RequireComponent(typeof(RigidBody))]、クラスの宣言(すべての使用法の下にあるスペース)の上に入力することができます。
楽しんで楽しいゲームを作ろう!





nullreferenceexception