System.InvalidOperationException 類別
本文提供此 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 線程)。 InvalidOperationException當您嘗試從 UI 線程以外的線程存取 UI 元素時,會擲回 。 下表顯示例外狀況訊息的文字。
應用程式類型 | 訊息 |
---|---|
WPF 應用程式 | 呼叫線程無法存取這個對象,因為不同的線程擁有它。 |
UWP 應用程式 | 應用程式稱為已針對不同線程封送處理之介面。 |
Windows Forms 應用程式 | 跨線程作業無效:控制從建立線程以外的線程存取的 『TextBox1』。 |
適用於 .NET 的 UI 架構會實 作發送器 模式,其中包含方法,以檢查 UI 線程上是否正在執行 UI 元素成員的呼叫,以及排程 UI 線程上呼叫的其他方法:
- 在 WPF 應用程式中,呼叫 Dispatcher.CheckAccess 方法來判斷方法是否在非 UI 線程上執行。 如果方法是在 UI 線程上執行,則
false
傳true
回 ,否則會傳回 。 呼叫 方法的 Dispatcher.Invoke 其中一個多載,以在UI線程上排程呼叫。 - 在UWP應用程式中,檢查 CoreDispatcher.HasThreadAccess 屬性,以判斷方法是否在非UI線程上執行。 CoreDispatcher.RunAsync呼叫 方法來執行更新UI線程的委派。
- 在 Windows Forms 應用程式中,使用 Control.InvokeRequired 屬性來判斷方法是否在非 UI 線程上執行。 呼叫 方法的 Control.Invoke 其中一個多載,以執行更新UI線程的委派。
下列範例說明 InvalidOperationException 當您嘗試從建立它之線程以外的線程更新UI元素時所擲回的例外狀況。 每個範例都需要您建立兩個控制項:
- 名為
textBox1
的文字框控制件。 在 Windows Forms 應用程式中,您應該將其 Multiline 屬性設定為true
。 - 名為
threadExampleBtn
的按鈕控制件。 這個範例會提供按鈕Click
事件的處理程式ThreadsExampleBtn_Click
。
在每個案例中 threadExampleBtn_Click
,事件處理程式會呼叫 DoSomeWork
方法兩次。 第一個呼叫會同步執行並成功。 但第二次呼叫,因為它以異步方式在線程集區線程上執行,所以會嘗試從非UI線程更新UI。 這會導致 InvalidOperationException 例外狀況。
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("{0}^{1}", number, square);
Console.WriteLine("Adding {0} to the collection...\n", square);
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..to
在 F#) 語句中,而不是foreach
、for...in
或For Each
來逐一for
查看它。 下列範例會使用 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("{0}^{1}", numbers[ctr], square); Console.WriteLine("Adding {0} to the collection...\n", square); 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("{0} {1}", 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("{0} {1}", 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> 參數。 如果您想要開發可根據多個準則排序物件的特製化排序類別,這個方法特別有用。下列範例會藉由開發用來排序
Person
集合的自定義PersonComparer
類別,來使用 方法。 然後,它會將這個類別的實例傳遞至 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("{0} {1}", 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("{0} {1}", 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
將可為 Null< 的 T> 轉換成其基礎類型
嘗試將值null
轉換成Nullable<T>其基礎類型時,會InvalidOperationException擲回例外狀況並顯示錯誤訊息:「可為 Null 的對象必須有值。
下列範例會在嘗試反覆運算包含Nullable(Of Integer)
值的陣列時擲回InvalidOperationException例外狀況。
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.MinEnumerable.Max、 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.Last、 或 Enumerable.Single之類的Enumerable.First方法,您可以替代替代方法,例如 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: {0}:",
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 {0}",
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 {0}",
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 例外狀況。
下表列出呼叫 和 Enumerable.SingleOrDefault 方法所Enumerable.Single擲回之例外狀況物件的例外狀況訊息InvalidOperationException。
方法 | 訊息 |
---|---|
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("{0} is the only value greater than 4", singleObject);
}
}
// 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()
下列範例會藉由呼叫 Enumerable.SingleOrDefault 方法,嘗試避免InvalidOperationException在序列是空的時擲回例外狀況。 不過,由於這個序列會傳回值大於 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("{0} is the only value greater than 2",
singleObject);
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 應該改為擲回其其中一個衍生類別或 ArgumentNullExceptionArgumentOutOfRangeException。