Partager via


Classe System.InvalidOperationException

Cet article vous offre des remarques complémentaires à la documentation de référence pour cette API.

InvalidOperationException est utilisé dans les cas où l’échec d’appel d’une méthode est dû à des raisons autres que des arguments non valides. En règle générale, il est levée lorsque l’état d’un objet ne peut pas prendre en charge l’appel de méthode. Par exemple, une exception InvalidOperationException est levée par des méthodes telles que :

  • IEnumerator.MoveNext si des objets d’une collection sont modifiés après la création de l’énumérateur. Pour plus d’informations, consultez Modification d’une collection lors de son itération.
  • ResourceSet.GetString si le jeu de ressources est fermé avant l’appel de méthode.
  • XContainer.Add, si l’objet ou les objets à ajouter entraînent un document XML mal structuré.
  • Une méthode qui tente de manipuler l’interface utilisateur à partir d’un thread qui n’étant pas le thread principal ou l’interface utilisateur.

Important

Étant donné que l’exception InvalidOperationException peut être levée dans une grande variété de circonstances, il est important de lire le message d’exception retourné par la propriété Message.

InvalidOperationException utilise HRESULT COR_E_INVALIDOPERATION, qui a la valeur 0x80131509.

Pour obtenir la liste des valeurs initiales des propriétés d’une instance de InvalidOperationException, consultez le InvalidOperationException constructeurs.

Causes courantes des exceptions InvalidOperationException

Les sections suivantes montrent quelques cas courants dans lesquels l’exception InvalidOperationException est levée dans une application. La façon dont vous gérez le problème dépend de la situation en question. Toutefois, la plupart du temps, l’exception résulte de l’erreur du développeur, et l’exception InvalidOperationException peut être anticipée et évitée.

Mise à jour d’un thread d’interface utilisateur à partir d’un thread autre que l’interface utilisateur

Souvent, les threads de travail sont utilisés pour effectuer un travail en arrière-plan qui implique la collecte de données à afficher dans l’interface utilisateur d’une application. Toutefois, La plupart infrastructures d'application GUI (interface graphique utilisateur) pour .NET, par exemple Windows Forms et Windows Presentation Foundation (WPF), vous permettent d'accéder aux objets d'interface graphique utilisateur uniquement à partir du thread qui crée et gère l'interface utilisateur (le thread principal ou le thread d'interface utilisateur ). Une exception InvalidOperationException est levée lorsque vous essayez d’accéder à un élément d’interface utilisateur à partir d’un thread autre que le thread d’interface utilisateur. Le texte du message d’exception s’affiche dans le tableau suivant.

Type d’application Message
Application WPF Le thread appelant ne peut pas accéder à cet objet parce qu'un autre thread en est propriétaire.
Application UWP L’application a appelé une interface qui a été marshalée pour un autre thread.
Application Windows Forms Opération inter-threads non valide : le contrôle 'zone de texte1' a fait l'objet d'un accès à partir d'un thread autre que celui sur lequel il a été créé.

Les infrastructures d'interface utilisateur pour .NET implémentent un modèle de répartiteur qui inclut une méthode permettant de vérifier si l'appel d'un membre d'élément d'interface utilisateur est en cours d'exécution sur le thread d'interface utilisateur, ainsi que d'autres méthodes permettant de planifier l'appel sur le thread d'interface utilisateur :

  • Dans les applications WPF, appelez la méthode Dispatcher.CheckAccess pour déterminer si une méthode s’exécute sur un thread autre que l’interface utilisateur. Elle retourne true si la méthode est en cours d’exécution sur le thread d’interface utilisateur et false dans le cas contraire. Appelez l’une des surcharges de la méthode Dispatcher.Invoke pour planifier l’appel sur le thread d’interface utilisateur.
  • Dans les applications UWP, vérifiez la propriété CoreDispatcher.HasThreadAccess pour déterminer si une méthode s’exécute sur un thread autre que l’interface utilisateur. Appelez la méthode CoreDispatcher.RunAsync pour exécuter un délégué qui met à jour le thread d’interface utilisateur.
  • Dans les applications Windows Forms, utilisez la propriété Control.InvokeRequired pour déterminer si une méthode s’exécute sur un thread autre que l’interface utilisateur. Appelez l’une des surcharges de la méthode Control.Invoke pour exécuter un délégué qui met à jour le thread d’interface utilisateur.

Les exemples suivants illustrent l’exception InvalidOperationException levée lorsque vous tentez de mettre à jour un élément d’interface utilisateur à partir d’un thread autre que le thread qui l’a créé. Chaque exemple nécessite que vous créez deux contrôles :

  • Contrôle de zone de texte nommé textBox1. Dans une application Windows Forms, vous devez définir sa propriété Multiline sur true.
  • Un contrôle bouton nommé threadExampleBtn. L’exemple fournit un gestionnaire, ThreadsExampleBtn_Click, pour l’événement Click du bouton.

Dans chaque cas, le gestionnaire d’événements threadExampleBtn_Click appelle la méthode DoSomeWork deux fois. Le premier appel s’exécute de manière synchrone et réussit. Mais le deuxième appel, car il s’exécute de manière asynchrone sur un thread de pool de threads, tente de mettre à jour l’interface utilisateur à partir d’un thread autre que l’interface utilisateur. Cela entraîne une exception InvalidOperationException.

Application 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 version suivante de la méthode DoSomeWork élimine l’exception dans une application 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; });
}

Applications 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 version suivante de la méthode DoSomeWork élimine l’exception dans une application 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

Modification d’une collection lors de son itération

L’instruction foreach en C#, for...in en F#ou For Each en Visual Basic est utilisée pour itérer les membres d’une collection et lire ou modifier ses éléments individuels. Toutefois, il ne peut pas être utilisé pour ajouter ou supprimer des éléments de la collection. Cette opération lève une exception InvalidOperationException avec un message similaire à « La collection a été modifié, l’opération d’énumération peut ne pas s’exécuter. »

L’exemple suivant itère une collection d’entiers tente d’ajouter le carré de chaque entier à la collection. L’exemple lève un appel InvalidOperationException avec le premier appel à la méthode 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()

Vous pouvez éliminer l’exception de l’une des deux manières, en fonction de votre logique d’application :

  • Si des éléments doivent être ajoutés à la collection lors de l’itération, vous pouvez l’itérer à l’aide de l’instruction for (for..to en F#) au lieu de foreach, for...in ou For Each. L’exemple suivant utilise l’instruction pour ajouter le carré de nombres de la collection à la collection.

    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
    

    Notez que vous devez établir le nombre d’itérations avant d’itérer la collection en utilisant un compteur à l’intérieur de la boucle qui la quittera de manière appropriée, en itérant vers l’arrière, de Count - 1 à 0, ou, comme l’exemple le fait, en affectant le nombre d’éléments du tableau à une variable et en l’utilisant pour établir la limite supérieure de la boucle. Sinon, si un élément est ajouté à la collection sur chaque itération, une boucle infinie s’affiche.

  • S’il n’est pas nécessaire d’ajouter des éléments à la collection lors de son itération, il est possible de stocker les éléments à ajouter dans une collection temporaire que vous ajoutez lors de l’itération de la collection. L’exemple suivant utilise cette approche pour ajouter le carré de nombres d’une collection à une collection temporaire, puis pour combiner les collections dans un objet de tableau unique.

    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
    

Trier un tableau ou d’une collection dont les objets ne peuvent pas être comparés

Les méthodes de tri à usage général, telles que la méthode Array.Sort(Array) ou la méthode List<T>.Sort(), nécessitent généralement qu’au moins un des objets à trier implémente l’interface IComparable<T> ou l’interface IComparable . Si ce n’est pas le cas, la collection ou le tableau ne peut pas être trié et la méthode lève une exception InvalidOperationException. L’exemple suivant définit une classe Person, stocke deux objets Person dans un objet générique List<T> et tente de les trier. Comme le montre la sortie de l’exemple, l’appel à la méthode List<T>.Sort() lève un 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()

Vous pouvez éliminer l’exception de trois façons :

  • Si vous êtes en mesure de posséder le type que vous essayez de trier (autrement dit, si vous contrôlez son code source), vous pouvez le modifier pour implémenter l’interface IComparable<T> ou l’interface IComparable. Cela nécessite que vous implémentez la méthode IComparable<T>.CompareTo ou CompareTo. Ajouter une implémentation d’interface à un type existant n’est pas un changement cassant.

    L’exemple suivant utilise cette approche pour fournir une implémentation IComparable<T> pour la classe Person. Vous pouvez toujours appeler la méthode de tri générale de la collection ou du tableau et, comme le montre la sortie de l’exemple, la collection trie correctement.

    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 vous ne pouvez pas modifier le code source du type que vous essayez de trier, vous pouvez définir une classe de tri à usage spécial qui implémente l’interface IComparer<T>. Vous pouvez appeler une surcharge de la méthode Sort qui inclut un paramètre IComparer<T>. Cette approche est particulièrement utile si vous souhaitez développer une classe de tri spécialisée qui peut trier des objets en fonction de plusieurs critères.

    L’exemple suivant utilise l’approche en développant une classe PersonComparer personnalisée utilisée pour trier des collections Person. Il transmet ensuite une instance de cette classe à la méthode 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 vous n’êtes pas en mesure de modifier le code source du type que vous essayez de trier, vous pouvez créer un délégué Comparison<T> pour effectuer le tri. La signature du délégué est

    Function Comparison(Of T)(x As T, y As T) As Integer
    
    int Comparison<T>(T x, T y)
    

    L’exemple suivant utilise l’approche en définissant une méthode PersonComparison qui correspond à la signature de délégué Comparison<T>. Il transmet ensuite ce délégué à la méthode 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
    

Conversion d’un <T> de valeur null sur son type sous-jacent

La tentative de conversion d’une valeur Nullable<T> qui est null correspondant à son type sous-jacent lève une exception InvalidOperationException et affiche le message d’erreur « L’objet de valeur null doit avoir une valeur.

L’exemple suivant lève une exception InvalidOperationException lorsqu’elle tente d’itérer un tableau qui inclut une valeur 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()

Pour empêcher l’exception :

L’exemple suivant permet d’éviter l’exception 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

Appelle une méthode System.Linq.Enumerable sur une collection vide

Les méthodes Enumerable.Aggregate, Enumerable.Average, Enumerable.First, Enumerable.Last, Enumerable.Max, Enumerable.Min, Enumerable.Single et Enumerable.SingleOrDefault effectuent des opérations sur une séquence et retournent un résultat unique. Certaines surcharges de ces méthodes lèvent une exception InvalidOperationException lorsque la séquence est vide, tandis que d’autres surcharges retournent null. La méthode Enumerable.SingleOrDefault lève également une exception InvalidOperationException lorsque la séquence contient plusieurs éléments.

Remarque

La plupart des méthodes qui lèvent une exception InvalidOperationException sont des surcharges. Vous devez bien comprendre le comportement de la surcharge que vous choisissez.

Le tableau suivant répertorie les messages d’exception des objets d’exception InvalidOperationException levées par des appels à certaines méthodes System.Linq.Enumerable.

Méthode Message
Aggregate
Average
Last
Max
Min
La séquence ne contient aucun élément
First La séquence ne contient aucun élément correspondant
Single
SingleOrDefault
La séquence contient plusieurs éléments correspondants

La façon dont vous éliminez ou gérez l’exception dépend des hypothèses de votre application et de la méthode spécifique que vous appelez.

  • Lorsque vous appelez volontairement ces méthodes sans rechercher la présence d'une séquence vide, vous supposez que la séquence n'est pas vide et qu'une séquence vide est un événement inattendu. Dans ce cas, l’intercepter ou lever à nouveau l’exception est une solution appropriée.

  • Si votre échec de vérification d’une séquence vide a été accidentelle, vous pouvez appeler l’une des surcharges de la surcharge Enumerable.Any pour déterminer si une séquence contient des éléments.

    Conseil

    L’appel de la méthode Enumerable.Any<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>) avant de générer une séquence peut améliorer les performances si les données à traiter peuvent contenir un grand nombre d’éléments ou si l’opération qui génère la séquence est coûteuse.

  • Si vous avez appelé une méthode telle que Enumerable.First, Enumerable.Last ou Enumerable.Single, vous pouvez remplacer une autre méthode, telle que Enumerable.FirstOrDefault, Enumerable.LastOrDefaultou Enumerable.SingleOrDefault, qui retourne une valeur par défaut au lieu d’un membre de la séquence.

Les exemples fournissent des détails supplémentaires.

L’exemple suivant utilise la méthode Enumerable.Average pour calculer la moyenne d’une séquence dont les valeurs sont supérieures à 4. Étant donné qu’aucune valeur du tableau d’origine ne dépasse 4, aucune valeur n’est incluse dans la séquence et la méthode lève une exception 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()

L’exception peut être éliminée en appelant la méthode Any pour déterminer si la séquence contient des éléments avant d’appeler la méthode qui traite la séquence, comme l’illustre l’exemple suivant.

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.

La méthode Enumerable.First retourne le premier élément d’une séquence ou le premier élément d’une séquence qui satisfait à une condition spécifiée. Si la séquence est vide et n’a donc pas de premier élément, elle lève une exception InvalidOperationException.

Dans l’exemple suivant, la méthode Enumerable.First<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>) lève une exception InvalidOperationException, car le tableau dbQueryResults ne contient pas d’élément supérieur à 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()

Vous pouvez appeler la méthode Enumerable.FirstOrDefault au lieu de retourner Enumerable.First une valeur spécifiée ou par défaut. Si la méthode ne trouve pas de premier élément dans la séquence, elle retourne la valeur par défaut pour ce type de données. La valeur par défaut est null pour un type référence, zéro pour un type de données numérique et DateTime.MinValue pour le type DateTime.

Remarque

L’interprétation de la valeur retournée par la méthode Enumerable.FirstOrDefault est souvent compliquée par le fait que la valeur par défaut du type peut être une valeur valide dans la séquence. Dans ce cas, vous appelez la méthode Enumerable.Any pour déterminer si la séquence a des membres valides avant d’appeler la méthode Enumerable.First.

L’exemple suivant appelle la méthode Enumerable.FirstOrDefault<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>) pour empêcher l’exception InvalidOperationException levée dans l’exemple précédent.

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.

Appeler Enumerable.Single ou Enumerable.SingleOrDefault sur une séquence sans un seul élément

La méthode Enumerable.Single retourne le seul élément d’une séquence, ou le seul élément d’une séquence qui répond à une condition spécifiée. S’il n’existe aucun élément dans la séquence ou s’il existe plusieurs éléments, la méthode lève une exception InvalidOperationException.

Vous pouvez utiliser la méthode Enumerable.SingleOrDefault pour retourner une valeur par défaut au lieu de lever une exception lorsque la séquence ne contient aucun élément. Toutefois, la méthode Enumerable.SingleOrDefault lève toujours une exception InvalidOperationException lorsque la séquence contient plusieurs éléments.

Le tableau suivant répertorie les messages d’exception des objets d’exception InvalidOperationException levés par des appels aux méthodes Enumerable.Single et Enumerable.SingleOrDefault.

Méthode Message
Single La séquence ne contient aucun élément correspondant
Single
SingleOrDefault
La séquence contient plusieurs éléments correspondants

Dans l’exemple suivant, l’appel à la méthode Enumerable.Single lève une exception InvalidOperationException, car la séquence n’a pas d’élément supérieur à 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()

L’exemple suivant tente d’empêcher l’exception InvalidOperationException levée lorsqu’une séquence est vide en appelant plutôt la méthode Enumerable.SingleOrDefault. Toutefois, étant donné que cette séquence retourne plusieurs éléments dont la valeur est supérieure à 2, elle lève également une exception 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()

L’appel de la méthode Enumerable.Single suppose qu’une séquence ou la séquence qui répond aux critères spécifiés ne contient qu’un seul élément. Enumerable.SingleOrDefault suppose une séquence avec zéro ou un résultat, mais pas plus. Si cette hypothèse est délibérée de votre part et que ces conditions ne sont pas remplies, la croissance ou la capture des résultats InvalidOperationException est appropriée. Sinon, ou si vous vous attendez à ce que des conditions non valides se produisent avec une certaine fréquence, vous devez envisager d’utiliser une autre méthode Enumerable, telle que FirstOrDefault ou Where.

Accès dynamique aux champs de domaine inter-applications

L’instruction OpCodes.Ldflda CIL (Common Intermediate Language) lève une InvalidOperationException exception si l’objet contenant le champ dont l’adresse que vous essayez de récupérer n’est pas dans le domaine d’application dans lequel votre code s’exécute. L’adresse d’un champ est accessible uniquement à partir du domaine d’application dans lequel il réside.

Lever une exception InvalidOperationException

Vous devez lever une exception InvalidOperationException uniquement lorsque l’état de votre objet pour une raison quelconque ne prend pas en charge un appel de méthode particulier. Autrement dit, l’appel de méthode est valide dans certaines circonstances ou contextes, mais n’est pas valide dans d’autres.

Si l’échec d’appel de méthode est dû à des arguments non valides, ArgumentException ou l’une de ses classes dérivées, ArgumentNullException ou ArgumentOutOfRangeException, doit être levée à la place.