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

 

System.InvalidOperationException は、オブジェクトのメソッドが呼び出されたときに、そのオブジェクトの状態が、メソッドの呼び出しをサポートできない場合にスローされます。 この例外は、メソッドがメインまたは UI 以外のスレッドから UI を操作しようとしたときにもスローされます。

重要

InvalidOperationException はさまざまな状況でスローされるので、例外処理アシスタントに表示される Message を読んで、その内容を理解することが重要です。

この記事の内容

UI 以外のスレッドで実行されているメソッドによって UI が更新される

foreach (Visual Basic では For Each) ブロック内のステートメントによって、反復処理中のコレクションが変更される

null の Nullable <T> が T にキャストされる

System.Linq.Enumerable メソッドが空のコレクションに対して呼び出される

関連記事

この記事のコード例では、アプリにおいて、InvalidOperationException 例外が発生する可能性があるいくつかの一般的な状況を示します。問題への対処方法は、その状況によって異なります。アプリの機能にとって致命的な例外がある場合は、try … catch (Visual Basic では Try ..Catch) コンストラクトを使用してその例外をキャプチャし、アプリの状態を消去した後、アプリを終了することをお勧めします。ただし、その他の InvalidOperationException は、予想して回避できます。変更後のメソッドの例で、これらの手法をいくつか紹介します。

UI 以外のスレッドで実行されているメソッドによって UI が更新される

UI 以外のスレッドからの UI の更新を原因とする InvalidOperationException の発生 | UI 以外のスレッドにおける InvalidOperationExceptions の回避

Windows フォームや Windows Presentation Foundation (WPF) など、ほとんどの .NET GUI (グラフィカル ユーザー インターフェイス) アプリ フレームワークでは、UI を管理するスレッド (メインまたは UI スレッド) からのみ、GUI オブジェクトにアクセスできます。 UI 以外のスレッドから UI 要素にアクセスしようとすると、InvalidOperationException がスローされます。

UI 以外のスレッドからの UI の更新を原因とする InvalidOperationException の発生

注意

次の例では、Task-based Asynchronous Pattern (TAP) を使用して UI 以外のスレッドを作成します。 ただし、これらの例はすべて .NET のAsynchronous Programming Patternsに関連しています。

これらの例では、ThreadsExampleBtn_Click イベント ハンドラーが DoSomeWork メソッドを 2 回呼び出します。 メソッドの 1 回目の呼び出し (DoSomeWork(20);) は同期的に実行され、成功します。 ただし、2 回目の呼び出し (Task.Run( () => { DoSomeWork(1000);});) はアプリのスレッド プール内のスレッドで実行されます。 この呼び出しは UI 以外のスレッドから UI を更新しようとするので、ステートメントは InvalidOperationException をスローします。 

WPF およびストア アプリ

注意

ストア Phone アプリでは、より詳細な Exception ではなく、InvalidOperationException がスローされます。

例外メッセージ:

WPF アプリ

追加情報: このオブジェクトは別のスレッドに所有されているため、呼び出しスレッドはこのオブジェクトにアクセスできません。

ストア アプリ

追加情報: アプリケーションは、別のスレッドにマーシャリングされたインターフェイスを呼び出しました。 (HRESULT からの例外: 0x8001010E (RPC_E_WRONG_THREAD))

private async void ThreadsExampleBtn_Click(object sender, RoutedEventArgs e) { TextBox1.Text = String.Empty; TextBox1.Text = "Simulating work on UI thread.\n"; DoSomeWork(20); TextBox1.Text += "Simulating work on non-UI thread.\n"; await Task.Run(() => DoSomeWork(1000)); TextBox1.Text += "ThreadsExampleBtn_Click completes. "; } private void DoSomeWork(int msOfWork) { // simulate work var endTime = DateTime.Now.AddMilliseconds(msOfWork); while (DateTime.Now < endTime) { // spin }; // report completion var msg = String.Format("Some work completed in {0} ms on UI thread. \n", msOfWork); TextBox1.Text += msg; }

Windows フォーム アプリ

例外メッセージ:

  • 追加情報: 有効ではないスレッド間の操作: コントロールが作成されたスレッド以外のスレッドからコントロール 'TextBox1' がアクセスされました。
private async void ThreadsExampleBtn_Click(object sender, EventArgs e) { TextBox1.Text = String.Empty; var tbLinesList = new List<string>() {"Simulating work on UI thread."}; TextBox1.Lines = tbLinesList.ToArray(); DoSomeWork(20, tbLinesList); tbLinesList.Add("Simulating work on non-UI thread."); TextBox1.Lines = tbLinesList.ToArray(); await Task.Run(() => DoSomeWork(1000, tbLinesList)); tbLinesList.Add("ThreadsExampleBtn_Click completes."); TextBox1.Lines = tbLinesList.ToArray(); } private void DoSomeWork(int msOfWork, List<string> tbLinesList) { // simulate work var endTime = DateTime.Now.AddMilliseconds(msOfWork); while (DateTime.Now < endTime) { }; { // spin }; // report completion var msg = String.Format("Some work completed in {0} ms on UI thread. \n", msOfWork); tbLinesList.Add(msg); TextBox1.Lines = tbLinesList.ToArray(); }

ページのトップへこの記事の内容In this sectionUI 以外のスレッドで実行されているメソッドによって UI が更新される

UI 以外のスレッドにおける InvalidOperationExceptions の回避

Windows UI フレームワークでは、UI 要素のメンバーへの呼び出しが UI スレッドで実行されているかどうかを確認するメソッドと、UI スレッドでの呼び出しをスケジュールするその他のメソッドを含む、ディスパッチャー パターンが実装されます。

WPF アプリ

WPF アプリでは、いずれかの Dispatcher.Invoke メソッドを使用して、UI スレッドでデリゲートを実行します。 必要に応じて、Dispatcher.CheckAccess メソッドを使用して、メソッドが UI 以外のスレッドで実行されているかどうかを確認します。

private void DoSomeWork(int msOfWork) { var endTime = DateTime.Now.AddMilliseconds(msOfWork); while (DateTime.Now < endTime) { // spin }; // report completion var msgFormat = "Some work completed in {0} ms on {1}UI thread.\n"; var msg = String.Empty; if (TextBox1.Dispatcher.CheckAccess()) { msg = String.Format(msgFormat, msOfWork, String.Empty); TextBox1.Text += msg; } else { msg = String.Format(msgFormat, msOfWork, "non-"); Action act = ()=> {TextBox1.Text += msg;}; TextBox1.Dispatcher.Invoke(act); } }

Windows フォーム アプリ

Windows フォーム アプリでは、Control.Invoke メソッドを使用して、UI スレッドを更新するデリゲートを実行します。 必要に応じて、Control.InvokeRequired プロパティを使用して、メソッドが UI 以外のスレッドで実行されているかどうかを確認します。

private void DoSomeWork(int msOfWork, List<string> tbLinesList) { // simulate work var endTime = DateTime.Now.AddMilliseconds(msOfWork); while (DateTime.Now < endTime) { // spin }; // report completion var msgFormat = "Some work completed in {0} ms on {1}UI thread.\n"; var msg = String.Empty; if (TextBox1.InvokeRequired) { msg = String.Format(msgFormat, msOfWork, "non-"); tbLinesList.Add(msg); Action act = () => TextBox1.Lines = tbLinesList.ToArray(); TextBox1.Invoke( act ); } else { msg = String.Format(msgFormat, msOfWork, String.Empty); tbLinesList.Add(msg); TextBox1.Lines = tbLinesList.ToArray(); } }

ストア アプリ

ストア アプリでは、M:Windows.UI.Core.CoreDispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority,Windows.UI.Core.DispatchedHandler) メソッドを使用して、UI スレッドを更新するデリゲートを実行します。 必要に応じて、P:Windows.UI.Core.CoreDispatcher.HasThreadAccess プロパティを使用して、メソッドが UI 以外のスレッドで実行されているかどうかを確認します。

private void DoSomeWork(int msOfWork) { // simulate work var endTime = DateTime.Now.AddMilliseconds(msOfWork); while (DateTime.Now < endTime) { // spin }; // report completion var msgFormat = "Some work completed in {0} ms on {1}UI thread.\n"; var msg = String.Empty; if (TextBox1.Dispatcher.HasThreadAccess) { msg = String.Format(msgFormat, msOfWork, String.Empty); TextBox1.Text += msg; } else { msg = String.Format(msgFormat, msOfWork, "non-"); TextBox1.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,()=> {TextBox1.Text += msg;}); } }

ページのトップへこの記事の内容In this sectionUI 以外のスレッドで実行されているメソッドによって UI が更新される

foreach (Visual Basic では For Each) ブロック内のステートメントによって、反復処理中のコレクションが変更される

foreach での InvalidOperationException の発生 | ループ内での InvalidOperationExceptions の回避

foreach ステートメント (Visual Basic では For Each) は、System.Collections.IEnumerable または System.Collections.Generic.IEnumerable<T> インターフェイスが実装された配列またはコレクション内の各要素に対して、埋め込みステートメントのグループを繰り返し実行します。foreach ステートメントを使用すると、コレクションの反復処理による要素の読み取りと変更を行うことができますが、コレクションに対する項目の追加または削除を行うことはできません。 foreach ステートメント内でソース コレクションに対して項目を追加または削除した場合、InvalidOperationException がスローされます。

foreach での InvalidOperationException の発生

この例では、InvalidOperationException ステートメントが foreach ブロック内でリストを変更しようとしたときに、numList.Add(5); がスローされます。

例外メッセージ:

  • 追加情報: コレクションは変更されています。列挙操作は実行されない場合があります。
private void AddElementsToAList() { var numList = new List<int>() { 1, 2, 3, 4 }; foreach (var num in numList) { if (num == 2) { numList.Add(5); } } // Display list elements foreach (var num in numList) { Console.Write("{0} ", num); } }

ページのトップへこの記事の内容In this sectionforeach (Visual Basic では For Each) ブロック内のステートメントによって、反復処理中のコレクションが変更される

ループ内での InvalidOperationExceptions の回避

重要

コレクションの反復処理中に、要素をリストに対して追加または削除した場合、予測しにくい想定外の悪影響が発生する可能性があります。 可能な限り、この操作を反復処理以外の部分に移動してください。

private void AddElementsToAList () { var numList = new List<int>() { 1, 2, 3, 4 }; var numberOf2s = 0; foreach (var num in numList) { if (num == 2) { numberOf2s++; } } for (var i = 0; i < numberOf2s; i++ ){numList.Add(5);} // Display list elements foreach (var num in numList) { Console.Write("{0} ", num); } }

コレクションの反復処理中に、リストに対して要素を追加または削除する必要がある場合は、for (Visual Basic では For) ループを使用します。

private void AddElementsToAList () { var numList = new List<int>() { 1, 2, 3, 4 }; for (var i = 0; i < numList.Count; i++) { if (numList[i] == 2) { numList.Add(5); } } // Display list elements foreach (var num in numList) { Console.Write("{0} ", num); } }

ページのトップへこの記事の内容In this sectionforeach (Visual Basic では For Each) ブロック内のステートメントによって、反復処理中のコレクションが変更される

null の Nullable <T> が T にキャストされる

無効なキャストを原因とする InvalidOperationException の発生 | 無効なキャストを原因とする InvalidOperationException の回避

Nullable<T> (Visual Basic では null) の Nothing 構造体を、その基になる型にキャストした場合、InvalidOperationException 例外がスローされます。

無効なキャストを原因とする InvalidOperationException の発生

このメソッドでは、Select メソッドが dbQueryResults の null 要素を int にキャストしたときに、InvalidOperationException がスローされます。

例外メッセージ:

  • 追加情報: Null 許容のオブジェクトには値を指定しなければなりません。
private void MapQueryResults() { var dbQueryResults = new int?[] { 1, 2, null, 4 }; var ormMap = dbQueryResults.Select(nullableInt => (int)nullableInt); //display map list foreach (var num in ormMap) { Console.Write("{0} ", num); } Console.WriteLine(); }

ページのトップへこの記事の内容In this sectionnull の Nullable <T> が T にキャストされる

無効なキャストを原因とする InvalidOperationException の回避

InvalidOperationException を回避する方法は、次のとおりです。

  • Nullable<T>.HasValue プロパティを使用して、null 以外の要素のみを選択します。

  • オーバーロードされたいずれかの Nullable<T>.GetValueOrDefault メソッドを使用して、null の項目の既定値を指定します。

Nullable<T>.HasValue の例

private void MapQueryResults() { var dbQueryResults = new int?[] { 1, 2, null, 4 }; var ormMap = dbQueryResults .Where(nulllableInt => nulllableInt.HasValue) .Select(num => (int)num); //display map list foreach (var num in ormMap) { Console.Write("{0} ", num); } Console.WriteLine(); // handle nulls Console.WriteLine("{0} nulls encountered in dbQueryResults", dbQueryResults.Where(nullableInt => !nullableInt.HasValue).Count()); }

Nullable<T>.GetValueOrDefault の例

この例では、GetValueOrDefault(T) を使用して、dbQueryResults から返されることが予期される値以外の既定値を指定します。

private void MapQueryResults() { var dbQueryResults = new int?[] { 1, 2, null, 4 }; var nullFlag = int.MinValue; var ormMap = dbQueryResults.Select(nullableInt => nullableInt.GetValueOrDefault(nullFlag)); // handle nulls Console.WriteLine("'{0}' indicates a null database value.", nullFlag); foreach (var num in ormMap) { Console.Write("{0} ", num); } Console.WriteLine(); }

ページのトップへこの記事の内容In this sectionnull の Nullable <T> が T にキャストされる

System.Linq.Enumerable メソッドが空のコレクションに対して呼び出される

Enumerable メソッド AggregateAverageLastMaxMinFirstSingle、および SingleOrDefault は、シーケンスに対して操作を実行し、1 つの結果を返します。

これらのメソッドの一部のオーバーロードは、シーケンスが空の場合、InvalidOperationException 例外をスローします (その他のオーバーロードは null (Visual Basic では Nothing) を返します)。SingleOrDefault も、シーケンスに複数の要素が含まれている場合、InvalidOperationException をスローします。

ヒント

このセクションで説明する Enumerable メソッドのほとんどは、オーバーロードされています。 使用するオーバーロードは、その動作を理解したうえで選択してください。

例外メッセージ:

Aggregate、Average、Last、Max、および Min メソッド | First および FirstOrDefault メソッド | Single および SingleOrDefault メソッド

Aggregate、Average、Last、Max、および Min メソッド

  • 追加情報: シーケンスに要素が含まれていません

Average での InvalidOperationException の発生

この例では、次のメソッドが InvalidOperationException メソッドを呼び出すときに、Average がスローされます。これは、Linq 式が 4 を超える要素を含まないシーケンスを返すからです。

private void FindAverageOfNumbersGreaterThan4() { var dbQueryResults = new[] { 1, 2, 3, 4 }; var avg = dbQueryResults.Where(num => num > 4).Average(); Console.WriteLine("The average value numbers greater than 4 in the returned records is {0}", avg); }

空のシーケンスを確認せずに、これらのメソッドのいずれかを使用した場合、シーケンスが空ではないこと、および空のシーケンスの発生を予期していないことを暗黙的に仮定することになります。 シーケンスが空ではないと仮定した場合、例外をキャッチまたはスローすることは適切です。

空のシーケンスを原因とする InvalidOperationException の回避

いずれかの Enumerable.Any メソッドを使用して、シーケンスが空であるかどうかを確認します。

ヒント

Any を使用すると、シーケンスに平均して多くの要素が含まれている可能性がある場合や、シーケンスを生成する操作の負荷が高い場合、確認のパフォーマンスが向上することがあります。

private void FindAverageOfNumbersGreaterThan4() { var dbQueryResults = new[] { 1, 2, 3, 4 }; var moreThan4 = dbQueryResults.Where(num => num > 4); if(moreThan4.Any()) { var msgFormat = "The average value numbers greater than 4 in the returned records is {0}"; Console.WriteLine(msgFormat, moreThan4.Average()); } else { // handle empty collection Console.WriteLine("There are no values greater than 4 in the ecords."); } }

ページのトップへこの記事の内容In this sectionSystem.Linq.Enumerable メソッドが空のコレクションに対して呼び出される

First および FirstOrDefault メソッド

First はシーケンス内の最初の項目を返し、シーケンスが空の場合は InvalidOperationException をスローします。FirstOrDefault の代わりに First メソッドを呼び出して、例外をスローする代わりに、指定した値または既定値を返すことができます。

注意

.NET Framework では、型に既定値の概念があります。 たとえば、参照型の既定値は null で、整数型の既定値は 0 です。 「既定値の一覧表 (C# リファレンス)」を参照してください。

First での InvalidOperationException の発生

First は、指定した条件を満たすシーケンス内の最初の要素を返す最適化メソッドです。 条件を満たす要素が見つからない場合、このメソッドは InvalidOperationException 例外をスローします。

この例では、First に 4 よりも大きい要素が含まれていないので、dbQueryResults メソッドは例外をスローします。

例外メッセージ:

  • 追加情報: シーケンスに、一致する要素は含まれていません
private void FindANumbersGreaterThan4() { var dbQueryResults = new[] { 1, 2, 3, 4 }; var firstNum = dbQueryResults.First(n=> n > 4); var msgFormat = "{0} is an element of dbQueryResults that is greater than 4"; Console.WriteLine(msgFormat, firstNum); }

FirstOrDefault を原因とする例外の回避

FirstOrDefault の代わりに、いずれかの First メソッドを使用して、firstNum シーケンスに少なくとも 1 つの要素が含まれているかどうかを確認できます。 クエリの条件を満たす要素が見つからない場合は、指定した値または既定値が返されます。 該当する要素が見つかったかどうかは、戻り値で確認できます。

注意

型の既定値がシーケンス内の有効な要素である場合、FirstOrDefault を使用するときの実装は、より複雑になる可能性があります。

private void FindANumbersGreaterThan4() { var dbQueryResults = new[] { 1, 2, 3, 4 }; // default(object) == null var firstNum = dbQueryResults.FirstOrDefault(n => n > 4); if (firstNum != 0) { var msgFormat = "{0} is an element of dbQueryResults that is greater than 4"; Console.WriteLine(msgFormat, firstNum); } else { // handle default case Console.WriteLine("No element of dbQueryResults is greater than 4."); } }

ページのトップへこの記事の内容In this sectionSystem.Linq.Enumerable メソッドが空のコレクションに対して呼び出される

Single および SingleOrDefault メソッド

Enumerable.Single メソッドは、シーケンスの唯一の要素、または指定されたテストの条件を満たす、シーケンスの 1 つの要素を返します。

シーケンス内に要素がない場合、または複数の要素がある場合、このメソッドは InvalidOperationException 例外をスローします。

SingleOrDefault を使用すると、シーケンスに要素が含まれていない場合に、例外をスローする代わりに、指定した値または既定値を返すことができます。 ただし、SingleOrDefault を使用しても、選択述語と一致する複数の要素がシーケンスに含まれている場合は、InvalidOperationException がスローされます。

注意

.NET Framework では、型に既定値の概念があります。 たとえば、参照型の既定値は null で、整数型の既定値は 0 です。 「既定値の一覧表 (C# リファレンス)」を参照してください。

Single での InvalidOperationExceptions の発生

この例では、singleObject に 4 より大きい要素が含まれていないので、InvalidOperationException は dbQueryResults をスローします。

例外メッセージ:

  • 追加情報: シーケンスに、一致する要素は含まれていません
private void FindTheOnlyNumberGreaterThan4() { var dbQueryResults = new[] { (object)1, (object)2, (object)3, (object)4 }; var singleObject = dbQueryResults.Single(obj => (int)obj > 4); // display results var msgFormat = "{0} is the only element of dbQueryResults that is greater than 4"; Console.WriteLine(msgFormat, singleObject); }

Single または SingleOrDefault での InvalidOperationExceptions の発生

この例では、singleObject に 2 より大きい複数の要素が含まれているので、InvalidOperationException は dbQueryResults をスローします。

例外メッセージ:

  • 追加情報: シーケンスに複数の一致する要素が含まれています
private void FindTheOnlyNumberGreaterThan2() { var dbQueryResults = new[] { (object)1, (object)2, (object)3, (object)4 }; var singleObject = dbQueryResults.SingleOrDefault(obj => (int)obj > 2); if (singleObject != null) { var msgFormat = "{0} is the only element of dbQueryResults that is greater than 2"; Console.WriteLine(msgFormat, singleObject); } else { // handle empty collection Console.WriteLine("No element of dbQueryResults is greater than 2."); } }

Single での InvalidOperationExceptions の回避

SingleSingleOrDefault をドキュメントとして使用して、開発者の意図を伝えることもできます。Single からは、条件から返される結果が 1 つだけであることを予期していることがよくわかります。SingleOrDefault からは、予期している結果の数が 1 または 0 であり、それを超える数ではないことがわかります。 これらの条件が無効である場合は、InvalidOperationException をスローおよびキャッチすることが適切です。 ただし、無効な条件がある程度の頻度で発生することが予想される場合は、EnumerableFirst などの他の Where メソッドを使用して結果を生成することを検討してください。

開発中は、いずれかの Assert メソッドを使用して、予期している内容を確認できます。 この例では、強調表示されたコードによってデバッガーが中断され、開発時のアサート ダイアログ ボックスが表示されます。 このアサートはリリース コードでは削除され、結果が無効である場合は Single が例外をスローします。

注意

Take<TSource> を使用し、その count パラメーターを 2 に設定すると、返されるシーケンスの要素が最大 2 つに制限されます。 このシーケンスには、確認する必要があるすべての状況 (要素が 0 個、1 個、および複数の場合) が含まれているので、シーケンスに多くの要素が含まれている可能性がある場合や、シーケンスを生成する操作の負荷が高い場合は、確認のパフォーマンスが向上することがあります。

private void FindTheOnlyNumberGreaterThan4() { var dbQueryResults = new[] { (object)1, (object)2, (object)3, (object)4 }; var moreThan4 = dbQueryResults.Where(obj => (int)obj > 4).Take(2); System.Diagnostics.Debug.Assert(moreThan4.Count() == 1,String.Format("moreThan4.Count() == 1; Actual count: {0}", moreThan4.Count())); // do not handle exceptions in release code Console.WriteLine("{0} is the only element of dbQueryResults that is greater than 4", moreThan4.Single()); }

リリース コードで例外を回避し、さらに無効な状態も処理する必要がある場合は、上記の手法を変更できます。 この例のメソッドは、switch ステートメント内の moreThan2 によって返された要素の数に応じて、異なる処理を実行します。

private void FindTheOnlyNumberGreaterThan2() { var dbQueryResults = new[] { (object)1, (object)2, (object)3, (object)4 }; var moreThan2 = dbQueryResults.TakeWhile(obj => (int)obj > 2).Take(2); switch(moreThan2.Count()) { case 1: // success var msgFormat = "{0} is the only element of dbQueryResults that is greater than 2"; Console.WriteLine(msgFormat, moreThan2.Single()); break; case 0: // handle empty collection Console.WriteLine("No element of the dbQueryResults are greater than 4."); break; default: // count > 1 // handle more than one element Console.WriteLine("More than one element of dbQueryResults is greater than 4"); break; } }

ページのトップへこの記事の内容In this sectionSystem.Linq.Enumerable メソッドが空のコレクションに対して呼び出される

関連記事

例外のデザイン ガイドライン (.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)

ページのトップへこの記事の内容