LINQ - Usando os métodos Single e SingleOrDefault


O LINQ possui o método de extensão Single que pode ser usado para retornar somente um elemento em uma sequência que satisfaça uma condição específica.

Nota: Para saber mais sobre métodos de extensão veja o artigo: VB.NET - Extensions Methods

O problema é que o método Single irá lançar uma exceção do tipo System.InvalidOperationException quando nenhum elemento na sequência satisfazer a condição que foi definida.

Para contornar este problema você pode usar o método de extensão SingleOrDefault que vai retornar o valor padrão do tipo ao invés de lançar a exceção quando nenhum elemento satisfazer a condição.

Agora, preste atenção, se mais de um elemento na sequência satisfazer a condição ambos os métodos irão lançar a exceção System.InvalidOperationException e neste caso você pode apelar para os métodos de extensão First ou Last.

Nota: Geralmente usamos estes métodos de extensão em expressões lambda.

O que são Expressões Lambda ?

As expressões lambda foram incluídas no VS/VB 2008 para dar suporte a consultas LINQ. As cláusulas Where são assim compiladas como expressões lambdas e chamadas em itens aplicáveis do seu dataset. Podem ser consideradas uma forma de delegate que pode passar ou retornar outra função.

Nota: Delegates permitem que uma classe use métodos de outra classe. Para saber mais sobre delegates leia o meu artigo: Usando Delegates

No LINQ as expressões lambda são usadas para realizar ações sobre listas de objetos e com métodos de extensão.

Uma expressão lambda é então uma função sem nome que calcula e retorna um valor único e podem ser usadas em qualquer lugar que um tipo delegate for válido.

fonte:  ASP .NET - Usando LINQ - Expressões Lambada

Vejamos isso na prática: ( criado no Visual Basic 2008 Express Edition aplicação do tipo console:)

Module Module1

    Sub Main()

        ' Cria uma lista genérica de inteiros
        Dim l0 As New List(Of Integer)

        'Inclui mais elementos a lista
        l0.Add(11) '
        l0.Add(15) 
        l0.Add(13) 
        l0.Add(17) 

        Try
            ' Retorna somente o resultado 11 pois somente ele satisfaz a condição (i=11)
            Dim resultado As Integer = l0.Single(Function(i) i = 11)
            Console.WriteLine("Elemento igual a 11 => " & resultado)
            Console.ReadKey()

            ' Retorna o valor padrão para os inteiros que é zero
            ' pois nenhum elemento na lista é igual a 14
            resultado = l0.SingleOrDefault(Function(i) i = 14)
            Console.WriteLine("Elemento igual a 14 (SingleOrDefault) => " & resultado)
            Console.ReadKey()

            ' lança a exceção System.InvalidOperationException 
            ' pois nenhum elemento na lista é igual a 14
            resultado = l0.Single(Function(i) i = 14)
            Console.WriteLine("Elemento igual a 14 (Single) => " & resultado)
            Console.ReadKey()

            ' lança a exceção System.InvalidOperationException
            ' pois temos dois elementos(15 e 17) maiores que 13
            resultado = l0.Single(Function(i) i > 13)
            Console.WriteLine("Elemento maior que 13 (Single) => " & resultado)
            Console.ReadKey()

            resultado = l0.SingleOrDefault(Function(i) i > 13)
            Console.WriteLine("Elemento maior que 13 (SingleOrDefault) => " & resultado)

        Catch ex As Exception
            Console.WriteLine("Erro : " & ex.Message)
        End Try
        Console.ReadKey()
    End Sub

End Module

Quando temos mais de um elemento na sequência, para contornar a exceção que seria lançada você pode usar o método de extensão para retornar o primeiro elemento que satifaz a condição:

resultado = l0.First(Function(i) i > 13)

ou o último elemento:

resultado = l0.Last(Function(i) i > 13)

O mesmo código para C# ficaria assim: (Pode ser usando no Visual C#  Express Edition ou no SharpDevelop 3.0)

public static void Main(string[] args)
{
	Console.WriteLine("Usando Single ou SingleOrDefault");
	
    // Cria uma nova lista generica de inteiros
    List<int> 10 = new List<int>();

    //inclui mais inteiros a lista
    l0.Add(12); 
    l0.Add(15);
    l0.Add(13);
    10.Add(17);

    // Retorna o valor 10 pois somente 10 satisfaz a condição
    int resultado = l0.Single(i => i == 10);
    Console.Write("valor = " + resultado);

    // Retorna o valor padrão que para os inteiros é zero
    // pois nenhum elemento na lista é igual a 14
    resultado = l0.SingleOrDefault(i => i == 14);
    Console.Write("valor = " + resultado);
		
    // lança a execção System.InvalidOperationException 
    // pois nenhum elemento na lista é igual a 14
    resultado = l0.Single(i => i == 14);
    Console.Write("valor = " + resultado);
		
    // lança a exeção System.InvalidOperationException
    // pois temos dois elementos, 15 e 17 que são maiores que 13
    resultado = l0.Single(i => i > 13);
    Console.Write("valor = " + resultado);

    resultado = l0.SingleOrDefault(i => i > 13);
    Console.Write("valor = " + resultado);

    Console.Write("Pressiona algo para continuar . . . ");
   Console.ReadKey(true);
}

Aguarde mais artigos sobre LINQ.

Referências:


José Carlos Macoratti