System.InvalidOperationException (clase)
En este artículo se proporcionan comentarios adicionales a la documentación de referencia de esta API.
InvalidOperationException se usa en los casos en los que el error al invocar un método no se debe a argumentos no válidos. Normalmente, se inicia cuando el estado de un objeto no puede admitir la llamada al método. Por ejemplo, una excepción InvalidOperationException la inician métodos como los siguientes:
- IEnumerator.MoveNext si los objetos de una colección se modifican después de crear el enumerador. Para más información, vea Cambio de una colección mientras se itera por ella.
- ResourceSet.GetString si el conjunto de recursos se cierra antes de que se realice la llamada al método.
- XContainer.Add, si el objeto o los objetos que se van a agregar darían como resultado un documento XML estructurado incorrectamente.
- Un método que intenta manipular la interfaz de usuario desde un subproceso que no es el principal ni el subproceso de la interfaz de usuario.
Importante
Como la excepción InvalidOperationException se puede iniciar en una amplia variedad de circunstancias, es importante leer el mensaje de excepción devuelto por la propiedad Message.
InvalidOperationException usa HRESULT COR_E_INVALIDOPERATION
, que tiene el valor 0x80131509.
Para obtener una lista de valores de propiedad iniciales de una instancia de InvalidOperationException, consulte el InvalidOperationException constructores.
Causas comunes de excepciones InvalidOperationException
En las secciones siguientes se muestran algunos casos comunes en los que se inicia una excepción InvalidOperationException en una aplicación. La manera de controlar el problema depende de cada situación concreta. Pero normalmente, la excepción se debe a un error del desarrollador y la excepción InvalidOperationException se puede prever y evitar.
Actualización de un subproceso de interfaz de usuario desde un subproceso que no es de interfaz de usuario
A menudo, los subprocesos de trabajo se usan para realizar algún trabajo en segundo plano que implica recopilar datos que se mostrarán en la interfaz de usuario de una aplicación. Pero La mayoría de los marcos de aplicaciones de GUI (interfaz gráfica de usuario) para .NET, como Windows Forms y Windows Presentation Foundation (WPF), le permiten acceder a objetos de la GUI solo desde el subproceso que crea y administra la interfaz de usuario (el subproceso Main o UI). Si trata de acceder a un elemento de la interfaz de usuario desde un subproceso que no es el subproceso de la interfaz de usuario, se inicia una excepción InvalidOperationException. El texto del mensaje de excepción se muestra en la tabla siguiente.
Tipo de aplicación | Mensaje |
---|---|
Aplicación WPF | El subproceso que realiza la llamada no puede acceder a este objeto porque el propietario es otro subproceso. |
Aplicación para UWP | La aplicación ha llamado a una interfaz serializada para un subproceso diferente. |
Aplicación de Windows Forms | Operación no válida entre subprocesos: Se ha accedido al control "TextBox1" desde un subproceso distinto a aquel en que se ha creado. |
Los marcos de interfaz de usuario para .NET implementan un patrón de distribuidor que incluye un método para comprobar si se ejecuta una llamada a un miembro de un elemento de interfaz de usuario en el subproceso de la interfaz de usuario, y otros métodos para programar la llamada en el subproceso de la interfaz de usuario:
- En las aplicaciones WPF, llame al método Dispatcher.CheckAccess para determinar si un método se ejecuta en un subproceso que no es el de la interfaz de usuario. Devuelve
true
si el método se ejecuta en el subproceso de la interfaz de usuario yfalse
de lo contrario. Llame a una de las sobrecargas del método Dispatcher.Invoke para programar la llamada en el subproceso de la interfaz de usuario. - En las aplicaciones para UWP, compruebe la propiedad CoreDispatcher.HasThreadAccess para determinar si un método se ejecuta en un subproceso ajeno a la interfaz de usuario. Llame al método CoreDispatcher.RunAsync para ejecutar un delegado que actualice el subproceso de la interfaz de usuario.
- En aplicaciones de Windows Forms, use la propiedad Control.InvokeRequired para determinar si un método se ejecuta en un subproceso ajeno a la interfaz de usuario. Llame a una de las sobrecargas del método Control.Invoke para ejecutar un delegado que actualice el subproceso de interfaz de usuario.
En los ejemplos siguientes se muestra la excepción InvalidOperationException que se inicia al intentar actualizar un elemento de interfaz de usuario desde un subproceso distinto del subproceso que lo ha creado. En cada ejemplo debe crear dos controles:
- Un control de cuadro de texto denominado
textBox1
. En una aplicación de Windows Forms, debe establecer su propiedad Multiline entrue
. - Un control de botón denominado
threadExampleBtn
. En el ejemplo se proporciona un controlador,ThreadsExampleBtn_Click
, para el eventoClick
del botón.
En cada caso, el controlador de eventos threadExampleBtn_Click
llama al método DoSomeWork
dos veces. La primera llamada se ejecuta de manera sincrónica y se realiza correctamente. Pero la segunda llamada, como se ejecuta de forma asincrónica en un subproceso de grupo de subprocesos, intenta actualizar la interfaz de usuario desde un subproceso que no es de interfaz de usuario. Esto da como resultado una excepción InvalidOperationException.
Aplicaciones de 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;
}
La siguiente versión del método DoSomeWork
elimina la excepción en una aplicación 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; });
}
Aplicaciones de 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
La siguiente versión del método DoSomeWork
elimina la excepción en una aplicación de 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
Cambio de una colección mientras se itera por ella
La instrucción foreach
de C#, for...in
en F#, o la instrucción For Each
de Visual Basic se usa para iterar por los miembros de una colección y leer o modificar sus elementos individuales. Pero no se puede usar para agregar o quitar elementos de la colección. Al hacerlo, se inicia una excepción InvalidOperationException con un mensaje similar al siguiente: "Se ha modificado la colección; es posible que la operación de enumeración no se ejecute."
En el ejemplo siguiente se itera por una colección de enteros que intenta agregar el cuadrado de cada entero a la colección. En el ejemplo se inicia una excepción InvalidOperationException con la primera llamada al método 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()
Puede eliminar la excepción de una de estas dos maneras, en función de la lógica de la aplicación:
Si se deben agregar elementos a la colección mientras se recorre en iteración, puede hacerlo por índice mediante la instrucción
for
(for..to
en F#) en lugar deforeach
,for...in
oFor Each
. En el ejemplo siguiente se usa la instrucción for para agregar el cuadrado de números de la colección a la colección.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
Tenga en cuenta que debe establecer el número de iteraciones antes de iterar por la colección mediante un contador dentro del bucle que salga adecuadamente del bucle, iterando hacia atrás, de
Count
- 1 a 0 o, como en el ejemplo, mediante la asignación del número de elementos de la matriz a una variable y su uso para establecer el límite superior del bucle. De lo contrario, si se agrega un elemento a la colección en cada iteración, se genera un bucle infinito.Si no es necesario agregar elementos a la colección mientras se itera por ella; puede almacenar los elementos que se van a agregar en una colección temporal y agregarla al iterar por la colección. En el ejemplo siguiente se usa este enfoque para agregar el cuadrado de números de una colección a una colección temporal y, después, para combinar las colecciones en un único objeto de matriz.
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
Ordenación de una matriz o colección cuyos objetos no se pueden comparar
Los métodos de ordenación de uso general, como los métodos Array.Sort(Array) o List<T>.Sort(), suelen necesitar que al menos uno de los objetos que se ordenan implemente la interfaz IComparable<T> o IComparable. Si no es así, la colección o matriz no se puede ordenar y el método inicia una excepción InvalidOperationException. En el ejemplo siguiente se define una clase Person
, se almacenan dos objetos Person
en un objeto List<T> genérico y se intenta ordenarlos. Como se muestra en la salida del ejemplo, la llamada al método List<T>.Sort() inicia una excepción 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()
Puede eliminar la excepción de tres maneras:
Si puede poseer el tipo que intenta ordenar (es decir, si controla su código fuente) puede modificarlo para implementar la interfaz IComparable<T> o IComparable. Para esto debe implementar el método IComparable<T>.CompareTo o CompareTo. Agregar una implementación de interfaz a un tipo existente no es un cambio importante.
En el ejemplo siguiente se usa este enfoque a fin de proporcionar una implementación IComparable<T> para la clase
Person
. Todavía puede llamar al método de ordenación general de la colección o matriz y, como se muestra en la salida del ejemplo, la colección se ordena correctamente.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
Si no puede modificar el código fuente del tipo que intenta ordenar, puede definir una clase de ordenación de propósito especial que implemente la interfaz IComparer<T>. Puede llamar a una sobrecarga del método
Sort
que incluye un parámetro IComparer<T>. Este enfoque es especialmente útil si quiere desarrollar una clase de ordenación especializada que pueda ordenar objetos en función de varios criterios.En el ejemplo siguiente se usa el enfoque mediante el desarrollo de una clase
PersonComparer
personalizada que se usa para ordenar coleccionesPerson
. Después, pasa una instancia de esta clase al método 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
Si no puede modificar el código fuente del tipo que intenta ordenar, puede crear un delegado Comparison<T> para realizar la ordenación. La firma del delegado es
Function Comparison(Of T)(x As T, y As T) As Integer
int Comparison<T>(T x, T y)
En el ejemplo siguiente se usa el enfoque mediante la definición de un método
PersonComparison
que coincide con la firma del delegado Comparison<T>. Después, pasa este delegado al método 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
Conversión de <T> que admite un valor NULL y que es NULL a su tipo subyacente
Al intentar convertir un valor Nullable<T> que es null
a su tipo subyacente, se inicia una excepción InvalidOperationException y se muestra el mensaje de error "El objeto que admite un valor NULL debe tener un valor.
En el ejemplo siguiente se inicia una excepción InvalidOperationException cuando intenta iterar por una matriz que incluye un valor 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()
Para evitar la excepción:
- Use la propiedad Nullable<T>.HasValue para seleccionar solamente los elementos que no son
null
. - Llame a una de las sobrecargas de Nullable<T>.GetValueOrDefault a fin de proporcionar un valor predeterminado para un valor
null
.
En el ejemplo siguiente se usan las dos opciones para evitar la excepción 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
Llamada a un método System.Linq.Enumerable en una colección vacía
Los métodos Enumerable.Aggregate, Enumerable.Average, Enumerable.First, Enumerable.Last, Enumerable.Max, Enumerable.Min, Enumerable.Single y Enumerable.SingleOrDefault realizan operaciones en una secuencia y devuelven un único resultado. Algunas sobrecargas de estos métodos inician una excepción InvalidOperationException cuando la secuencia está vacía, mientras que otras sobrecargas devuelven null
. El método Enumerable.SingleOrDefault también inicia una excepción InvalidOperationException cuando la secuencia contiene más de un elemento.
Nota:
La mayoría de los métodos que inician una excepción InvalidOperationException son sobrecargas. Asegúrese de que comprende el comportamiento de la sobrecarga que elija.
En la tabla siguiente se enumeran los mensajes de excepción de los objetos de excepción InvalidOperationException iniciados por llamadas a algunos métodos System.Linq.Enumerable.
Método | Mensaje |
---|---|
Aggregate Average Last Max Min |
La secuencia no contiene elementos |
First |
La secuencia no contiene ningún elemento coincidente |
Single SingleOrDefault |
La secuencia contiene más de un elemento coincidente |
La forma de eliminar o controlar la excepción depende de las suposiciones de la aplicación y del método concreto al que se llame.
Cuando llama deliberadamente a uno de estos métodos sin comprobar si hay una secuencia vacía, asume de forma implícita que la secuencia no está vacía y que una secuencia vacía es un suceso inesperado. En este caso, detectar o volver a iniciar la excepción es adecuado.
Si no ha podido comprobar si una secuencia está vacía, puede llamar a una de las sobrecargas de la sobrecarga Enumerable.Any para determinar si una secuencia contiene elementos.
Sugerencia
Llamar al método Enumerable.Any<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>) antes de generar una secuencia puede mejorar el rendimiento si los datos que se van a procesar pueden contener un gran número de elementos o si la operación que genera la secuencia es costosa.
Si ha llamado a un método como Enumerable.First, Enumerable.Lasto Enumerable.Single, puede sustituir un método alternativo, como Enumerable.FirstOrDefault, Enumerable.LastOrDefaulto Enumerable.SingleOrDefault, que devuelve un valor predeterminado en lugar de un miembro de la secuencia.
En los ejemplos se proporcionan detalles adicionales.
En el ejemplo siguiente se usa el método Enumerable.Average para calcular el promedio de una secuencia cuyos valores son mayores que 4. Como ningún valor de la matriz original es superior a 4, no se incluye ningún valor en la secuencia y el método inicia una excepción 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()
La excepción se puede eliminar si se llama al método Any para determinar si la secuencia contiene elementos antes de llamar al método que procesa la secuencia, como se muestra en el ejemplo siguiente.
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.
El método Enumerable.First devuelve el primer elemento de una secuencia o el primer elemento de una secuencia que cumple una condición especificada. Si la secuencia está vacía y, por tanto, no tiene un primer elemento, inicia una excepción InvalidOperationException.
En el ejemplo siguiente, el método Enumerable.First<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>) inicia una excepción InvalidOperationException porque la matriz dbQueryResults no contiene un elemento mayor que 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()
Puede llamar al método Enumerable.FirstOrDefault en lugar de Enumerable.First para devolver un valor predeterminado o especificado. Si el método no encuentra un primer elemento en la secuencia, devuelve el valor predeterminado para ese tipo de datos. El valor predeterminado es null
para un tipo de referencia, cero para un tipo de datos numérico y DateTime.MinValue para el tipo DateTime.
Nota:
La interpretación del valor devuelto por el método Enumerable.FirstOrDefault suele ser complicada por el hecho de que el valor predeterminado del tipo puede ser un valor válido en la secuencia. En este caso, puede llamar al método Enumerable.Any para determinar si la secuencia tiene miembros válidos antes de llamar al método Enumerable.First.
En el ejemplo siguiente se llama al método Enumerable.FirstOrDefault<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>) para evitar la excepción InvalidOperationException iniciada en el ejemplo anterior.
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.
Llamada a Enumerable.Single o Enumerable.SingleOrDefault en una secuencia sin un elemento
El método Enumerable.Single devuelven el único elemento de una secuencia, o bien el único elemento de una secuencia que cumple una condición especificada. Si no hay elementos en la secuencia o si hay más de un elemento , el método inicia una excepción InvalidOperationException.
Puede usar el método Enumerable.SingleOrDefault para devolver un valor especificado o predeterminado en lugar de iniciar una excepción cuando la secuencia no contiene elementos. Pero el método Enumerable.SingleOrDefault también todavía inicia una excepción InvalidOperationException cuando la secuencia contiene más de un elemento.
En la tabla siguiente se enumeran los mensajes de excepción de los objetos de excepción InvalidOperationException iniciados por llamadas a los métodos Enumerable.Single y Enumerable.SingleOrDefault.
Método | Mensaje |
---|---|
Single |
La secuencia no contiene ningún elemento coincidente |
Single SingleOrDefault |
La secuencia contiene más de un elemento coincidente |
En el ejemplo siguiente, la llamada al método Enumerable.Single inicia una excepción InvalidOperationException porque la secuencia no tiene un elemento mayor que 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()
En el ejemplo siguiente se intenta evitar la excepción InvalidOperationException iniciada cuando una secuencia está vacía mediante una llamada al método Enumerable.SingleOrDefault. Pero como esta secuencia devuelve varios elementos cuyo valor es mayor que 2, también inicia una excepción 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()
Al llamar al método Enumerable.Single se supone que una secuencia o la secuencia que cumple los criterios especificados contiene solo un elemento. Enumerable.SingleOrDefault asume una secuencia con cero o un resultado, pero no más. Si esta suposición es deliberada por su parte y no se cumplen estas condiciones, volver a iniciar o capturar la excepción InvalidOperationException resultante es adecuado. De lo contario, o si espera que se produzcan condiciones no válidas con cierta frecuencia, debe plantearse usar otro método Enumerable, como FirstOrDefault o Where.
Acceso dinámico a campos de dominio entre aplicaciones
La OpCodes.Ldflda instrucción de lenguaje intermedio común (CIL) produce una InvalidOperationException excepción si el objeto que contiene el campo cuya dirección está intentando recuperar no está dentro del dominio de aplicación en el que se ejecuta el código. Solo se puede acceder a la dirección de un campo desde el dominio de aplicación en el que reside.
Inicio de una excepción InvalidOperationException
Solo debe iniciar una excepción InvalidOperationException cuando el estado del objeto por algún motivo no admite una llamada de método determinada. Es decir, la llamada al método es válida en algunas circunstancias o contextos, pero no en otras.
Si la invocación del método no se produce correctamente debido a argumentos no válidos, se debe iniciar ArgumentException, o bien una de sus clases derivadas, ArgumentNullException o ArgumentOutOfRangeException.