public ref class InvalidOperationException : Exception
public ref class InvalidOperationException : SystemException
public class InvalidOperationException : Exception
public class InvalidOperationException : SystemException
[System.Serializable]
public class InvalidOperationException : SystemException
[System.Serializable]
[System.Runtime.InteropServices.ComVisible(true)]
public class InvalidOperationException : SystemException
type InvalidOperationException = class
    inherit Exception
type InvalidOperationException = class
    inherit SystemException
[<System.Serializable>]
type InvalidOperationException = class
    inherit SystemException
[<System.Serializable>]
[<System.Runtime.InteropServices.ComVisible(true)>]
type InvalidOperationException = class
    inherit SystemException
Public Class InvalidOperationException
Inherits Exception
Public Class InvalidOperationException
Inherits SystemException
Object
InvalidOperationException

InvalidOperationException 在调用方法失败是由于参数无效以外的原因导致的情况下使用 。 通常,当对象的状态不支持方法调用时,会引发它。 例如,异常 InvalidOperationException 由以下方法引发:

  • IEnumerator.MoveNext 如果在创建枚举器后修改集合的对象。 有关详细信息,请参阅 在循环访问集合时更改集合

  • ResourceSet.GetString 如果在进行方法调用之前关闭资源集,则为 。

  • XContainer.Add ,如果要添加的一个或多个对象将导致结构不正确的 XML 文档。

  • 尝试从不是main或 UI 线程的线程操作 UI 的方法。

    InvalidOperationException 由于异常可以在各种情况下引发,因此读取 属性返回 Message 的异常消息非常重要。

    本部分内容:

    InvalidOperationException 异常的一些常见原因
    从非 UI 线程更新 UI 线程
    在循环访问集合时更改集合
    对无法比较其对象的数组或集合进行排序
    将可为<Null 的 T> 强制转换为其基础类型
    在空集合上调用 System.Linq.Enumerable 方法
    在没有一个元素的序列上调用 Enumerable.Single 或 Enumerable.SingleOrDefault
    动态跨应用程序域字段访问
    引发 InvalidOperationException 异常

    InvalidOperationException 异常的一些常见原因

    以下部分介绍了应用中引发异常的一些常见情况 InvalidOperationException 。 如何处理问题取决于具体情况。 但是,最常见的是,异常是由于开发人员错误导致, InvalidOperationException 可以预见和避免异常。

    从非 UI 线程更新 UI 线程

    通常,工作线程用于执行一些后台工作,涉及收集要显示在应用程序用户界面中的数据。 但是, 大多数 GUI (图形用户界面) 适用于 .NET 的应用程序框架(例如 Windows 窗体 和 Windows Presentation Foundation (WPF) )都允许你仅从主线程或 UI 线程) 创建和管理 UI (的线程访问 GUI 对象。 InvalidOperationException 尝试从 UI 线程以外的线程访问 UI 元素时,会引发 。 异常消息的文本显示在下表中。

    应用程序类型 Message

    适用于 .NET 的 UI 框架实现调度 程序 模式,该模式包括一个方法,用于检查 UI 线程上是否正在执行对 UI 元素成员的调用,以及其他方法以在 UI 线程上计划调用:

  • 在 WPF 应用中,调用 Dispatcher.CheckAccess 方法以确定方法是否在非 UI 线程上运行。 如果方法在 UI 线程上运行,则返回 true false 否则返回 。 调用 方法的一个重载 Dispatcher.Invoke ,以在 UI 线程上计划调用。

  • 在 UWP 应用中,检查 CoreDispatcher.HasThreadAccess 属性以确定方法是否在非 UI 线程上运行。 CoreDispatcher.RunAsync 调用 方法以执行更新 UI 线程的委托。

  • 在Windows 窗体应用中,使用 Control.InvokeRequired 属性确定方法是否在非 UI 线程上运行。 调用 方法的重载之 Control.Invoke 一,以执行更新 UI 线程的委托。

    以下示例演示了 InvalidOperationException 尝试从创建 UI 元素的线程以外的线程更新 UI 元素时引发的异常。 每个示例都要求创建两个控件:

  • 名为 的 textBox1 文本框控件。 在Windows 窗体应用中,应将其 Multiline 属性设置为 true

  • 名为 的 threadExampleBtn 按钮控件。 该示例为按钮 Click 的事件提供处理程序 ThreadsExampleBtn_Click

    在每种情况下, threadExampleBtn_Click 事件处理程序都会调用 DoSomeWork 方法两次。 第一次调用同步运行并成功。 但第二次调用(因为它在线程池线程上异步运行),因此会尝试从非 UI 线程更新 UI。 这会导致 InvalidOperationException 异常。

    WPF 和 UWP 应用

    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; Private Async Sub threadExampleBtn_Click(sender As Object, e As RoutedEventArgs) Handles threadExampleBtn.Click textBox1.Text = String.Empty textBox1.Text = "Simulating work on UI thread." + vbCrLf DoSomeWork(20) textBox1.Text += "Work completed..." + vbCrLf textBox1.Text += "Simulating work on non-UI thread." + vbCrLf Await Task.Factory.StartNew(Sub() DoSomeWork(1000) End Sub) textBox1.Text += "Work completed..." + vbCrLf End Sub Private Async Sub DoSomeWork(milliseconds As Integer) ' Simulate work. Await Task.Delay(milliseconds) ' Report completion. Dim msg = String.Format("Some work completed in {0} ms.", milliseconds) + vbCrLf textBox1.Text += msg End Sub

    方法的 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; textBox1.Dispatcher.Invoke(() => { textBox1.Text += msg; }); Private Async Sub DoSomeWork(milliseconds As Integer) ' Simulate work. Await Task.Delay(milliseconds) ' Report completion. Dim uiAccess As Boolean = textBox1.Dispatcher.CheckAccess() Dim msg As String = String.Format("Some work completed in {0} ms. on {1}UI thread", milliseconds, If(uiAccess, String.Empty, "non-")) + vbCrLf If uiAccess Then textBox1.Text += msg textBox1.Dispatcher.Invoke( Sub() textBox1.Text += msg) End If End Sub

    方法的 DoSomeWork 以下版本消除了 UWP 应用中的异常。

    private async void DoSomeWork(int milliseconds) // Simulate work. await Task.Delay(milliseconds); // Report completion. bool uiAccess = textBox1.Dispatcher.HasThreadAccess; 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; await textBox1.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { textBox1.Text += msg; }); Private Async Sub DoSomeWork(milliseconds As Integer) ' Simulate work. Await Task.Delay(milliseconds) ' Report completion. Dim uiAccess As Boolean = textBox1.Dispatcher.HasThreadAccess Dim msg As String = String.Format("Some work completed in {0} ms. on {1}UI thread" + vbCrLf, milliseconds, If(uiAccess, String.Empty, "non-")) If (uiAccess) Then textBox1.Text += msg Await textBox1.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, Sub() textBox1.Text += msg) End If End Sub

    Windows 窗体应用

    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 threadExampleBtn.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 窗体应用中的异常。

    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())) 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 Example 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 Example 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) 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 Example 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 Example 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) Console.WriteLine("Elements now in the collection: ") For Each number In numbers Console.Write("{0} ", number) 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

    请注意,在循环访问集合之前,必须通过使用循环内适当退出循环的计数器、从 - 1 向后 Count 迭代到 0 的计数器,或者像示例一样,将数组中的元素数分配给变量并使用它建立循环的上限,来建立集合的迭代次数。 否则,如果在每次迭代时将元素添加到集合中,则会产生无限循环。

  • 如果在循环访问集合时不需要向集合添加元素,可以将要添加的元素存储在循环访问集合完成时添加的临时集合中。 以下示例使用此方法将集合中的数字平方添加到临时集合,然后将集合合并为单个数组对象。

    using System; using System.Collections.Generic; public class Example 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 Example 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) ' 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) 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 Person public Person(String fName, String lName) FirstName = fName; LastName = lName; public String FirstName { get; set; } public String LastName { get; set; } public class Example public static void Main() var people = new List<Person>(); people.Add(new Person("John", "Doe")); people.Add(new Person("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 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 End Class Module Example 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) 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 Person : IComparable<Person> public Person(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 Example public static void Main() var people = new List<Person>(); people.Add(new Person("John", "Doe")); people.Add(new Person("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 Example 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) End Sub End Module ' The example displays the following output: ' Jane Doe ' John Doe
  • 如果无法修改尝试排序的类型源代码,则可以定义实现 IComparer<T> 接口的特殊用途排序类。 可以调用包含 IComparer<T> 参数的方法的 Sort 重载。 如果要开发可基于多个条件对对象进行排序的专用排序类,此方法特别有用。

    以下示例通过开发用于对集合进行排序 Person 的自定义 PersonComparer 类来使用 方法。 然后,它将此类的实例传递给 List<T>.Sort(IComparer<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 PersonComparer : IComparer<Person> public int Compare(Person x, Person y) return String.Format("{0} {1}", x.LastName, x.FirstName). CompareTo(String.Format("{0} {1}", y.LastName, y.FirstName)); public class Example public static void Main() var people = new List<Person>(); people.Add(new Person("John", "Doe")); people.Add(new Person("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 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 End Class Public Class PersonComparer : Implements IComparer(Of Person) Public Function Compare(x As Person, y As Person) As Integer _ Implements IComparer(Of Person).Compare Return String.Format("{0} {1}", x.LastName, x.FirstName). CompareTo(String.Format("{0} {1}", y.LastName, y.FirstName)) End Function End Class Module Example Public Sub Main() Dim people As New List(Of Person)() people.Add(New Person("John", "Doe")) people.Add(New Person("Jane", "Doe")) people.Sort(New PersonComparer()) For Each person In people Console.WriteLine("{0} {1}", person.FirstName, person.LastName) 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 Example 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 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 End Class Module Example Public Sub Main() Dim people As New List(Of Person)() people.Add(New Person("John", "Doe")) people.Add(New Person("Jane", "Doe")) people.Sort(AddressOf PersonComparison) For Each person In people Console.WriteLine("{0} {1}", person.FirstName, person.LastName) End Sub Public Function PersonComparison(x As Person, y As Person) 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 Example 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 Example 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) 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 Example 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 Example 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) 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) Console.WriteLine() End Sub End Module ' The example displays the following output: ' 1 2 0 4 ' 1 2 -1 4

    在空集合上调用 System.Linq.Enumerable 方法

    Enumerable.AggregateEnumerable.Average、、Enumerable.FirstEnumerable.LastEnumerable.MaxEnumerable.MinEnumerable.SingleEnumerable.SingleOrDefault 方法对序列执行操作并返回单个结果。 当序列为空时,这些方法的某些重载会引发 InvalidOperationException 异常,而其他重载则返回 null。 当 Enumerable.SingleOrDefault 序列包含多个元素时,方法还会引发 InvalidOperationException 异常。

    引发 InvalidOperationException 异常的大多数方法都是重载。 请确保了解所选重载的行为。

    下表列出了调用某些System.Linq.Enumerable方法引发的InvalidOperationException异常对象中的异常消息。

    Message

    消除或处理异常的方式取决于应用程序的假设和调用的特定方法。

  • 在未检查空序列的情况下故意调用其中一种方法时,假设序列不为空,并且空序列是意外发生的。 在这种情况下,捕获或重新引发异常是合适的 。

  • 如果未能为空序列检查是无意的,则可以调用重载Enumerable.Any之一来确定序列是否包含任何元素。

    Enumerable.Any<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>)如果要处理的数据可能包含大量元素,或者生成序列的操作成本高昂,那么在生成序列之前调用 方法可以提高性能。

  • 如果已调用 、 或 Enumerable.SingleEnumerable.LastEnumerable.First方法,则可以替换返回默认值的备用方法,例如 Enumerable.FirstOrDefaultEnumerable.LastOrDefaultEnumerable.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 Example 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()); // 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()}:" // 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 Example 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()) ' 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 Example 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 Example 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 Example 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."); 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." 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 Example 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.") 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.SingleInvalidOperationException异常对象中的异常消息。

    Message

    在下面的示例中,对 Enumerable.Single 方法的调用会引发异常 InvalidOperationException ,因为序列没有大于 4 的元素。

    using System; using System.Linq; public class Example 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 Example 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 Example 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); // 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" // 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 Example 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) ' 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 方法,例如 FirstOrDefaultWhere

    动态跨应用程序域字段访问

    OpCodes.Ldflda如果包含您尝试检索其地址的字段的对象不在正在执行代码的应用程序域中,则 Microsoft 中间语言 (MSIL) 指令将引发InvalidOperationException异常。 字段的地址只能从它所在的应用程序域进行访问。

    引发 InvalidOperationException 异常

    仅当对象的状态出于某种原因不支持特定方法调用时,才应引发 InvalidOperationException 异常。 也就是说,方法调用在某些情况下或上下文中有效,但在其他情况下无效。

    如果方法调用失败是由于参数无效引起的,则应 ArgumentException 改为引发或其派生类 ArgumentNullException 之一或 ArgumentOutOfRangeException

    InvalidOperationException 使用 HRESULT COR_E_INVALIDOPERATION,其值0x80131509。

    有关实例的初始属性值的列表InvalidOperationException,请参阅InvalidOperationException构造函数。

  •