Como: adicionar métodos personalizados para consultas do LINQ

Você pode estender o conjunto de métodos que podem ser usados para consultas LINQ, adicionando os métodos de extensão para o IEnumerable<T> interface.Por exemplo, com as operações máxima ou média padrão, você pode criar um método personalizado de agregação para calcular um valor único de uma seqüência de valores.Você também pode criar um método que funciona como um filtro personalizado ou uma transformação de dados específicos para uma seqüência de valores e retorna uma nova seqüência.Exemplos de tais métodos são Distinct, Skip<TSource>, e Reverse<TSource>.

Quando você estende o IEnumerable<T> interface, você pode aplicar seus métodos personalizados para qualquer coleção enumerável.Para obter mais informações, consulte Métodos de extensão (guia de programação do C#) ou Métodos de extensão (Visual Basic).

Adicionar um método agregado

Um método aggregate calcula um valor único de um conjunto de valores.LINQ fornece vários métodos de agregação, incluindo Average, Min, e Max.Você pode criar seu próprio método aggregate adicionando um método de extensão para o IEnumerable<T> interface.

O exemplo de código a seguir mostra como criar um método de extensão chamado Median para computar um ponto intermediário para uma seqüência de números do tipo double.

Imports System.Runtime.CompilerServices

Module LINQExtension

    ' Extension method for the IEnumerable(of T) interface. 
    ' The method accepts only values of the Double type.
    <Extension()> 
    Function Median(ByVal source As IEnumerable(Of Double)) As Double
        If source.Count = 0 Then
            Throw New InvalidOperationException("Cannot compute median for an empty set.")
        End If

        Dim sortedSource = From number In source 
                           Order By number

        Dim itemIndex = sortedSource.Count \ 2

        If sortedSource.Count Mod 2 = 0 Then
            ' Even number of items in list.
            Return (sortedSource(itemIndex) + sortedSource(itemIndex - 1)) / 2
        Else
            ' Odd number of items in list.
            Return sortedSource(itemIndex)
        End If
    End Function
End Module
public static class LINQExtension
{
    public static double Median(this IEnumerable<double> source)
    {
        if (source.Count() == 0)
        {
            throw new InvalidOperationException("Cannot compute median for an empty set.");
        }

        var sortedList = from number in source
                         orderby number
                         select number;

        int itemIndex = (int)sortedList.Count() / 2;

        if (sortedList.Count() % 2 == 0)
        {
            // Even number of items.
            return (sortedList.ElementAt(itemIndex) + sortedList.ElementAt(itemIndex - 1)) / 2;
        }
        else
        {
            // Odd number of items.
            return sortedList.ElementAt(itemIndex);
        }
    }
}

Chamar esse método de extensão para qualquer coleção enumerável, da mesma forma que você chamar outros métodos agregados a partir do IEnumerable<T> interface.

ObservaçãoObservação

No Visual Basic, você pode usar uma chamada de método ou a sintaxe de consulta padrão para o Aggregate ou Group By cláusula.Para obter mais informações, consulte Cláusula Aggregate (Visual Basic) e Agrupar por cláusula (Visual Basic).

O exemplo de código a seguir mostra como usar o Median método para uma matriz do tipo double.

        Dim numbers1() As Double = {1.9, 2, 8, 4, 5.7, 6, 7.2, 0}

        Dim query1 = Aggregate num In numbers1 Into Median()

        Console.WriteLine("Double: Median = " & query1)



...


        ' This code produces the following output:
        '
        ' Double: Median = 4.85

        double[] numbers1 = { 1.9, 2, 8, 4, 5.7, 6, 7.2, 0 };

        var query1 = numbers1.Median();

        Console.WriteLine("double: Median = " + query1);



...


/*
 This code produces the following output:

 Double: Median = 4.85
*/

Cc981895.collapse_all(pt-br,VS.110).gifUm método Aggregate para aceitar vários tipos de sobrecarga

Você pode sobrecarregar o método aggregate para que ele aceite as seqüências de vários tipos.A abordagem padrão é criar uma sobrecarga para cada tipo.Outra abordagem é criar uma sobrecarga que irá tirar um tipo genérico e convertê-lo a um tipo específico, usando um delegado.Você também pode combinar as duas abordagens.

Cc981895.collapse_all(pt-br,VS.110).gifPara criar uma sobrecarga para cada tipo.

Você pode criar uma sobrecarga específica para cada tipo que você deseja oferecer suporte.O exemplo de código a seguir mostra uma sobrecarga de Median método para o integer tipo.

' Integer overload

<Extension()> 
Function Median(ByVal source As IEnumerable(Of Integer)) As Double
    Return Aggregate num In source Select CDbl(num) Into med = Median()
End Function
//int overload

public static double Median(this IEnumerable<int> source)
{
    return (from num in source select (double)num).Median();
}

Agora você pode chamar o Median sobrecargas para ambos integer e double tipos, conforme mostrado no código a seguir:

        Dim numbers1() As Double = {1.9, 2, 8, 4, 5.7, 6, 7.2, 0}

        Dim query1 = Aggregate num In numbers1 Into Median()

        Console.WriteLine("Double: Median = " & query1)



...


        Dim numbers2() As Integer = {1, 2, 3, 4, 5}

        Dim query2 = Aggregate num In numbers2 Into Median()

        Console.WriteLine("Integer: Median = " & query2)



...


' This code produces the following output:
'
' Double: Median = 4.85
' Integer: Median = 3
        double[] numbers1 = { 1.9, 2, 8, 4, 5.7, 6, 7.2, 0 };

        var query1 = numbers1.Median();

        Console.WriteLine("double: Median = " + query1);



...


        int[] numbers2 = { 1, 2, 3, 4, 5 };

        var query2 = numbers2.Median();

        Console.WriteLine("int: Median = " + query2);



...


/*
 This code produces the following output:

 Double: Median = 4.85
 Integer: Median = 3
*/

Cc981895.collapse_all(pt-br,VS.110).gifPara criar uma sobrecarga genérica

Você também pode criar uma sobrecarga que aceita uma seqüência de objetos genéricos.Essa sobrecarga leva um delegado como um parâmetro e o usa para converter uma seqüência de objetos de um tipo genérico em um tipo específico.

O código a seguir mostra uma sobrecarga do Median método que usa a Func<T, TResult> delegar como um parâmetro.Esse delegado utiliza um objeto de tipo genérico t e retorna um objeto do tipo double.

' Generic overload.

<Extension()> 
Function Median(Of T)(ByVal source As IEnumerable(Of T), 
                      ByVal selector As Func(Of T, Double)) As Double
    Return Aggregate num In source Select selector(num) Into med = Median()
End Function
// Generic overload.

public static double Median<T>(this IEnumerable<T> numbers,
                       Func<T, double> selector)
{
    return (from num in numbers select selector(num)).Median();
}

Agora você pode chamar o Median método para uma seqüência de objetos de qualquer tipo.Se o tipo não tiver seu próprio sobrecarga de método, você precisa passar um parâmetro delegate.No Visual Basic e C#, você pode usar uma expressão lambda para essa finalidade.Além disso, nas do Visual Basic somente, se você usar o Aggregate ou Group By cláusula em vez da chamada do método, você pode passar qualquer valor ou expressão que está no escopo desta cláusula.

O exemplo de código a seguir mostra como chamar o Median método para uma matriz de inteiros e uma matriz de seqüências de caracteres.Para seqüências de caracteres, a mediana para os comprimentos de seqüências de caracteres na matriz é calculada.O exemplo mostra como passar o Func<T, TResult> delegar o parâmetro para o Median método para cada caso.

Dim numbers3() As Integer = {1, 2, 3, 4, 5}

' You can use num as a parameter for the Median method 
' so that the compiler will implicitly convert its value to double.
' If there is no implicit conversion, the compiler will
' display an error message.

Dim query3 = Aggregate num In numbers3 Into Median(num)

Console.WriteLine("Integer: Median = " & query3)

Dim numbers4() As String = {"one", "two", "three", "four", "five"}

' With the generic overload, you can also use numeric properties of objects.

Dim query4 = Aggregate str In numbers4 Into Median(str.Length)

Console.WriteLine("String: Median = " & query4)

' This code produces the following output:
'
' Integer: Median = 3
' String: Median = 4
int[] numbers3 = { 1, 2, 3, 4, 5 };

/* 
  You can use the num=>num lambda expression as a parameter for the Median method 
  so that the compiler will implicitly convert its value to double.
  If there is no implicit conversion, the compiler will display an error message.          
*/

var query3 = numbers3.Median(num => num);

Console.WriteLine("int: Median = " + query3);

string[] numbers4 = { "one", "two", "three", "four", "five" };

// With the generic overload, you can also use numeric properties of objects.

var query4 = numbers4.Median(str => str.Length);

Console.WriteLine("String: Median = " + query4);

/*
 This code produces the following output:

 Integer: Median = 3
 String: Median = 4
*/

Adicionando um método que retorna uma coleção

Você pode estender o IEnumerable<T> interface com um método de consulta personalizada que retorna uma seqüência de valores.Nesse caso, o método deve retornar uma coleção do tipo IEnumerable<T>.Tais métodos podem ser usados para aplicar transformações de dados ou filtros a uma seqüência de valores.

O exemplo a seguir mostra como criar um método de extensão chamado AlternateElements que retorna todos os outros elementos em uma coleção, iniciando a partir do primeiro elemento.

' Extension method for the IEnumerable(of T) interface. 
' The method returns every other element of a sequence.

<Extension()> 
Function AlternateElements(Of T)(
    ByVal source As IEnumerable(Of T)
    ) As IEnumerable(Of T)

    Dim list As New List(Of T)
    Dim i = 0
    For Each element In source
        If (i Mod 2 = 0) Then
            list.Add(element)
        End If
        i = i + 1
    Next
    Return list
End Function
// Extension method for the IEnumerable<T> interface. 
// The method returns every other element of a sequence.

public static IEnumerable<T> AlternateElements<T>(this IEnumerable<T> source)
{
    List<T> list = new List<T>();

    int i = 0;

    foreach (var element in source)
    {
        if (i % 2 == 0)
        {
            list.Add(element);
        }

        i++;
    }

    return list;
}

Você pode chamar esse método de extensão para qualquer coleção enumerável, exatamente como você chamaria a outros métodos da IEnumerable<T> interface, conforme mostrado no código a seguir:

Dim strings() As String = {"a", "b", "c", "d", "e"}

Dim query = strings.AlternateElements()

For Each element In query
    Console.WriteLine(element)
Next

' This code produces the following output:
'
' a
' c
' e
string[] strings = { "a", "b", "c", "d", "e" };

var query = strings.AlternateElements();

foreach (var element in query)
{
    Console.WriteLine(element);
}
/*
 This code produces the following output:

 a
 c
 e
*/

Consulte também

Referência

IEnumerable<T>

Métodos de extensão (guia de programação do C#)

Conceitos

Métodos de extensão (Visual Basic)