Compartilhar via


Exceções de solução de problemas: System.InvalidOperationException

Uma InvalidOperationException é acionada quando um método de um objeto é chamado quando o estado do objeto não oferece suporte à chamada do método. A exceção também é lançada quando um método tenta manipular a IU de um thread que não é um thread principal ou da IU.

Importante

Porque InvalidOperationExceptions pode ser lançada em uma ampla variedade de circunstâncias, é importante ler e entender o Message que é exibida no Assistente de exceção.

Neste artigo

Um método em execução em um segmento sem interface de usuário atualiza a interface do usuário

uma instrução em um bloco de foreach (For Each no Visual Basic) altera a coleção estiver iterando

um Nullable < T > que é nula é convertida em T

System.Linq.Enumerable um método é chamado em uma coleção vazia

artigos relacionados

Os exemplos de código neste artigo mostram como algumas exceções InvalidOperationException comuns podem ocorrer no aplicativo. Como lidar com os problemas depende da situação específica. Se a exceção é fatal para a funcionalidade do seu aplicativo, você talvez queira usar um try... catch (Try... Capturar no Visual Basic) construção para capturar a exceção e limpar o estado do aplicativo antes de sair. Mas outras InvalidOperationExceptions podem ser previstas e evitadas. Os exemplos de método revisados mostram algumas dessas técnicas.

Um método em execução em um thread de IU não atualiza a IU

Causando uma InvalidOperationException com uma atualização de IU de um thread não IU | Evitando InvalidOperationExceptions em threads não UI

A maioria das estruturas de aplicativo .NET GUI (Interface Gráfica do Usuário), como Windows Forms e Windows Presentation Foundation (WPF) permite que você acesse objetos de GUI somente a partir do thread que cria e gerencia a IU (o thread Principal ou IU). Uma InvalidOperationException é lançada quando você tenta acessar um elemento de IU de um thread que não seja o thread da IU.

Causando uma InvalidOperationException com uma atualização de IU de um thread não IU

Dica

Os exemplos seguintes usam a Padrão assíncrono baseado em tarefa (TAP) criar threads sem interface de usuário.No entanto, os exemplos também são relevantes para todos os .NET Padrões de programação assíncrona.

Nesses exemplos, o manipulador do evento ThreadsExampleBtn_Click chama o método DoSomeWork duas vezes. A primeira chamada para o método (DoSomeWork(20); é executada simultaneamente e realizada com êxito. Mas a segunda chamada (Task.Run( () => { DoSomeWork(1000);});) é executada em um thread no pool de threads. Como essa chamada tenta atualizar a IU de um thread não IU, a instrução gera uma InvalidOperationException

Aplicativos WPF e Store

Dica

Em aplicativos do Store Phone, uma Exception é lançada em vez da InvalidOperationException mais específica.

Mensagens de exceção:

Aplicativos WPF

Informações adicionais: o thread de chamada não pode acessar esse objeto porque possui um thread diferente.

Aplicativos da Store

Informações adicionais: o aplicativo chamou uma interface que foi tratada por um thread diferente. (Exceção de HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))

private async void ThreadsExampleBtn_Click(object sender, RoutedEventArgs e)
{
    TextBox1.Text = String.Empty;

    TextBox1.Text = "Simulating work on UI thread.\n";
    DoSomeWork(20);

    TextBox1.Text += "Simulating work on non-UI thread.\n";
    await Task.Run(() => DoSomeWork(1000));

    TextBox1.Text += "ThreadsExampleBtn_Click completes. ";
}

private void DoSomeWork(int msOfWork)
{
    // simulate work
    var endTime = DateTime.Now.AddMilliseconds(msOfWork);
    while (DateTime.Now < endTime)
    {
        // spin
    };

    // report completion
    var msg = String.Format("Some work completed in {0} ms on UI thread. \n", msOfWork);
    TextBox1.Text += msg;
}

Aplicativos do Windows Forms

Mensagem de exceção:

  • Informações adicionais: operação entre threads inválida: controle 'TextBox1' acessado de um thread diferente do thread que foi criado.
private async void ThreadsExampleBtn_Click(object sender, EventArgs e)
{
    TextBox1.Text = String.Empty;

    var tbLinesList = new List<string>() {"Simulating work on UI thread."};
    TextBox1.Lines = tbLinesList.ToArray();
    DoSomeWork(20, tbLinesList);

    tbLinesList.Add("Simulating work on non-UI thread.");
    TextBox1.Lines = tbLinesList.ToArray();
    await Task.Run(() => DoSomeWork(1000, tbLinesList));

    tbLinesList.Add("ThreadsExampleBtn_Click completes.");
    TextBox1.Lines = tbLinesList.ToArray();
}
private void DoSomeWork(int msOfWork, List<string> tbLinesList)
{
    // simulate work
    var endTime = DateTime.Now.AddMilliseconds(msOfWork);
    while (DateTime.Now < endTime) { };
    {
        // spin
    };

    // report completion
    var msg = String.Format("Some work completed in {0} ms on UI thread. \n", msOfWork);
    tbLinesList.Add(msg);
    TextBox1.Lines = tbLinesList.ToArray();
}

Voltar ao início Neste artigo Nesta seção nesta seção

Evitando InvalidOperationExceptions em threads não UI

Estruturas de IU do Windows implementam um padrão dispatcher que inclui um método para verificar se uma chamada para um membro de um elemento de IU está sendo executada no thread da IU e outros métodos para agendar a chamada no thread da IU.

Aplicativos WPF

Em aplicativos WPF, use um dos métodos Dispatcher.Invoke para executar um delegado no thread da IU. Se necessário, use o método Dispatcher.CheckAccess para determinar se um método está sendo executado em um thread não IU.

private void DoSomeWork(int msOfWork)
{
    var endTime = DateTime.Now.AddMilliseconds(msOfWork);
    while (DateTime.Now < endTime)
    {
        // spin
    };

    // report completion
    var msgFormat = "Some work completed in {0} ms on {1}UI thread.\n";
    var msg = String.Empty;
    if (TextBox1.Dispatcher.CheckAccess())
    {
        msg = String.Format(msgFormat, msOfWork, String.Empty);
        TextBox1.Text += msg;
    }
    else
    {
        msg = String.Format(msgFormat, msOfWork, "non-");
        Action act = ()=> {TextBox1.Text += msg;};
        TextBox1.Dispatcher.Invoke(act);
    }
}

Aplicativos do Windows Forms

Em aplicativos do Windows Form, use o método Control.Invoke para executar um delegado que atualiza o thread da IU. Se necessário, use a propriedade Control.InvokeRequired para determinar se um método está sendo executado em um thread não IU.

private void DoSomeWork(int msOfWork, List<string> tbLinesList)
{
    // simulate work
    var endTime = DateTime.Now.AddMilliseconds(msOfWork);
    while (DateTime.Now < endTime)
    {
        // spin
    };

    // report completion
    var msgFormat = "Some work completed in {0} ms on {1}UI thread.\n";
    var msg = String.Empty;
    if (TextBox1.InvokeRequired)
    {
        msg = String.Format(msgFormat, msOfWork, "non-");
        tbLinesList.Add(msg);
        Action act = () => TextBox1.Lines = tbLinesList.ToArray();
        TextBox1.Invoke( act );
    }
    else
    {
        msg = String.Format(msgFormat, msOfWork, String.Empty);
        tbLinesList.Add(msg);
        TextBox1.Lines = tbLinesList.ToArray();
    }
}

Aplicativos da Store

Em aplicativos do Store, use o método CoreDispatcher.RunAsync para executar um delegado que atualiza o thread da IU. Se necessário, use a propriedade HasThreadAccess para determinar se um método está sendo executado em um thread não IU.

private void DoSomeWork(int msOfWork)
{
    // simulate work
    var endTime = DateTime.Now.AddMilliseconds(msOfWork);
    while (DateTime.Now < endTime)
    {
        // spin
    };

    // report completion
    var msgFormat = "Some work completed in {0} ms on {1}UI thread.\n";
    var msg = String.Empty;

    if (TextBox1.Dispatcher.HasThreadAccess)
    {
        msg = String.Format(msgFormat, msOfWork, String.Empty);
        TextBox1.Text += msg;
    }
    else
    {
        msg = String.Format(msgFormat, msOfWork, "non-");
        TextBox1.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,  
            ()=> {TextBox1.Text += msg;});
    }
}

Voltar ao início Neste artigo Nesta seção nesta seção

Uma instrução em um bloco foreach (For Each no Visual Basic) altera a coleção que está iterando

Causando uma InvalidOperationException com foreach | Evitando InvalidOperationExceptions em loops

Uma instrução foreach (For Each em Visual Basic) repete um grupo de comandos internos para cada elemento em uma matriz ou coleção que implementa a interface IEnumerable ou IEnumerable. A instrução foreach é usada para iterar a coleção e ler e modificar os elementos, mas não pode ser usada para adicionar ou remover itens da coleção. Uma InvalidOperationException é lançada se você adicionar ou remove itens da coleção de origem em uma instrução foreach.

Causando uma InvalidOperationException com foreach

Neste exemplo, uma InvalidOperationException é acionada quando a instrução numList.Add(5); tenta modificar lista no bloco foreach.

Mensagem de exceção:

  • Informações adicionais: a coleção foi modificada; não é possível executar a operação de enumeração.
private void AddElementsToAList()
{
    var numList = new List<int>() { 1, 2, 3, 4 };

    foreach (var num in numList)
    {
        if (num == 2)
        {
            numList.Add(5);
        }
    }

    // Display list elements
    foreach (var num in numList)
    {
        Console.Write("{0} ", num);
    }
}

Voltar ao início Neste artigo Nesta seção nesta seção

Evitando InvalidOperationExceptions em loops

Importante

A adição ou remoção de elementos de uma lista enquanto você está iterando a coleção pode efeitos colaterais imprevistos.Se possível, mova a operação para fora de interação.

private void AddElementsToAList ()
{
    var numList = new List<int>() { 1, 2, 3, 4 };

    var numberOf2s = 0;

    foreach (var num in numList)
    {
        if (num == 2)
        {
            numberOf2s++;
        }
    }

    for (var i = 0; i < numberOf2s; i++ ) 
    { 
        numList.Add(5); 
    }

    // Display list elements
    foreach (var num in numList)
    {
        Console.Write("{0} ", num);
    }
}

Se sua situação exigir que você adicione ou remova elementos de uma lista enquanto realiza uma iteração na coleção, use um loop for (For no Visual Basic):

private void AddElementsToAList ()
{
    var numList = new List<int>() { 1, 2, 3, 4 };

    for (var i = 0; i < numList.Count; i++) 
    {
        if (numList[i] == 2)
        {
            numList.Add(5);
        }
    }

    // Display list elements
    foreach (var num in numList)
    {
        Console.Write("{0} ", num);
    }
}

Voltar ao início Neste artigo Nesta seção nesta seção

Uma propriedade Nullable<T> anulável é convertida em T

Causando uma InvalidOperationException com uma conversão inválida | Evitando InvalidOperationException de uma conversão inválida

Se você converter um Nullable que é de estrutura null (Nothing no Visual Basic) para seu tipo subjacente, um InvalidOperationException exceção é lançada.

Causando uma InvalidOperationException com uma conversão inválida

Neste método, uma InvalidOperationException é gerada quando o método Select projeta um elemento nulo de dbQueryResults em um inteiro.

Mensagem de exceção:

  • Informações adicionais: o objeto anulável deve ter um valor.
private void MapQueryResults()
{
    var dbQueryResults = new int?[] { 1, 2, null, 4 };

    var ormMap = dbQueryResults.Select(nullableInt => (int)nullableInt);

    //display map list
    foreach (var num in ormMap)
    {
        Console.Write("{0} ", num);
    }
    Console.WriteLine();
}

Voltar ao início Neste artigo Nesta seção nesta seção

Evitando InvalidOperationException de uma conversão inválida

Para evitar InvalidOperationException:

Exemplo de Nullable<T>.HasValue

private void MapQueryResults()
{
    var dbQueryResults = new int?[] { 1, 2, null, 4 };

    var ormMap = dbQueryResults
        .Where(nulllableInt => nulllableInt.HasValue)
        .Select(num => (int)num);

    //display map list
    foreach (var num in ormMap)
    {
        Console.Write("{0} ", num);
    }
    Console.WriteLine();

    // handle nulls
    Console.WriteLine("{0} nulls encountered in dbQueryResults",
        dbQueryResults.Where(nullableInt => !nullableInt.HasValue).Count());
}

Exemplo de Nullable<T>.GetValueOrDefault

Neste exemplo, usamos GetValueOrDefault(UTP) para especificar um padrão que está fora dos valores esperados retornados por dbQueryResults.

private void MapQueryResults()
{
    var dbQueryResults = new int?[] { 1, 2, null, 4 };
    var nullFlag = int.MinValue;

    var ormMap = dbQueryResults.Select(nullableInt => nullableInt.GetValueOrDefault(nullFlag));

    // handle nulls
    Console.WriteLine("'{0}' indicates a null database value.", nullFlag);

    foreach (var num in ormMap)
    {
        Console.Write("{0} ", num);
    }
    Console.WriteLine();
}

Voltar ao início Neste artigo Nesta seção nesta seção

Um método System.Linq.Enumerable é chamado em uma coleção vazia

O Enumerable métodos Aggregate, Average, Last, Max, Min, First, Single, e SingleOrDefault executar operações em uma sequência e retornar um único resultado.

Algumas sobrecargas desses métodos geram uma exceção InvalidOperationException quando a sequência está vazia (outras sobrecargas retornam null (Nothing em Visual Basic). SingleOrDefault também gera InvalidOperationException quando a sequência contém mais de um elemento.

Dica

A maioria dos métodos Enumerable discutidos nesta seção estão sobrecarregados.Certifique-se de entender o comportamento da sobrecarga que você escolher.

Mensagens de exceção:

Métodos Aggregate, Average, Last, Max e Min | Métodos First e FirstOrDefault | Métodos Single e SingleOrDefault

Métodos Aggregate, Average, Last, Max e Min

  • Informações adicionais: a sequência não contém elementos

Causando uma InvalidOperationException com Average

Neste exemplo, o seguinte método lança um InvalidOperationException quando ele chama o Average método porque a expressão Linq retorna uma seqüência que não contém elementos que são maiores do que 4.

private void FindAverageOfNumbersGreaterThan4()
{
    var dbQueryResults = new[] { 1, 2, 3, 4 };

    var avg = dbQueryResults.Where(num => num > 4).Average();

    Console.WriteLine("The average value numbers greater than 4 in the returned records is {0}", avg);
}

Quando você usa um desses métodos sem verificar se há uma sequência vazia, está implicitamente supondo que a sequência não está vazia e que uma sequência vazia é uma ocorrência inesperada. Capturar ou gerar a exceção é apropriado quando você supões que a sequência não estará vazia.

Evitando InvalidOperationException causado por uma sequência vazia

Use um do Enumerable.Any métodos para verificar se a seqüência for vazia.

Dica

Usando Any pode melhorar o desempenho da verificação se a sequência para calcular a média pode conter um grande número de elementos ou se a operação que gera a seqüência é cara.

private void FindAverageOfNumbersGreaterThan4()
{
    var dbQueryResults = new[] { 1, 2, 3, 4 };

    var moreThan4 = dbQueryResults.Where(num => num > 4);

    if(moreThan4.Any())
    {
        var msgFormat = "The average value numbers greater than 4 in the returned records is {0}";
        Console.WriteLine(msgFormat, moreThan4.Average());
    }
    else
    {
        // handle empty collection 
        Console.WriteLine("There are no values greater than 4 in the ecords.");
    }
}

Voltar ao início Neste artigo Nesta seção nesta seção

Métodos First e FirstOrDefault

First retorna o primeiro item em uma sequência ou gera uma InvalidOperationException se a sequência estiver vazia. Você pode chamar o método FirstOrDefault em vez de First para retornar um valor especificado ou padrão em vez de gerar a exceção.

Dica

No .NET Framework, os tipos têm um conceito de valores padrão.Por exemplo, para qualquer tipo de referência o padrão é nulo, e para um tipo de inteiro é zero.Consulte Tabela de valores padrão (Referência de C#)

Causando uma InvalidOperationException com First

O First é um método de otimização que retorna o primeiro elemento em uma sequência que satisfaz a uma condição especificada. Se um elemento que satisfaça à condição não for encontrado, os métodos gerarão uma exceção InvalidOperationException.

Neste exemplo, o método First gera a exceção porque dbQueryResults não contém um elemento maior que 4.

Mensagem de exceção:

  • Informações adicionais: a sequência não contém elementos correspondentes
private void FindANumbersGreaterThan4()
{
    var dbQueryResults = new[] { 1, 2, 3, 4 };

    var firstNum = dbQueryResults.First(n=> n > 4);

    var msgFormat = "{0} is an element of dbQueryResults that is greater than 4";
    Console.WriteLine(msgFormat, firstNum);

}

Evitando uma exceção com FirstOrDefault

Você pode usar um dos métodos FirstOrDefault no lugar de First para verificar se a sequência firstNum contém pelo menos um elemento. Se a consulta não encontrar um elemento que satisfaz a condição, retornará um valor especificado ou padrão. Você pode verificar o valor retornado para determinar se os elementos forem encontrados.

Dica

Usando FirstOrDefault pode ser mais complicado implementar se o valor padrão do tipo é um elemento válido na sequência.

private void FindANumbersGreaterThan4()
{
    var dbQueryResults = new[] { 1, 2, 3, 4 };

    // default(object) == null
    var firstNum = dbQueryResults.FirstOrDefault(n => n > 4);

    if (firstNum != 0)
    {
        var msgFormat = "{0} is an element of dbQueryResults that is greater than 4";
        Console.WriteLine(msgFormat, firstNum);
    }
    else
    {
        // handle default case
        Console.WriteLine("No element of dbQueryResults is greater than 4.");
    }
}

Voltar ao início Neste artigo Nesta seção nesta seção

Métodos Single e SingleOrDefault

O Enumerable.Single métodos retornam o único elemento de uma seqüência ou o único elemento de uma sequência que atende a um teste especificado.

Se não existirem elementos na sequência ou se houver mais de um elemento na sequência, o método gerará uma exceção InvalidOperationException.

Você pode usar SingleOrDefault para retornar um valor especificado ou padrão em vez de gerar a exceção quando a sequência não contiver elementos. No entanto, SingleOrDefault ainda gera uma InvalidOperationException quando a sequência contém mais de um elemento que corresponde ao predicado da seleção.

Dica

No .NET Framework, os tipos têm um conceito de valores padrão.Por exemplo, para qualquer tipo de referência o padrão é nulo, e para um tipo de inteiro é zero.Consulte Tabela de valores padrão (Referência de C#)

Causando InvalidOperationExceptions Single

Neste exemplo, singleObject gera uma InvalidOperationException porque dbQueryResults não contém um elemento maior que 4.

Mensagem de exceção:

  • Informações adicionais: a sequência não contém elementos correspondentes
private void FindTheOnlyNumberGreaterThan4()
{
    var dbQueryResults = new[] { (object)1, (object)2, (object)3, (object)4 };

    var singleObject = dbQueryResults.Single(obj => (int)obj > 4);

    // display results
    var msgFormat = "{0} is the only element of dbQueryResults that is greater than 4";
    Console.WriteLine(msgFormat, singleObject);
}

Causando InvalidOperationExceptions com Single ou SingleOrDefault

Neste exemplo, singleObject gera um InvalidOperationException porque dbQueryResults contém mais de um elemento maior que 2.

Mensagem de exceção:

  • Informações adicionais: a sequência contém mais de um elemento correspondente
private void FindTheOnlyNumberGreaterThan2()
{
    var dbQueryResults = new[] { (object)1, (object)2, (object)3, (object)4 };

    var singleObject = dbQueryResults.SingleOrDefault(obj => (int)obj > 2);

    if (singleObject != null)
    {
        var msgFormat = "{0} is the only element of dbQueryResults that is greater than 2";
        Console.WriteLine(msgFormat, singleObject);
    }
    else
    {
        // handle empty collection
        Console.WriteLine("No element of dbQueryResults is greater than 2.");
    }
}

Evitando InvalidOperationExceptions com Single

O uso de Single e SingleOrDefault também serve como documentação de suas intenções. Single implica fortemente que você espera um e apenas um resultado da condição. SingleOrDefault declara que você espera um ou zero resultados, mas não mais. Quando essas condições são inválidas, gerar ou capturar InvalidOperationException é apropriado. No entanto, se você espera que condições inválidas ocorram com alguma frequência, considere usar outros métodos Enumerable, como First ou Where para gerar os resultados.

Durante o desenvolvimento, você pode usar um dos métodos Assert para verificar as suposições. Neste exemplo, o código realçado faz com que o depurador interrompa e exiba uma caixa de diálogo de declaração durante o desenvolvimento. A declaração é removida no código da versão e qualquer Single será gerada, se os resultados forem inválidos.

Dica

O uso de Take``1 e a configuração de seu parâmetro count para 2 limita a sequência retornada a no máximo dois elementos.Essa sequência inclui todas as ocorrências que você precisa verificar (0, 1 e mais de 1 elemento) e pode melhorar o desempenho da verificação quando a sequência contiver um grande número de elementos ou se a operação que gera a sequência for cara.

private void FindTheOnlyNumberGreaterThan4()
{
    var dbQueryResults = new[] { (object)1, (object)2, (object)3, (object)4 };
    var moreThan4 = dbQueryResults.Where(obj => (int)obj > 4).Take(2);

    System.Diagnostics.Debug.Assert(moreThan4.Count() == 1, 
        String.Format("moreThan4.Count() == 1; Actual count: {0}", moreThan4.Count()));

    // do not handle exceptions in release code
    Console.WriteLine("{0} is the only element of dbQueryResults that is greater than 4", 
        moreThan4.Single());
}

Se você quiser evitar a exceção, mas ainda manipular estados inválidos no código da versão, pode modificar a técnica descrita acima. Neste exemplo, o método responde com o número de elementos retornados por moreThan2 no a instrução switch.

private void FindTheOnlyNumberGreaterThan2()
{
    var dbQueryResults = new[] { (object)1, (object)2, (object)3, (object)4 };

    var moreThan2 = dbQueryResults.TakeWhile(obj => (int)obj > 2).Take(2);

    switch(moreThan2.Count())
    { 
        case 1:
            // success
            var msgFormat = "{0} is the only element of dbQueryResults that is greater than 2";
            Console.WriteLine(msgFormat, moreThan2.Single());
            break;
        case 0:
            // handle empty collection
            Console.WriteLine("No element of the dbQueryResults are greater than 4.");
            break;
        default: // count > 1
            // handle more than one element
            Console.WriteLine("More than one element of dbQueryResults is greater than 4");
            break;
    }
}

Voltar ao início Neste artigo Nesta seção nesta seção

Artigos relacionados

Diretrizes de design para exceções (diretrizes de Design do .NET Framework)

manipulando e lançando exceções (Essentials de aplicativo do .NET Framework)

como: receber notificações de exceção de primeira Chance (guia de desenvolvimento do .NET Framework)

como: manipular exceções em uma consulta PLINQ (guia de desenvolvimento do .NET Framework)

Exceções em Threads gerenciados (guia de desenvolvimento do .NET Framework)

exceções e manipulação de exceções (guia de programação c#)

(referência de c#) instruções de manipulação de exceção

Try...Catch...Instrução Finally (Visual Basic)

tratamento de exceções (F #)

exceções em C++ c++ /CLI

(Task Parallel Library) de manipulação de exceção

tratamento de exceções (depuração)

passo a passo: Manipulando uma exceção de simultaneidade (acesso a dados no Visual Studio)

como: manipular erros e exceções que ocorrem com ligação de dados (Windows Forms)

tratamento de exceções em aplicativos de rede (XAML) (Windows)

Voltar ao início Neste artigo