本文提供此 API 參考文件的補充備註。
InvalidOperationException 在叫用方法失敗是因為無效自變數以外的原因所造成的情況下使用。 一般而言,當物件的狀態不支援方法呼叫時,就會擲回它。 例如,下列方法會拋出 InvalidOperationException 例外:
- IEnumerator.MoveNext 如果在建立列舉值之後修改集合的物件,則為 。 如需詳細資訊,請參閱 在反覆運算集合時變更集合。
- ResourceSet.GetString 如果在進行方法呼叫之前關閉了資源集,則會發生錯誤。
- XContainer.Add若欲加入的物件或物件會導致 XML 文件結構不正確。
- 嘗試從非主要或非UI線程的線程中操作UI的方法。
這很重要
由於InvalidOperationException例外狀況可能在多種情況下擲回,請務必閱讀Message屬性所傳回的訊息以了解詳細錯誤原因。
InvalidOperationException 會使用具有 值0x80131509的 HRESULT COR_E_INVALIDOPERATION
。
如需查看 InvalidOperationException 實例的初始屬性值列表,請參閱 InvalidOperationException 的建構子。
InvalidOperationException 例外狀況的常見原因
下列各節說明如何在應用程式中擲回例外狀況的一些常見案例 InvalidOperationException 。 處理問題的方式取決於特定情況。 不過,最常見的是,例外狀況會產生開發人員錯誤,而且 InvalidOperationException 可以預期並避免例外狀況。
從非UI線程更新UI線程
背景工作線程通常會用來執行一些背景工作,其中牽涉到收集數據以顯示在應用程式的使用者介面中。 不過。 適用於 .NET 的大部分 GUI(圖形使用者介面)應用程式架構,例如 Windows Forms 和 Windows Presentation Foundation (WPF),可讓您只從建立和管理 UI 的線程存取 GUI 物件(主要或 UI 線程)。 當您嘗試從 UI 執行緒以外的執行緒存取 UI 元素時,會拋出InvalidOperationException。 下表顯示例外狀況訊息的文字。
應用程式類型 | 訊息 |
---|---|
WPF 應用程式 | 呼叫線程無法存取這個對象,因為不同的線程擁有它。 |
UWP 應用程式 | 應用程式呼叫了針對不同執行緒封送處理的介面。 |
Windows Forms 應用程式 | 跨執行緒作業無效:控制元件 'TextBox1' 是從其創建執行緒之外的執行緒訪問的。 |
適用於 .NET 的 UI 架構會實作 發送器 模式,其中包含方法,以檢查 UI 線程上是否正在執行 UI 元素成員的呼叫,以及排程 UI 線程上呼叫的其他方法:
- 在 WPF 應用程式中,呼叫 Dispatcher.CheckAccess 方法來判斷方法是否在非 UI 線程上執行。 如果方法是在 UI 線程上執行,則
true
返回,否則會返回false
。 呼叫 Dispatcher.Invoke 方法的其中一個多載以在 UI 執行緒上排程呼叫。 - 在UWP應用程式中,檢查 CoreDispatcher.HasThreadAccess 屬性,以判斷方法是否在非UI線程上執行。 請調用 CoreDispatcher.RunAsync 方法來執行更新UI執行緒的委派。
- 在 Windows Forms 應用程式中,使用 Control.InvokeRequired 屬性來判斷方法是否在非 UI 線程上執行。 呼叫 Control.Invoke 方法的其中一個多載,以執行更新 UI 執行緒的委派。
下列範例說明當您嘗試從非建立該 UI 元素的線程更新它時,所引發的 InvalidOperationException 例外狀況。 每個範例都需要您建立兩個控制項:
- 名為
textBox1
的文字框控制件。 在 Windows Forms 應用程式中,您應該將其 Multiline 屬性設定為true
。 - 名為
threadExampleBtn
的按鈕控制件。 這個範例會提供按鈕ThreadsExampleBtn_Click
事件的處理程式Click
。
在每個案例中,事件處理程式會呼叫
WPF 應用程式
private async void threadExampleBtn_Click(object sender, RoutedEventArgs e)
{
textBox1.Text = String.Empty;
textBox1.Text = "Simulating work on UI thread.\n";
DoSomeWork(20);
textBox1.Text += "Work completed...\n";
textBox1.Text += "Simulating work on non-UI thread.\n";
await Task.Run(() => DoSomeWork(1000));
textBox1.Text += "Work completed...\n";
}
private async void DoSomeWork(int milliseconds)
{
// Simulate work.
await Task.Delay(milliseconds);
// Report completion.
var msg = String.Format("Some work completed in {0} ms.\n", milliseconds);
textBox1.Text += msg;
}
下列版本的 DoSomeWork
方法會排除 WPF 應用程式中的例外狀況。
private async void DoSomeWork(int milliseconds)
{
// Simulate work.
await Task.Delay(milliseconds);
// Report completion.
bool uiAccess = textBox1.Dispatcher.CheckAccess();
String msg = String.Format("Some work completed in {0} ms. on {1}UI thread\n",
milliseconds, uiAccess ? String.Empty : "non-");
if (uiAccess)
textBox1.Text += msg;
else
textBox1.Dispatcher.Invoke(() => { textBox1.Text += msg; });
}
Windows Forms 應用程式
List<String> lines = new List<String>();
private async void threadExampleBtn_Click(object sender, EventArgs e)
{
textBox1.Text = String.Empty;
lines.Clear();
lines.Add("Simulating work on UI thread.");
textBox1.Lines = lines.ToArray();
DoSomeWork(20);
lines.Add("Simulating work on non-UI thread.");
textBox1.Lines = lines.ToArray();
await Task.Run(() => DoSomeWork(1000));
lines.Add("ThreadsExampleBtn_Click completes. ");
textBox1.Lines = lines.ToArray();
}
private async void DoSomeWork(int milliseconds)
{
// simulate work
await Task.Delay(milliseconds);
// report completion
lines.Add(String.Format("Some work completed in {0} ms on UI thread.", milliseconds));
textBox1.Lines = lines.ToArray();
}
Dim lines As New List(Of String)()
Private Async Sub threadExampleBtn_Click(sender As Object, e As EventArgs) Handles Button1.Click
TextBox1.Text = String.Empty
lines.Clear()
lines.Add("Simulating work on UI thread.")
TextBox1.Lines = lines.ToArray()
DoSomeWork(20)
lines.Add("Simulating work on non-UI thread.")
TextBox1.Lines = lines.ToArray()
Await Task.Run(Sub() DoSomeWork(1000))
lines.Add("ThreadsExampleBtn_Click completes. ")
TextBox1.Lines = lines.ToArray()
End Sub
Private Async Sub DoSomeWork(milliseconds As Integer)
' Simulate work.
Await Task.Delay(milliseconds)
' Report completion.
lines.Add(String.Format("Some work completed in {0} ms on UI thread.", milliseconds))
textBox1.Lines = lines.ToArray()
End Sub
下列版本的 DoSomeWork
方法會排除 Windows Forms 應用程式中的例外狀況。
private async void DoSomeWork(int milliseconds)
{
// simulate work
await Task.Delay(milliseconds);
// Report completion.
bool uiMarshal = textBox1.InvokeRequired;
String msg = String.Format("Some work completed in {0} ms. on {1}UI thread\n",
milliseconds, uiMarshal ? String.Empty : "non-");
lines.Add(msg);
if (uiMarshal) {
textBox1.Invoke(new Action(() => { textBox1.Lines = lines.ToArray(); }));
}
else {
textBox1.Lines = lines.ToArray();
}
}
Private Async Sub DoSomeWork(milliseconds As Integer)
' Simulate work.
Await Task.Delay(milliseconds)
' Report completion.
Dim uiMarshal As Boolean = TextBox1.InvokeRequired
Dim msg As String = String.Format("Some work completed in {0} ms. on {1}UI thread" + vbCrLf,
milliseconds, If(uiMarshal, String.Empty, "non-"))
lines.Add(msg)
If uiMarshal Then
TextBox1.Invoke(New Action(Sub() TextBox1.Lines = lines.ToArray()))
Else
TextBox1.Lines = lines.ToArray()
End If
End Sub
在反覆運算集合時變更集合
foreach
C# 中的語句、 for...in
F# 或 For Each
Visual Basic 中的 語句是用來逐一查看集合的成員,以及讀取或修改其個別元素。 不過,它無法用來新增或移除集合中的專案。 這樣做會擲回例外 InvalidOperationException 狀況,並出現類似「已修改集合」的訊息:列舉運算可能不會執行。」
下列範例會逐一遍歷整數集合並嘗試將每個整數的平方加入該集合中。 此範例在第一次呼叫 InvalidOperationException 方法時,會擲回 List<T>.Add。
using System;
using System.Collections.Generic;
public class IteratingEx1
{
public static void Main()
{
var numbers = new List<int>() { 1, 2, 3, 4, 5 };
foreach (var number in numbers)
{
int square = (int)Math.Pow(number, 2);
Console.WriteLine($"{number}^{square}");
Console.WriteLine($"Adding {square} to the collection...");
Console.WriteLine();
numbers.Add(square);
}
}
}
// The example displays the following output:
// 1^1
// Adding 1 to the collection...
//
//
// Unhandled Exception: System.InvalidOperationException: Collection was modified;
// enumeration operation may not execute.
// at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
// at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
// at Example.Main()
open System
let numbers = ResizeArray [| 1; 2; 3; 4; 5 |]
for number in numbers do
let square = Math.Pow(number, 2) |> int
printfn $"{number}^{square}"
printfn $"Adding {square} to the collection...\n"
numbers.Add square
// The example displays the following output:
// 1^1
// Adding 1 to the collection...
//
//
// Unhandled Exception: System.InvalidOperationException: Collection was modified
// enumeration operation may not execute.
// at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
// at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
// at <StartupCode$fs>.main()
Imports System.Collections.Generic
Module Example6
Public Sub Main()
Dim numbers As New List(Of Integer)({1, 2, 3, 4, 5})
For Each number In numbers
Dim square As Integer = CInt(Math.Pow(number, 2))
Console.WriteLine("{0}^{1}", number, square)
Console.WriteLine("Adding {0} to the collection..." + vbCrLf,
square)
numbers.Add(square)
Next
End Sub
End Module
' The example displays the following output:
' 1^1
' Adding 1 to the collection...
'
'
' Unhandled Exception: System.InvalidOperationException: Collection was modified;
' enumeration operation may not execute.
' at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
' at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
' at Example.Main()
您可以根據應用程式邏輯,以下列兩種方式之一排除例外狀況:
如果必須在循環集合時加入元素,可以使用
for
語句(在 F# 中則為for..to
),而不是foreach
、for...in
或For Each
來按索引進行循環。 下列範例會使用 for 語句,將集合中的數位平方加入集合中。using System; using System.Collections.Generic; public class IteratingEx2 { public static void Main() { var numbers = new List<int>() { 1, 2, 3, 4, 5 }; int upperBound = numbers.Count - 1; for (int ctr = 0; ctr <= upperBound; ctr++) { int square = (int)Math.Pow(numbers[ctr], 2); Console.WriteLine($"{numbers[ctr]}^{square}"); Console.WriteLine($"Adding {square} to the collection..."); Console.WriteLine(); numbers.Add(square); } Console.WriteLine("Elements now in the collection: "); foreach (var number in numbers) Console.Write("{0} ", number); } } // The example displays the following output: // 1^1 // Adding 1 to the collection... // // 2^4 // Adding 4 to the collection... // // 3^9 // Adding 9 to the collection... // // 4^16 // Adding 16 to the collection... // // 5^25 // Adding 25 to the collection... // // Elements now in the collection: // 1 2 3 4 5 1 4 9 16 25
open System open System.Collections.Generic let numbers = ResizeArray [| 1; 2; 3; 4; 5 |] let upperBound = numbers.Count - 1 for i = 0 to upperBound do let square = Math.Pow(numbers[i], 2) |> int printfn $"{numbers[i]}^{square}" printfn $"Adding {square} to the collection...\n" numbers.Add square printfn "Elements now in the collection: " for number in numbers do printf $"{number} " // The example displays the following output: // 1^1 // Adding 1 to the collection... // // 2^4 // Adding 4 to the collection... // // 3^9 // Adding 9 to the collection... // // 4^16 // Adding 16 to the collection... // // 5^25 // Adding 25 to the collection... // // Elements now in the collection: // 1 2 3 4 5 1 4 9 16 25
Imports System.Collections.Generic Module Example7 Public Sub Main() Dim numbers As New List(Of Integer)({1, 2, 3, 4, 5}) Dim upperBound = numbers.Count - 1 For ctr As Integer = 0 To upperBound Dim square As Integer = CInt(Math.Pow(numbers(ctr), 2)) Console.WriteLine("{0}^{1}", numbers(ctr), square) Console.WriteLine("Adding {0} to the collection..." + vbCrLf, square) numbers.Add(square) Next Console.WriteLine("Elements now in the collection: ") For Each number In numbers Console.Write("{0} ", number) Next End Sub End Module ' The example displays the following output: ' 1^1 ' Adding 1 to the collection... ' ' 2^4 ' Adding 4 to the collection... ' ' 3^9 ' Adding 9 to the collection... ' ' 4^16 ' Adding 16 to the collection... ' ' 5^25 ' Adding 25 to the collection... ' ' Elements now in the collection: ' 1 2 3 4 5 1 4 9 16 25
請注意,您必須先設定反覆次數,才能開始進行集合的反覆運算,可以透過在迴圈中使用計數器來正確結束迴圈,或從
Count
- 1 迭代到 0,或如範例所示,將陣列中的元素數量指派給一個變數,並使用它來設定迴圈的上限。 否則,如果在每次反覆運算時將元素加入至集合,就會產生無盡的循環結果。如果不需要在反覆運算專案時將元素加入至集合,您可以將專案儲存在反覆運算集合完成時所新增的暫存集合中。 下列範例會使用此方法將集合中的數位平方加入暫存集合,然後將集合合併成單一陣列物件。
using System; using System.Collections.Generic; public class IteratingEx3 { public static void Main() { var numbers = new List<int>() { 1, 2, 3, 4, 5 }; var temp = new List<int>(); // Square each number and store it in a temporary collection. foreach (var number in numbers) { int square = (int)Math.Pow(number, 2); temp.Add(square); } // Combine the numbers into a single array. int[] combined = new int[numbers.Count + temp.Count]; Array.Copy(numbers.ToArray(), 0, combined, 0, numbers.Count); Array.Copy(temp.ToArray(), 0, combined, numbers.Count, temp.Count); // Iterate the array. foreach (var value in combined) Console.Write("{0} ", value); } } // The example displays the following output: // 1 2 3 4 5 1 4 9 16 25
open System open System.Collections.Generic let numbers = ResizeArray [| 1; 2; 3; 4; 5 |] let temp = ResizeArray() // Square each number and store it in a temporary collection. for number in numbers do let square = Math.Pow(number, 2) |> int temp.Add square // Combine the numbers into a single array. let combined = Array.zeroCreate<int> (numbers.Count + temp.Count) Array.Copy(numbers.ToArray(), 0, combined, 0, numbers.Count) Array.Copy(temp.ToArray(), 0, combined, numbers.Count, temp.Count) // Iterate the array. for value in combined do printf $"{value} " // The example displays the following output: // 1 2 3 4 5 1 4 9 16 25
Imports System.Collections.Generic Module Example8 Public Sub Main() Dim numbers As New List(Of Integer)({1, 2, 3, 4, 5}) Dim temp As New List(Of Integer)() ' Square each number and store it in a temporary collection. For Each number In numbers Dim square As Integer = CInt(Math.Pow(number, 2)) temp.Add(square) Next ' Combine the numbers into a single array. Dim combined(numbers.Count + temp.Count - 1) As Integer Array.Copy(numbers.ToArray(), 0, combined, 0, numbers.Count) Array.Copy(temp.ToArray(), 0, combined, numbers.Count, temp.Count) ' Iterate the array. For Each value In combined Console.Write("{0} ", value) Next End Sub End Module ' The example displays the following output: ' 1 2 3 4 5 1 4 9 16 25
無法比較的物件之陣列或集合的排序
一般用途的排序方法,例如 Array.Sort(Array) 方法或 List<T>.Sort() 方法,通常需要至少一個要排序的對象實作 IComparable<T> 或 IComparable 介面。 如果沒有,則無法排序集合或陣列,而且方法會 InvalidOperationException 擲回例外狀況。 下列範例會定義 類別、將兩Person
個Person
物件儲存在泛型List<T>物件中,並嘗試排序它們。 如範例的輸出所示,對 List<T>.Sort() 方法的呼叫會擲回一個 InvalidOperationException 。
using System;
using System.Collections.Generic;
public class Person1
{
public Person1(string fName, string lName)
{
FirstName = fName;
LastName = lName;
}
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class ListSortEx1
{
public static void Main()
{
var people = new List<Person1>();
people.Add(new Person1("John", "Doe"));
people.Add(new Person1("Jane", "Doe"));
people.Sort();
foreach (var person in people)
Console.WriteLine($"{person.FirstName} {person.LastName}");
}
}
// The example displays the following output:
// Unhandled Exception: System.InvalidOperationException: Failed to compare two elements in the array. --->
// System.ArgumentException: At least one object must implement IComparable.
// at System.Collections.Comparer.Compare(Object a, Object b)
// at System.Collections.Generic.ArraySortHelper`1.SwapIfGreater(T[] keys, IComparer`1 comparer, Int32 a, Int32 b)
// at System.Collections.Generic.ArraySortHelper`1.DepthLimitedQuickSort(T[] keys, Int32 left, Int32 right, IComparer`1 comparer, Int32 depthLimit)
// at System.Collections.Generic.ArraySortHelper`1.Sort(T[] keys, Int32 index, Int32 length, IComparer`1 comparer)
// --- End of inner exception stack trace ---
// at System.Collections.Generic.ArraySortHelper`1.Sort(T[] keys, Int32 index, Int32 length, IComparer`1 comparer)
// at System.Array.Sort[T](T[] array, Int32 index, Int32 length, IComparer`1 comparer)
// at System.Collections.Generic.List`1.Sort(Int32 index, Int32 count, IComparer`1 comparer)
// at Example.Main()
type Person(firstName: string, lastName: string) =
member val FirstName = firstName with get, set
member val LastName = lastName with get, set
let people = ResizeArray()
people.Add(Person("John", "Doe"))
people.Add(Person("Jane", "Doe"))
people.Sort()
for person in people do
printfn $"{person.FirstName} {person.LastName}"
// The example displays the following output:
// Unhandled Exception: System.InvalidOperationException: Failed to compare two elements in the array. --->
// System.ArgumentException: At least one object must implement IComparable.
// at System.Collections.Comparer.Compare(Object a, Object b)
// at System.Collections.Generic.ArraySortHelper`1.SwapIfGreater(T[] keys, IComparer`1 comparer, Int32 a, Int32 b)
// at System.Collections.Generic.ArraySortHelper`1.DepthLimitedQuickSort(T[] keys, Int32 left, Int32 right, IComparer`1 comparer, Int32 depthLimit)
// at System.Collections.Generic.ArraySortHelper`1.Sort(T[] keys, Int32 index, Int32 length, IComparer`1 comparer)
// --- End of inner exception stack trace ---
// at System.Collections.Generic.ArraySortHelper`1.Sort(T[] keys, Int32 index, Int32 length, IComparer`1 comparer)
// at System.Array.Sort[T](T[] array, Int32 index, Int32 length, IComparer`1 comparer)
// at System.Collections.Generic.List`1.Sort(Int32 index, Int32 count, IComparer`1 comparer)
// at <StartupCode$fs>.main()
Imports System.Collections.Generic
Public Class Person9
Public Sub New(fName As String, lName As String)
FirstName = fName
LastName = lName
End Sub
Public Property FirstName As String
Public Property LastName As String
End Class
Module Example9
Public Sub Main()
Dim people As New List(Of Person9)()
people.Add(New Person9("John", "Doe"))
people.Add(New Person9("Jane", "Doe"))
people.Sort()
For Each person In people
Console.WriteLine("{0} {1}", person.FirstName, person.LastName)
Next
End Sub
End Module
' The example displays the following output:
' Unhandled Exception: System.InvalidOperationException: Failed to compare two elements in the array. --->
' System.ArgumentException: At least one object must implement IComparable.
' at System.Collections.Comparer.Compare(Object a, Object b)
' at System.Collections.Generic.ArraySortHelper`1.SwapIfGreater(T[] keys, IComparer`1 comparer, Int32 a, Int32 b)
' at System.Collections.Generic.ArraySortHelper`1.DepthLimitedQuickSort(T[] keys, Int32 left, Int32 right, IComparer`1 comparer, Int32 depthLimit)
' at System.Collections.Generic.ArraySortHelper`1.Sort(T[] keys, Int32 index, Int32 length, IComparer`1 comparer)
' --- End of inner exception stack trace ---
' at System.Collections.Generic.ArraySortHelper`1.Sort(T[] keys, Int32 index, Int32 length, IComparer`1 comparer)
' at System.Array.Sort[T](T[] array, Int32 index, Int32 length, IComparer`1 comparer)
' at System.Collections.Generic.List`1.Sort(Int32 index, Int32 count, IComparer`1 comparer)
' at Example.Main()
您可以使用下列三種方式中的任何一種方式來消除例外狀況:
如果您可以擁有您嘗試排序的類型(也就是,如果您控制其原始程式碼),您可以修改它來實 IComparable<T> 作 或 IComparable 介面。 這需要您實作任一 IComparable<T>.CompareTo 方法或 CompareTo 方法。 將介面實作新增至現有類型並非重大變更。
下列範例使用此方法為 IComparable<T> 類別提供
Person
的實作。 您仍然可以呼叫集合或陣列的一般排序方法,而且,如範例所示的輸出,集合會成功排序。using System; using System.Collections.Generic; public class Person2 : IComparable<Person> { public Person2(String fName, String lName) { FirstName = fName; LastName = lName; } public String FirstName { get; set; } public String LastName { get; set; } public int CompareTo(Person other) { return String.Format("{0} {1}", LastName, FirstName). CompareTo(String.Format("{0} {1}", other.LastName, other.FirstName)); } } public class ListSortEx2 { public static void Main() { var people = new List<Person2>(); people.Add(new Person2("John", "Doe")); people.Add(new Person2("Jane", "Doe")); people.Sort(); foreach (var person in people) Console.WriteLine($"{person.FirstName} {person.LastName}"); } } // The example displays the following output: // Jane Doe // John Doe
open System type Person(firstName: string, lastName: string) = member val FirstName = firstName with get, set member val LastName = lastName with get, set interface IComparable<Person> with member this.CompareTo(other) = compare $"{this.LastName} {this.FirstName}" $"{other.LastName} {other.FirstName}" let people = ResizeArray() people.Add(new Person("John", "Doe")) people.Add(new Person("Jane", "Doe")) people.Sort() for person in people do printfn $"{person.FirstName} {person.LastName}" // The example displays the following output: // Jane Doe // John Doe
Imports System.Collections.Generic Public Class Person : Implements IComparable(Of Person) Public Sub New(fName As String, lName As String) FirstName = fName LastName = lName End Sub Public Property FirstName As String Public Property LastName As String Public Function CompareTo(other As Person) As Integer _ Implements IComparable(Of Person).CompareTo Return String.Format("{0} {1}", LastName, FirstName). CompareTo(String.Format("{0} {1}", other.LastName, other.FirstName)) End Function End Class Module Example10 Public Sub Main() Dim people As New List(Of Person)() people.Add(New Person("John", "Doe")) people.Add(New Person("Jane", "Doe")) people.Sort() For Each person In people Console.WriteLine("{0} {1}", person.FirstName, person.LastName) Next End Sub End Module ' The example displays the following output: ' Jane Doe ' John Doe
如果您無法修改嘗試排序之類型的原始程式碼,您可以定義實作 介面的特殊用途排序類別 IComparer<T> 。 您可以呼叫具有
Sort
參數的IComparer<T>方法重載。 如果您想要開發可根據多個準則排序物件的特製化排序類別,這個方法特別有用。下列範例透過開發自定義的
PersonComparer
類別來排序Person
集合,採用了這種方法。 然後,它會將這個類別的實例傳遞至 List<T>.Sort(IComparer<T>) 方法。using System; using System.Collections.Generic; public class Person3 { public Person3(String fName, String lName) { FirstName = fName; LastName = lName; } public String FirstName { get; set; } public String LastName { get; set; } } public class PersonComparer : IComparer<Person3> { public int Compare(Person3 x, Person3 y) { return String.Format("{0} {1}", x.LastName, x.FirstName). CompareTo(String.Format("{0} {1}", y.LastName, y.FirstName)); } } public class ListSortEx3 { public static void Main() { var people = new List<Person3>(); people.Add(new Person3("John", "Doe")); people.Add(new Person3("Jane", "Doe")); people.Sort(new PersonComparer()); foreach (var person in people) Console.WriteLine($"{person.FirstName} {person.LastName}"); } } // The example displays the following output: // Jane Doe // John Doe
open System open System.Collections.Generic type Person(firstName, lastName) = member val FirstName = firstName with get, set member val LastName = lastName with get, set type PersonComparer() = interface IComparer<Person> with member _.Compare(x: Person, y: Person) = $"{x.LastName} {x.FirstName}".CompareTo $"{y.LastName} {y.FirstName}" let people = ResizeArray() people.Add(Person("John", "Doe")) people.Add(Person("Jane", "Doe")) people.Sort(PersonComparer()) for person in people do printfn $"{person.FirstName} {person.LastName}" // The example displays the following output: // Jane Doe // John Doe
Imports System.Collections.Generic Public Class Person11 Public Sub New(fName As String, lName As String) FirstName = fName LastName = lName End Sub Public Property FirstName As String Public Property LastName As String End Class Public Class PersonComparer : Implements IComparer(Of Person11) Public Function Compare(x As Person11, y As Person11) As Integer _ Implements IComparer(Of Person11).Compare Return String.Format("{0} {1}", x.LastName, x.FirstName). CompareTo(String.Format("{0} {1}", y.LastName, y.FirstName)) End Function End Class Module Example11 Public Sub Main() Dim people As New List(Of Person11)() people.Add(New Person11("John", "Doe")) people.Add(New Person11("Jane", "Doe")) people.Sort(New PersonComparer()) For Each person In people Console.WriteLine("{0} {1}", person.FirstName, person.LastName) Next End Sub End Module ' The example displays the following output: ' Jane Doe ' John Doe
如果您無法修改嘗試排序之類型的原始程式碼,您可以建立 Comparison<T> 委派來執行排序。 代表的簽名為
Function Comparison(Of T)(x As T, y As T) As Integer
int Comparison<T>(T x, T y)
下列範例透過定義符合
PersonComparison
委派簽章的 Comparison<T> 方法來運作。 然後,它會將此委派傳遞至 List<T>.Sort(Comparison<T>) 方法。using System; using System.Collections.Generic; public class Person { public Person(String fName, String lName) { FirstName = fName; LastName = lName; } public String FirstName { get; set; } public String LastName { get; set; } } public class ListSortEx4 { public static void Main() { var people = new List<Person>(); people.Add(new Person("John", "Doe")); people.Add(new Person("Jane", "Doe")); people.Sort(PersonComparison); foreach (var person in people) Console.WriteLine($"{person.FirstName} {person.LastName}"); } public static int PersonComparison(Person x, Person y) { return String.Format("{0} {1}", x.LastName, x.FirstName). CompareTo(String.Format("{0} {1}", y.LastName, y.FirstName)); } } // The example displays the following output: // Jane Doe // John Doe
open System open System.Collections.Generic type Person(firstName, lastName) = member val FirstName = firstName with get, set member val LastName = lastName with get, set let personComparison (x: Person) (y: Person) = $"{x.LastName} {x.FirstName}".CompareTo $"{y.LastName} {y.FirstName}" let people = ResizeArray() people.Add(Person("John", "Doe")) people.Add(Person("Jane", "Doe")) people.Sort personComparison for person in people do printfn $"{person.FirstName} {person.LastName}" // The example displays the following output: // Jane Doe // John Doe
Imports System.Collections.Generic Public Class Person12 Public Sub New(fName As String, lName As String) FirstName = fName LastName = lName End Sub Public Property FirstName As String Public Property LastName As String End Class Module Example12 Public Sub Main() Dim people As New List(Of Person12)() people.Add(New Person12("John", "Doe")) people.Add(New Person12("Jane", "Doe")) people.Sort(AddressOf PersonComparison) For Each person In people Console.WriteLine("{0} {1}", person.FirstName, person.LastName) Next End Sub Public Function PersonComparison(x As Person12, y As Person12) As Integer Return String.Format("{0} {1}", x.LastName, x.FirstName). CompareTo(String.Format("{0} {1}", y.LastName, y.FirstName)) End Function End Module ' The example displays the following output: ' Jane Doe ' John Doe
將 Nullable<T> 中的 null 轉換成其基礎類型
嘗試將值Nullable<T>轉換為null
的其基礎類型時,將會擲回InvalidOperationException例外狀況,並顯示錯誤訊息:「可為 Null 的對象必須有值。」
下列範例會在嘗試反覆運算包含InvalidOperationException值的陣列時擲回Nullable(Of Integer)
例外狀況。
using System;
using System.Linq;
public class NullableEx1
{
public static void Main()
{
var queryResult = new int?[] { 1, 2, null, 4 };
var map = queryResult.Select(nullableInt => (int)nullableInt);
// Display list.
foreach (var num in map)
Console.Write("{0} ", num);
Console.WriteLine();
}
}
// The example displays the following output:
// 1 2
// Unhandled Exception: System.InvalidOperationException: Nullable object must have a value.
// at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
// at Example.<Main>b__0(Nullable`1 nullableInt)
// at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()
// at Example.Main()
open System
open System.Linq
let queryResult = [| Nullable 1; Nullable 2; Nullable(); Nullable 4 |]
let map = queryResult.Select(fun nullableInt -> nullableInt.Value)
// Display list.
for num in map do
printf $"{num} "
printfn ""
// The example displays the following output:
// 1 2
// Unhandled Exception: System.InvalidOperationException: Nullable object must have a value.
// at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
// at Example.<Main>b__0(Nullable`1 nullableInt)
// at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()
// at <StartupCode$fs>.main()
Imports System.Linq
Module Example13
Public Sub Main()
Dim queryResult = New Integer?() {1, 2, Nothing, 4}
Dim map = queryResult.Select(Function(nullableInt) CInt(nullableInt))
' Display list.
For Each num In map
Console.Write("{0} ", num)
Next
Console.WriteLine()
End Sub
End Module
' The example displays thIe following output:
' 1 2
' Unhandled Exception: System.InvalidOperationException: Nullable object must have a value.
' at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
' at Example.<Main>b__0(Nullable`1 nullableInt)
' at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()
' at Example.Main()
若要防止例外狀況:
- 使用Nullable<T>.HasValue屬性僅選取不是
null
的元素。 - 呼叫 Nullable<T>.GetValueOrDefault 的其中一個重載,以提供
null
值的預設值。
下列範例會執行這兩個動作以避免 InvalidOperationException 例外狀況。
using System;
using System.Linq;
public class NullableEx2
{
public static void Main()
{
var queryResult = new int?[] { 1, 2, null, 4 };
var numbers = queryResult.Select(nullableInt => (int)nullableInt.GetValueOrDefault());
// Display list using Nullable<int>.HasValue.
foreach (var number in numbers)
Console.Write("{0} ", number);
Console.WriteLine();
numbers = queryResult.Select(nullableInt => (int) (nullableInt.HasValue ? nullableInt : -1));
// Display list using Nullable<int>.GetValueOrDefault.
foreach (var number in numbers)
Console.Write("{0} ", number);
Console.WriteLine();
}
}
// The example displays the following output:
// 1 2 0 4
// 1 2 -1 4
open System
open System.Linq
let queryResult = [| Nullable 1; Nullable 2; Nullable(); Nullable 4 |]
let numbers = queryResult.Select(fun nullableInt -> nullableInt.GetValueOrDefault())
// Display list using Nullable<int>.HasValue.
for number in numbers do
printf $"{number} "
printfn ""
let numbers2 = queryResult.Select(fun nullableInt -> if nullableInt.HasValue then nullableInt.Value else -1)
// Display list using Nullable<int>.GetValueOrDefault.
for number in numbers2 do
printf $"{number} "
printfn ""
// The example displays the following output:
// 1 2 0 4
// 1 2 -1 4
Imports System.Linq
Module Example14
Public Sub Main()
Dim queryResult = New Integer?() {1, 2, Nothing, 4}
Dim numbers = queryResult.Select(Function(nullableInt) _
CInt(nullableInt.GetValueOrDefault()))
' Display list.
For Each number In numbers
Console.Write("{0} ", number)
Next
Console.WriteLine()
' Use -1 to indicate a missing values.
numbers = queryResult.Select(Function(nullableInt) _
CInt(If(nullableInt.HasValue, nullableInt, -1)))
' Display list.
For Each number In numbers
Console.Write("{0} ", number)
Next
Console.WriteLine()
End Sub
End Module
' The example displays the following output:
' 1 2 0 4
' 1 2 -1 4
在空集合上呼叫 System.Linq.Enumerable 方法
Enumerable.Aggregate、Enumerable.Average、Enumerable.First、、Enumerable.Last、Enumerable.MaxEnumerable.Min、 Enumerable.Single和 Enumerable.SingleOrDefault 方法會在序列上執行作業,並傳回單一結果。 當序列是空的時,這些方法的某些多載會擲 InvalidOperationException 回例外狀況,而其他多載則傳回 null
。 當序列包含一個以上的元素時,方法 Enumerable.SingleOrDefault 也會擲回 InvalidOperationException 例外狀況。
備註
大部分拋出InvalidOperationException異常的的方法都是重載。 請務必瞭解您選擇的重載行為。
下表列出呼叫 InvalidOperationException 某些 System.Linq.Enumerable 方法所擲回之例外狀況物件的例外狀況訊息。
方法 | 訊息 |
---|---|
Aggregate Average Last Max Min |
序列不包含任何元素 |
First |
序列不包含相符的元素 |
Single SingleOrDefault |
序列包含多於一個的相符元素 |
您消除或處理例外狀況的方式取決於您應用程式的假設,以及您呼叫的特定方法。
當您刻意呼叫其中一個方法而不檢查空序列時,假設序列不是空的,而且空序列是非預期的發生。 在此情況下,攔截或重新擲回例外狀況是適當的。
如果您不小心漏檢查空序列,可以呼叫 Enumerable.Any 的一個多載來判斷序列是否包含任何元素。
小提示
在產生序列之前,如果需要處理的數據可能包含大量元素或產生序列的操作成本高昂,呼叫Enumerable.Any<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>)方法可以改善效能。
如果您已呼叫 、Enumerable.First、 或 Enumerable.Last之類的Enumerable.Single方法,您可以替代替代方法,例如 Enumerable.FirstOrDefault、Enumerable.LastOrDefault、 或 Enumerable.SingleOrDefault,以傳回預設值,而不是序列的成員。
這些範例會提供其他詳細數據。
下列範例使用 Enumerable.Average 方法來計算值大於 4 的序列的平均值。 由於原始數位中沒有任何值超過 4,因此序列中未包含任何值,而且 方法會 InvalidOperationException 擲回例外狀況。
using System;
using System.Linq;
public class Example
{
public static void Main()
{
int[] data = { 1, 2, 3, 4 };
var average = data.Where(num => num > 4).Average();
Console.Write("The average of numbers greater than 4 is {0}",
average);
}
}
// The example displays the following output:
// Unhandled Exception: System.InvalidOperationException: Sequence contains no elements
// at System.Linq.Enumerable.Average(IEnumerable`1 source)
// at Example.Main()
open System
open System.Linq
let data = [| 1; 2; 3; 4 |]
let average =
data.Where(fun num -> num > 4).Average();
printfn $"The average of numbers greater than 4 is {average}"
// The example displays the following output:
// Unhandled Exception: System.InvalidOperationException: Sequence contains no elements
// at System.Linq.Enumerable.Average(IEnumerable`1 source)
// at <StartupCode$fs>.main()
Imports System.Linq
Module Example
Public Sub Main()
Dim data() As Integer = { 1, 2, 3, 4 }
Dim average = data.Where(Function(num) num > 4).Average()
Console.Write("The average of numbers greater than 4 is {0}",
average)
End Sub
End Module
' The example displays the following output:
' Unhandled Exception: System.InvalidOperationException: Sequence contains no elements
' at System.Linq.Enumerable.Average(IEnumerable`1 source)
' at Example.Main()
藉由呼叫 Any 方法來判斷序列是否包含任何元素,然後再呼叫處理序列的方法,即可排除例外狀況,如下列範例所示。
using System;
using System.Linq;
public class EnumerableEx2
{
public static void Main()
{
int[] dbQueryResults = { 1, 2, 3, 4 };
var moreThan4 = dbQueryResults.Where(num => num > 4);
if (moreThan4.Any())
Console.WriteLine($"Average value of numbers greater than 4: {moreThan4.Average()}:");
else
// handle empty collection
Console.WriteLine("The dataset has no values greater than 4.");
}
}
// The example displays the following output:
// The dataset has no values greater than 4.
open System
open System.Linq
let dbQueryResults = [| 1; 2; 3; 4 |]
let moreThan4 =
dbQueryResults.Where(fun num -> num > 4)
if moreThan4.Any() then
printfn $"Average value of numbers greater than 4: {moreThan4.Average()}:"
else
// handle empty collection
printfn "The dataset has no values greater than 4."
// The example displays the following output:
// The dataset has no values greater than 4.
Imports System.Linq
Module Example1
Public Sub Main()
Dim dbQueryResults() As Integer = {1, 2, 3, 4}
Dim moreThan4 = dbQueryResults.Where(Function(num) num > 4)
If moreThan4.Any() Then
Console.WriteLine("Average value of numbers greater than 4: {0}:",
moreThan4.Average())
Else
' Handle empty collection.
Console.WriteLine("The dataset has no values greater than 4.")
End If
End Sub
End Module
' The example displays the following output:
' The dataset has no values greater than 4.
方法會 Enumerable.First 傳回序列中的第一個項目,或序列中符合指定條件的第一個項目。 如果序列是空的,因此沒有第一個元素,則會拋出 InvalidOperationException 例外狀況。
在下列範例中,方法 Enumerable.First<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>) 會擲回 InvalidOperationException 例外狀況,因為 dbQueryResults 陣列不包含大於 4 的元素。
using System;
using System.Linq;
public class EnumerableEx3
{
public static void Main()
{
int[] dbQueryResults = { 1, 2, 3, 4 };
var firstNum = dbQueryResults.First(n => n > 4);
Console.WriteLine($"The first value greater than 4 is {firstNum}");
}
}
// The example displays the following output:
// Unhandled Exception: System.InvalidOperationException:
// Sequence contains no matching element
// at System.Linq.Enumerable.First[TSource](IEnumerable`1 source, Func`2 predicate)
// at Example.Main()
open System
open System.Linq
let dbQueryResults = [| 1; 2; 3; 4 |]
let firstNum = dbQueryResults.First(fun n -> n > 4)
printfn $"The first value greater than 4 is {firstNum}"
// The example displays the following output:
// Unhandled Exception: System.InvalidOperationException:
// Sequence contains no matching element
// at System.Linq.Enumerable.First[TSource](IEnumerable`1 source, Func`2 predicate)
// at <StartupCode$fs>.main()
Imports System.Linq
Module Example2
Public Sub Main()
Dim dbQueryResults() As Integer = {1, 2, 3, 4}
Dim firstNum = dbQueryResults.First(Function(n) n > 4)
Console.WriteLine("The first value greater than 4 is {0}",
firstNum)
End Sub
End Module
' The example displays the following output:
' Unhandled Exception: System.InvalidOperationException:
' Sequence contains no matching element
' at System.Linq.Enumerable.First[TSource](IEnumerable`1 source, Func`2 predicate)
' at Example.Main()
您可以呼叫 Enumerable.FirstOrDefault 方法,而不是 Enumerable.First 傳回指定的或預設值。 如果方法在序列中找不到第一個元素,則會傳回該數據類型的預設值。 默認值是 null
用於參考型別,數值類型默認為零,DateTime.MinValue 用於 DateTime 類型。
備註
解析由 Enumerable.FirstOrDefault 方法傳回的值通常很複雜,因為型別的預設值可能是序列中的有效值。 在此情況下,您會呼叫 Enumerable.Any 方法來判斷序列是否具有有效的成員,然後再呼叫 Enumerable.First 方法。
下列範例會呼叫 Enumerable.FirstOrDefault<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>) 方法,以防止 InvalidOperationException 在上一個範例中擲回的例外狀況。
using System;
using System.Linq;
public class EnumerableEx4
{
public static void Main()
{
int[] dbQueryResults = { 1, 2, 3, 4 };
var firstNum = dbQueryResults.FirstOrDefault(n => n > 4);
if (firstNum == 0)
Console.WriteLine("No value is greater than 4.");
else
Console.WriteLine($"The first value greater than 4 is {firstNum}");
}
}
// The example displays the following output:
// No value is greater than 4.
open System
open System.Linq
let dbQueryResults = [| 1; 2; 3; 4 |]
let firstNum = dbQueryResults.FirstOrDefault(fun n -> n > 4)
if firstNum = 0 then
printfn "No value is greater than 4."
else
printfn $"The first value greater than 4 is {firstNum}"
// The example displays the following output:
// No value is greater than 4.
Imports System.Linq
Module Example3
Public Sub Main()
Dim dbQueryResults() As Integer = {1, 2, 3, 4}
Dim firstNum = dbQueryResults.FirstOrDefault(Function(n) n > 4)
If firstNum = 0 Then
Console.WriteLine("No value is greater than 4.")
Else
Console.WriteLine("The first value greater than 4 is {0}",
firstNum)
End If
End Sub
End Module
' The example displays the following output:
' No value is greater than 4.
在沒有一個元素的序列上呼叫 Enumerable.Single 或 Enumerable.SingleOrDefault
方法 Enumerable.Single 會傳回序列的唯一專案,或符合指定條件之序列的唯一專案。 如果序列中沒有任何專案,或有多個專案,則方法會 InvalidOperationException 擲回例外狀況。
您可以使用 Enumerable.SingleOrDefault 方法傳回預設值,而不是在序列不包含任何元素時擲回例外狀況。 不過,當序列包含一個以上的專案時, Enumerable.SingleOrDefault 方法仍然會擲回 InvalidOperationException 例外狀況。
以下表格列出由呼叫 InvalidOperationException 和 Enumerable.Single 方法所擲回的例外狀況物件的例外狀況訊息。
方法 | 訊息 |
---|---|
Single |
序列不包含相符的元素 |
Single SingleOrDefault |
序列包含多於一個的相符元素 |
在下列範例中,呼叫Enumerable.Single 方法會擲回InvalidOperationException 例外狀況,因為序列沒有大於 4 的元素。
using System;
using System.Linq;
public class EnumerableEx5
{
public static void Main()
{
int[] dbQueryResults = { 1, 2, 3, 4 };
var singleObject = dbQueryResults.Single(value => value > 4);
// Display results.
Console.WriteLine($"{singleObject} is the only value greater than 4");
}
}
// The example displays the following output:
// Unhandled Exception: System.InvalidOperationException:
// Sequence contains no matching element
// at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source, Func`2 predicate)
// at Example.Main()
open System
open System.Linq
let dbQueryResults = [| 1; 2; 3; 4 |]
let singleObject = dbQueryResults.Single(fun value -> value > 4)
// Display results.
printfn $"{singleObject} is the only value greater than 4"
// The example displays the following output:
// Unhandled Exception: System.InvalidOperationException:
// Sequence contains no matching element
// at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source, Func`2 predicate)
// at <StartupCode$fs>.main()
Imports System.Linq
Module Example4
Public Sub Main()
Dim dbQueryResults() As Integer = {1, 2, 3, 4}
Dim singleObject = dbQueryResults.Single(Function(value) value > 4)
' Display results.
Console.WriteLine("{0} is the only value greater than 4",
singleObject)
End Sub
End Module
' The example displays the following output:
' Unhandled Exception: System.InvalidOperationException:
' Sequence contains no matching element
' at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source, Func`2 predicate)
' at Example.Main()
下列範例嘗試透過改用呼叫 InvalidOperationException 方法,以避免在序列為空時擲回 Enumerable.SingleOrDefault 例外狀況。 不過,由於這個序列會傳回值大於 2 的多個元素,因此也會擲回 InvalidOperationException 例外狀況。
using System;
using System.Linq;
public class EnumerableEx6
{
public static void Main()
{
int[] dbQueryResults = { 1, 2, 3, 4 };
var singleObject = dbQueryResults.SingleOrDefault(value => value > 2);
if (singleObject != 0)
Console.WriteLine($"{singleObject} is the only value greater than 2");
else
// Handle an empty collection.
Console.WriteLine("No value is greater than 2");
}
}
// The example displays the following output:
// Unhandled Exception: System.InvalidOperationException:
// Sequence contains more than one matching element
// at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
// at Example.Main()
open System
open System.Linq
let dbQueryResults = [| 1; 2; 3; 4 |]
let singleObject = dbQueryResults.SingleOrDefault(fun value -> value > 2)
if singleObject <> 0 then
printfn $"{singleObject} is the only value greater than 2"
else
// Handle an empty collection.
printfn "No value is greater than 2"
// The example displays the following output:
// Unhandled Exception: System.InvalidOperationException:
// Sequence contains more than one matching element
// at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
// at <StartupCode$fs>.main()
Imports System.Linq
Module Example5
Public Sub Main()
Dim dbQueryResults() As Integer = {1, 2, 3, 4}
Dim singleObject = dbQueryResults.SingleOrDefault(Function(value) value > 2)
If singleObject <> 0 Then
Console.WriteLine("{0} is the only value greater than 2",
singleObject)
Else
' Handle an empty collection.
Console.WriteLine("No value is greater than 2")
End If
End Sub
End Module
' The example displays the following output:
' Unhandled Exception: System.InvalidOperationException:
' Sequence contains more than one matching element
' at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
' at Example.Main()
呼叫Enumerable.Single 方法 假設序列或符合指定準則的序列僅包含一個元素。 Enumerable.SingleOrDefault 假設具有零或一個結果的序列,但不能超過。 如果這個假設是您刻意做出的,且不符合這些條件,那麼重新擲回或攔截結果 InvalidOperationException 是適當的。 否則,或如果您預期無效條件會頻繁發生,您應該考慮採用其他 Enumerable 方法,例如 FirstOrDefault 或 Where。
動態跨應用程式域欄位存取
OpCodes.Ldflda如果物件包含您嘗試擷取其位址的欄位不在程式代碼執行所在的應用程式域中,則通用中繼語言 (CIL) 指令會擲回InvalidOperationException例外狀況。 欄位的位址只能從其所在的應用程式域存取。
引發 InvalidOperationException 例外狀況
只有在對象狀態基於某些原因不支援特定方法呼叫時,才應該擲回 InvalidOperationException 例外狀況。 也就是說,在某些情況下或內容中,方法呼叫有效,但在其他情況下則無效。
如果方法呼叫失敗是因為無效參數,則應該擲回 ArgumentException 或其衍生類別之一,例如 ArgumentNullException 或 ArgumentOutOfRangeException。