例外のトラブルシューティング : System.NullReferenceException

 

値が NullReferenceException である 参照型 (C#Visual Basic) のメソッドまたはプロパティの使用を試みると、null が発生します。 たとえば、初めに new キーワード (Visual Basic では New) を使用せずにオブジェクトの使用を試みたり、値が null (Visual Basic では Nothing) に設定されているオブジェクトの使用を試みたりした可能性があります。

この記事のセクション

この記事で使用されるクラス

NullReferenceExceptions の共通した原因

開発中に null 参照例外のソースを発見

NullReferenceExceptions の回避

リリース コード内の NullReferenceExceptions の処理

この記事で使用されるクラス

この記事中の例のほとんどは次のクラスの一方または両方を使用します。

public class Automobile { public EngineInfo Engine {get; set;} } public class EngineInfo { public EngineInfo() { } public EngineInfo(string powerSrc, double engineSize) { Power = powerSrc; Size = engineSize; } public double Size { get; set; } public string Power = null; }
Public Class Automobile Public Property Engine As EngineInfo End Class Public Class EngineInfo Public Sub New() End Sub Public Sub New(powerSrc As String, engineSize As Double) Power = powerSrc Size = engineSize End Sub Public Property Size() As Double Public Power As String = Nothing End Class

ページのトップへこの記事のセクション

NullReferenceExceptions の共通した原因

任意の参照型変数は null にすることができます。 ローカル変数、クラスのプロパティ、メソッド パラメーター、およびメソッドの戻り値はすべて null 参照を含むことができます。 これらの変数が null の場合にメソッドまたはプロパティを呼び出すと NullReferenceException が発生します。 具体的な例を次に示します。

ローカル変数またはメンバー フィールドは宣言されているが初期化されていない

プロパティまたはフィールドが null である

メソッド パラメーターが null である

メソッドの戻り値が null である

コレクションまたは配列の中のオブジェクトが null である

条件のために作成されないオブジェクト

メソッドに参照により渡されるオブジェクトが null に設定される

ローカル変数またはメンバー フィールドは宣言されているが初期化されていない

この単純なエラーは Visual Basic コード内で最も頻繁に発生します。 out パラメーターとして渡される変数を宣言する場合などを除き、C# コンパイラではローカル参照変数を初期化せずに使用できません。 Visual Basic コンパイラは警告を生成します。

  • 次の C# コードの強調表示された行は、このコンパイラ エラーを生成します。

    未割り当てのローカル変数 engine の使用
  • 次の Visual Basic コードの強調表示された行は、コンパイラ警告 BC42104 を生成します。

    変数 engine は、値が割り当てられる前に参照によって使用されています。 実行時には、null 参照の例外が発生する可能性があります。

    また、その業は実行時に NullReferenceException をスローします。

public void NullReferencFromUninitializedLocalVariable() { EngineInfo engine; Console.WriteLine(engine.ToString()); }
Public Sub NullReferencFromUninitializedLocalVariable() Dim engine As EngineInfo Console.WriteLine(engine.ToString()) End Sub

ページのトップへNullReferenceExceptions の共通した原因

ページのトップへこの記事のセクション

プロパティまたはフィールドが null である

クラスが作成されると、クラスのフィールドとプロパティは自動的に既定値に初期化されます。 参照型の既定値は null (Visual Basic の場合は Nothing) です。フィールドまたはプロパティの値が null の場合に親クラスのフィールドまたはプロパティでメンバー メソッドを呼び出すと NullReferenceException が発生します。

この例で、Engine の car プロパティは null に自動初期化されるため、強調表示された行は NullReferenceException をスローします。

public void NullReferenceFromProperty() { var car = new Automobile(); Console.WriteLine(car.Engine.ToString()); }
Public Sub NullReferenceFromProperty() Dim car = New Automobile() Console.WriteLine(car.Engine.ToString()) End Sub

ページのトップへNullReferenceExceptions の共通した原因

ページのトップへこの記事のセクション

メソッド パラメーターが null である

参照型であるメソッド パラメーターは null (Visual Basic の場合は Nothing) になることができます。 null であるパラメーター値でメンバー メソッドまたはプロパティを呼び出すと NullReferenceException が発生します。

この例で、BadEngineInfoPassedToMethod は null であるパラメーターを使用して NullReferenceFromMethodParameter を呼び出すため、強調表示された行は NullReferenceException をスローします。

public void BadEngineInfoPassedToMethod() { EngineInfo eng = null; NullReferenceFromMethodParameter(eng); } public void NullReferenceFromMethodParameter(EngineInfo engine) { Console.WriteLine(engine.ToString()); }
Public Sub BadParameterPassedToMethod() As EngineInfo Dim eng As EngineInfo = Nothing NullReferenceFromMethodParameter(eng) End Sub Public Sub NullReferenceFromMethodParameter(engine As EngineInfo) Console.WriteLine(engine.ToString()) End Sub

ページのトップへNullReferenceExceptions の共通した原因

ページのトップへこの記事のセクション

メソッドの戻り値が null である

参照型を返すメソッドは null (Visual Basic の場合は Nothing) を返すことがあります。 参照が null の場合、返される参照型のメソッドまたはプロパティを呼び出すと、NullReferenceException が発生します。

この例で、BadGetEngineInfo の呼び出しは NullReferenceFromMethodParameter メソッドで null 参照を返すので、強調表示された行は NullReferenceException をスローします。

public EngineInfo BadGetEngineInfo() { EngineInfo engine = null; return engine; } public void NullReferenceFromMethodReturnValue() { var engine = BadGetEngineInfo(); Console.WriteLine(engine.ToString()); }
Public Function BadGetEngineInfo() As EngineInfo Dim engine As EngineInfo = Nothing Return engine End Function Public Sub NullReferenceFromMethodReturnValue() Dim engine = BadGetEngineInfo() Console.WriteLine(engine.ToString()) End Sub

ページのトップへNullReferenceExceptions の共通した原因

ページのトップへこの記事のセクション

コレクションまたは配列の中のオブジェクトが null である

参照型のリストまたは配列には null である項目が含まれていることがあります。 null であるリスト項目のメソッドまたはプロパティを呼び出すと NullReferenceException が発生します。

この例で、NullReferenceFromListItem() の呼び出しは null である項目を返すため、BadGetCarList 内の強調表示された行は NullReferenceException をスローします。

public Automobile[] BadGetCarList() { var autos = new Automobile[10]; for (int i = 0; i autos.Length; i++) { if (i != 6) { autos[i] = new Automobile(); } } return autos; } public void NullReferenceFromListItem() { var cars = BadGetCarList(); foreach (Automobile car in cars) { Console.WriteLine(car.ToString()); } }
Public Function BadGetCarList() As Automobile() Dim autos = New Automobile(10) {} For i As Integer = 0 To 9 If i <> 6 Then autos(i) = New Automobile() End If Next Return autos End Function Public Sub NullReferenceFromListItem() Dim cars = BadGetCarList() For Each car As Automobile In cars Console.WriteLine(car.ToString()) Next End Sub

ページのトップへNullReferenceExceptions の共通した原因

ページのトップへこの記事のセクション

条件のために作成されないオブジェクト

参照型が条件ブロック内で初期化される場合、条件が false のときはオブジェクトが作成されません。

この例で、NullReferenceFromConditionalCreation の強調表示された行は、engine メソッドが DetermineTheCondition() を返す場合にのみ true 変数を初期化するので、NullReferenceException をスローします。

public bool DetermineTheCondition() { return false; } public void NullReferenceFromConditionalCreation() { EngineInfo engine = null; var condition = DetermineTheCondition(); if (condition) { engine = new EngineInfo(); engine.Power = "Diesel"; engine.Size = 2.4; } Console.WriteLine(engine.Size); }
Public Function DetermineTheCondition() As Boolean Return False End Function Public Sub NullReferenceFromConditionalCreation() Dim engine As EngineInfo = Nothing Dim condition = DetermineTheCondition() If condition Then engine = New EngineInfo() engine.Power = "Diesel" engine.Size = 2.4 End If Console.WriteLine(engine.Size) End Sub

ページのトップへNullReferenceExceptions の共通した原因

ページのトップへこの記事のセクション

メソッドに渡されるオブジェクトのプロパティが null に設定される

オブジェクトをパラメーターとして値によりメソッドに渡す場合 (C# の場合は ref または out キーワードを使用せず、Visual Basic の場合は ByRef キーワードを使用しない)、メソッドはパラメーターのメモリ位置 (パラメーターが指し示す場所) を変更することはできませんが、オブジェクトのプロパティは変更できます。

この例では、NullPropertyReferenceFromPassToMethod メソッドが Automobile オブジェクトを作成し、Engine プロパティを初期化します。 次に、BadSwapCarEngine を呼び出し、新しいオブジェクトをパラメーターとして渡します。BadSwapCarEngine は Engine プロパティを null に設定するため、NullPropertyReferenceFromPassToMethod の強調表示された行は NullReferenceException をスローします。

public void BadSwapCarEngine(Automobile car) { car.Engine = null; } public void (Automobile car) { car.Engine = new EngineInfo("GAS", 1.5); BadSwapCarEngine(car); Console.WriteLine(car.Engine.ToString()); }
Public Sub BadSwapCarEngine(car As Automobile) car.Engine = Nothing End Sub Public Sub NullPropertyReferenceFromPassToMethod() Dim car As New Automobile() car.Engine = New EngineInfo("GAS", 1.5) BadSwapCarEngine(car) Console.WriteLine(car.Engine.ToString()) End Sub

ページのトップへNullReferenceExceptions の共通した原因

ページのトップへこの記事のセクション

メソッドに参照により渡されるオブジェクトが null に設定される

参照型をパラメーターとして参照によりメソッドに渡す場合 (C# の場合は ref または out キーワードを使用し、Visual Basic の場合は ByRef キーワードを使用)、パラメーターが指し示すメモリ位置を変更できます。

参照型を参照によりメソッドに渡す場合、メソッドは参照される型を null (Visual Basic の場合は Nothing) に設定することがあります。

この例で、NullReferenceFromPassToMethodByRef メソッドの呼び出しは BadEngineSwapByRef 変数を null に設定するため、stockEngine の強調表示された行は NullReferenceException をスローします。

public void BadEngineSwapByRef(ref EngineInfo engine) { engine = null; } public void NullReferenceFromPassToMethodByRef() { var stockEngine = new EngineInfo(); stockEngine.Power = "Gas"; stockEngine.Size = 7.0; BadSwapEngineByRef(ref stockEngine); Console.WriteLine(stockEngine.ToString()); }
Public Sub BadSwapEngineByRef(ByRef engine As EngineInfo) engine = Nothing End Sub Public Sub NullReferenceFromPassToMethodByRef() Dim formatStr = "The stock engine has been replaced by a {0} liter {} engine" Dim stockEngine = New EngineInfo() stockEngine.Power = "Gas" stockEngine.Size = 7.0 BadSwapEngineByRef(stockEngine) Console.WriteLine(stockEngine.ToString()) End Sub

ページのトップへNullReferenceExceptions の共通した原因

ページのトップへこの記事のセクション

開発中に null 参照例外のソースを発見

データヒント、ローカル ウィンドウ、およびウォッチ ウィンドウを使用して変数値を確認する

呼び出し履歴を調べて参照変数が初期化されない場所または null に設定される場所を探します。

条件付きブレークポイントを設定して、オブジェクトが null (Visual Basic の場合は Nothing) の場合にデバッグを停止する

データヒント、ローカル ウィンドウ、およびウォッチ ウィンドウを使用して変数値を確認する

  • 変数名の上にマウス ポインターを置き、データヒントで変数の値を確認します。 変数がオブジェクトまたはコレクションを参照する場合、データ型を拡張してプロパティまたは要素を確認できます。

  • [ローカル] ウィンドウを開き、現在のコンテキストでアクティブな変数を確認します。

  • ウォッチ ウィンドウを使用して、コードをステップ実行するのにつれて変数がどのように変化するかを確認します。

ページのトップへ開発中に null 参照例外のソースを発見

ページのトップへこの記事のセクション

呼び出し履歴を調べて参照変数が初期化されない場所または null に設定される場所を探します。

Visual Studio の [呼び出し履歴] ウィンドウには、例外またはブレークポイントでデバッガーが停止するときに完了していないメソッドの名前が一覧表示されます。[呼び出し履歴] ウィンドウで名前を選択し、[フレームに切り替え] を選択して、実行コンテキストをメソッドに変更し、その変数を確認できます。

ページのトップへ開発中に null 参照例外のソースを発見

ページのトップへこの記事のセクション

条件付きブレークポイントを設定して、オブジェクトが null (Visual Basic の場合は Nothing) の場合にデバッグを停止する

変数が null の場合は、条件付きブレークポイントをブレークに設定できます。 条件付きブレークポイントは、コレクション内の項目が断続的にしか null にならない場合など、null 参照が頻繁には発生しない場合に役立つことがあります。 条件付きブレークポイントのもう 1 つの利点は、特定の処理ルーチンをコミットする前に問題をデバッグできるということです。

ページのトップへ開発中に null 参照例外のソースを発見

ページのトップへこの記事のセクション

NullReferenceExceptions の回避

Debug.Assert を使用してインバリアントを確認する

参照型を完全に初期化する

Debug.Assert を使用してインバリアントを確認する

インバリアントは true であることが確かな条件です。Debug.Assert (System.Diagnostics) ステートメントは、アプリのデバッグ ビルドからのみ呼び出され、リリース コードからは呼び出されません。 インバリアント条件が true でない場合、デバッガーは Assert ステートメントで中断し、ダイアログ ボックスを表示します。Debug.Assert は、アプリの開発中に条件が変更されていないことをチェックします。 アサーションは、コードを読む他の開発者用に、条件が常に true でなければならないことを記録する役割も果たします。

たとえば、MakeEngineFaster メソッドは、ただ 1 つの呼び出しメソッド (engine) が TheOnlyCallerOfMakeEngineFaster を完全に初期化するとわかっているため、EngineInfo パラメーターが常に null でないと仮定します。MakeEngineFaster 内のアサートはこの仮定を記録し、仮定が true であることをチェックします。

開発者がパラメーターを初期化しない新しい呼び出しメソッド (BadNewCallerOfMakeEngineFaster) を追加すると、アサートがトリガーされます。

private void TheOnlyCallerOfMakeEngineFaster() { var engine = new EngineInfo(); engine.Power = "GAS"; engine.Size = 1.5; MakeEngineFaster(engine); } private void MakeEngineFaster(EngineInfo engine) { System.Diagnostics.Debug.Assert(engine != null, "Assert: engine != null"); engine.Size *= 2; Console.WriteLine("The engine is twice as fast"); } private void BadNewCallerOfMakeEngineFaster() { EngineInfo engine = null; MakeEngineFaster(engine); }
Public Sub TheOnlyCallerOfMakeEngineFaster() Dim engine As New EngineInfo engine.Power = "GAS" engine.Size = 1.5 MakeEngineFaster(engine) End Sub Private Sub MakeEngineFaster(engine As EngineInfo) System.Diagnostics.Debug.Assert(engine IsNot Nothing, "Assert: engine IsNot Nothing") engine.Size = engine.Size * 2 Console.WriteLine("The engine is twice as fast") End Sub Public Sub BadNewCallerOfMakeEngineFaster() Dim engine As EngineInfo = Nothing MakeEngineFaster(engine) End Sub

ページのトップへNullReferenceExceptions の回避

ページのトップへこの記事のセクション

参照型を完全に初期化する

多くの NullReferenceExceptions を回避するには、できるだけ作成時の状態に近く、参照型を完全に初期化します。

独自のクラスに完全な初期化を追加する

NullReferenceException をスローするクラスを制御する場合、型のコンストラクター内でオブジェクトを完全に初期化することを検討します。 たとえば、完全な初期化を保証するクラスの改訂された例を次に示します。

public class Automobile { public EngineInfo Engine { get; set; } public Automobile(){this.Engine = new EngineInfo();} public Automobile(string powerSrc, double engineSize) { this.Engine = new EngineInfo(powerSrc, engineSize); } } public class EngineInfo { public double Size {get; set;} public string Power {get; set;} public EngineInfo(){// the base enginethis.Power = "GAS";this.Size = 1.5;} public EngineInfo(string powerSrc, double engineSize) { this.Power = powerSrc; this.Size = engineSize; } }
Public Class Automobile Public Property Engine As EngineInfo     Public Sub New()Me.Engine = New EngineInfo()End SubPublic Sub New(powerSrc As String, engineSize As Double)Me.Engine = New EngineInfo(powerSrc, engineSize)End Sub End Class Public Class BaseEngineInfo Public Sub New()' the base engineMe.Power = "GAS"Me.Size = 1.5End Sub Public Sub New(powerSrc As String, engineSize As Double) Power = powerSrc Size = engineSize End Sub Public Property Size() As Double Public Power As String = String.Empty End Class

注意

大規模またはあまり使用されないプロパティに関して限定的な初期化を使用する

クラスのメモリ使用量を削減し、パフォーマンスを向上させるには、参照型プロパティの限定的な初期化の使用を検討します。 「限定的な初期化」を参照してください。

リリース コード内の NullReferenceExceptions の処理

参照型を使用する前に null (Visual Basic の場合は Nothing) をチェックする

try – catch – finally (Visual Basic の場合は Try – Catch – Finally) を使用して例外を処理する

NullReferenceException は発生後に処理するより、回避することの方が適切です。 例外の処理によって、コードは管理や理解が難しくなることがあるうえ、他のバグが生じることも考えられます。 NullReferenceException は多くの場合、回復不能なエラーです。 こうした場合、例外によってアプリを停止させることが最良の代替策である可能性があります。

ただし、次のように、エラーの処理が有用である状況は多く存在します。

  • アプリは null であるオブジェクトを無視できます。 たとえば、アプリがデータベース内のレコードを取得して処理する場合、結果として null オブジェクトである、いくつかの不良レコードを無視できる可能性があります。 必要な処理は、ログ ファイルまたはアプリケーションの UI に不良データを記録することだけであることが考えられます。

  • 例外から回復することができます。 たとえば、参照型を返す Web サービスの呼び出しは、接続が失われた場合や接続がタイムアウトした場合には、null を返す可能性があります。 接続を再確立し、呼び出しを再度試すことができます。

  • アプリの状態を有効な状態に復元できます。 たとえば、NullReferenceException をスローするメソッドを呼び出す前に、情報をデータ ストアに保存する必要がある、複数手順のタスクを実行していることがあります。 初期化されていないオブジェクトがデータ レコードを破損する可能性がある場合、アプリを終了する前に過去のデータを削除できます。

  • 例外の報告が推奨されます。 たとえば、エラーがアプリのユーザーによる間違いに起因する場合、メッセージを生成して、ユーザーが正しい情報を入力するのを支援できます。 問題の解決に役立てるために、エラーに関する情報をログに記録することもできます。 ASP.NET などのいくつかのフレームワークには、アプリのクラッシュを防止するために、すべてのエラーをキャプチャする上位レベルの例外ハンドラーが用意されています。このハンドラーを使用した場合、例外をログに記録することが、その例外が発生したことを知る唯一の方法になる可能性があります。

リリース コードにおける NullReferenceException の処理方法は 2 つあります。

参照型を使用する前に null (Visual Basic の場合は Nothing) をチェックする

オブジェクトを使用する前に null のための明示的なテストを使用すると、try-catch-finally 構造のパフォーマンス低下を回避できます。 ただし、初期化されていないオブジェクトに対する処理を判別して実装する必要はあります。

この例では、CheckForNullReferenceFromMethodReturnValue が BadGetEngineInfo メソッドの戻り値をテストします。 オブジェクトが null でない場合は使用されます。null の場合、メソッドはエラーを報告します。

public EngineInfo BadGetEngineInfo() { EngineInfo engine = null; return engine; } public void CheckForNullReferenceFromMethodReturnValue() { var engine = BadGetEngineInfo(); if(engine != null) { // modify the info engine.Power = "DIESEL"; engine.Size = 2.4; } else { // report the error Console.WriteLine("BadGetEngine returned null") } }
public EngineInfo BadGetEngineInfo() { EngineInfo engine = null; return engine; } Public Sub CheckForNullReferenceFromMethodReturnValue() Dim engine = BadGetEngineInfo() If (engine IsNot Nothing) Then ' modify the info engine.Power = "DIESEL" engine.Size = 2.4 Else ' report the error Console.WriteLine("BadGetEngineInfo returned Nothing") End If End Sub

ページのトップへリリース コード内の NullReferenceExceptions の処理

ページのトップへこの記事のセクション

try – catch – finally (Visual Basic の場合は Try – Catch – Finally) を使用して例外を処理する

組み込み例外処理構造 (C# の場合は try, catch, finally、Visual Basic の場合は Try, Catch, Finally) の使用は、オブジェクトが null でないことのチェックに比べて、NullReferenceExceptions の処理に関してより多くの選択肢を提供します。

この例で、CatchNullReferenceFromMethodCall は 2 つのアサートを使用して、パラメーターがエンジンを含め、完全な自動車を含む仮定を確認します。try ブロックで、RarelyBadEngineSwap の呼び出しは自動車の Engine プロパティを壊すことがあるので、強調表示された行は NullReferenceException をスローします。catch ブロックは例外をキャプチャし、例外情報をファイルに書き出し、エラーをユーザーに報告します。finally ブロックで、メソッドは自動車の状態がメソッドの開始時ほど悪くないことを保証します。

public void RarelyBadSwapCarEngine(Automobile car) { if ((new Random()).Next() == 42) { car.Engine = null; } else { car.Engine = new EngineInfo("DIESEL", 2.4); } } public void CatchNullReferenceFromMethodCall(Automobile car) { System.Diagnostics.Debug.Assert(car != null, "Assert: car != null"); System.Diagnostics.Debug.Assert(car.Engine != null, "Assert: car.Engine != null"); // save current engine properties in case they're needed var enginePowerBefore = car.Engine.Power; var engineSizeBefore = car.Engine.Size; try { RarelyBadSwapCarEngine(car); var msg = "Swap succeeded. New engine power source: {0} size {1}"; Console.WriteLine(msg, car.Engine.Power, car.Engine.Size); } catch(NullReferenceException nullRefEx) { // write exception info to log file LogException(nullRefEx); // notify the user Console.WriteLine("Engine swap failed. Please call your customer rep."); } finally { if(car.Engine == null) { car.Engine = new EngineInfo(enginePowerBefore, engineSizeBefore); } } }
Public Sub RarelyBadSwapCarEngine(car As Automobile) If (New Random()).Next = 42 Then car.Engine = Nothing Else car.Engine = New EngineInfo("DIESEL", 2.4) End If End Sub Public Sub CatchNullReferenceFromMethodCall(car As Automobile) System.Diagnostics.Debug.Assert(car IsNot Nothing) System.Diagnostics.Debug.Assert(car.Engine IsNot Nothing) ' save current engine properties in case they're needed Dim powerBefore = car.Engine.Power Dim sizeBefore = car.Engine.Size Try RarelyBadSwapCarEngine(car) Dim msg = "Swap succeeded. New engine power source: {0} size {1}" Console.WriteLine(msg, car.Engine.Power, car.Engine.Size) Catch nullRefEx As NullReferenceException ' write exception info to log file LogException(nullRefEx) ' notify user Console.WriteLine("Engine swap failed. Please call your customer rep.") Finally If car.Engine Is Nothing Then car.Engine = New EngineInfo(powerBefore, sizeBefore) End Try End Sub

ページのトップへリリース コード内の NullReferenceExceptions の処理

ページのトップへこの記事のセクション

関連記事

例外のデザインのガイドライン (.NET Framework デザイン ガイドライン)

例外の処理とスロー (.NET Framework アプリケーションの基本事項)

方法: 初回例外通知を受け取る (.NET Framework 開発ガイド)

方法: PLINQ クエリの例外を処理する (.NET Framework 開発ガイド)

マネージ スレッドの例外 (.NET Framework 開発ガイド)

例外と例外処理 (C# プログラミング ガイド)

例外処理ステートメント (C# リファレンス)

Try...Catch...Finally ステートメント (Visual Basic)

例外処理 (F#)

C++/CLI の例外

例外処理 (タスク並列ライブラリ)

デバッガーでの例外の管理

チュートリアル : 同時実行例外の処理 (Visual Studio におけるデータのアクセス)

方法 : データ バインドで発生するエラーと例外を処理する (Windows フォーム)

ネットワーク アプリでの例外の処理 (XAML) (Windows)

ページのトップへこの記事のセクション