Gewusst wie: Hinzufügen von benutzerdefinierten Methoden zu LINQ-Abfragen

Sie können die Methoden für LINQ-Abfragen durch Hinzufügen von Erweiterungsmethoden zur IEnumerable<T>-Schnittstelle erweitern. Zusätzlich zu den Standardoperationen für Mittel- oder Höchstwerte können Sie z. B. eine benutzerdefinierte Aggregatmethode erstellen, um einen einzelnen Wert aus einer Sequenz von Werten zu berechnen. Darüber hinaus können Sie auch eine Methode erstellen, die als benutzerdefinierter Filter oder spezifische Datentransformation für eine Sequenz von Werten fungiert und eine neue Sequenz zurückgibt. Beispiele für solche Methoden sind Distinct, Skip<TSource> und Reverse<TSource>.

Wenn Sie die IEnumerable<T>-Schnittstelle erweitern, können Sie die benutzerdefinierten Methoden auf jede aufzählbare Auflistung anwenden. Weitere Informationen finden Sie unter Erweiterungsmethoden (C#-Programmierhandbuch) oder unter Erweiterungsmethoden (Visual Basic).

Hinzufügen einer Aggregatmethode

Eine Aggregatmethode berechnet einen einzelnen Wert aus einem Satz von Werten. LINQ stellt mehrere Aggregatmethoden bereit, einschließlich von Average, Min und Max. Sie können eine eigene Aggregatmethode erstellen, indem Sie der IEnumerable<T>-Schnittstelle eine Erweiterungsmethode hinzufügen.

Im folgenden Codebeispiel wird gezeigt, wie eine Erweiterungsmethode mit dem Namen Median erstellt wird, um für eine Sequenz von Zahlen des Typs double einen Median zu berechnen.

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);
        }
    }
}

Diese Erweiterungsmethode für aufzählbare Auflistungen wird auf die gleiche Weise wie andere Aggregatmethoden von der IEnumerable<T>-Schnittstelle aufgerufen.

Tipp

In Visual Basic können Sie entweder einen Methodenaufruf oder die Standardabfragesyntax für die Aggregate-Klausel oder die Group By-Klausel verwenden. Weitere Informationen finden Sie unter Aggregate-Klausel (Visual Basic) und unter GROUP BY-Klausel (Visual Basic).

Im folgenden Codebeispiel wird veranschaulicht, wie Sie die Median-Methode für ein Array vom Typ double verwenden.

        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
*/

Überladen einer Aggregatmethode zum Akzeptieren verschiedener Typen

Sie können die Aggregatmethode überladen, damit sie Sequenzen verschiedener Typen akzeptiert. Der Standardansatz besteht darin, für jeden Typ eine Überladung zu erstellen. Ein anderer Ansatz ist das Erstellen einer Überladung, die einen generischen Typ annimmt und mit einem Delegaten in einen bestimmten Typ konvertiert. Sie können beide Ansätze auch kombinieren.

So erstellen Sie eine Überladung für jeden Typ

Sie können eine spezifische Überladung für jeden Typ erstellen, der unterstützt werden soll. Im folgenden Codebeispiel wird eine Überladung der Median-Methode für den integer-Typ veranschaulicht.

' 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();
}

Sie können jetzt die Median-Überladungen für den integer-Typ und den double-Typ aufrufen, wie im folgenden Code gezeigt:

        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
*/

So erstellen Sie eine generische Überladung

Sie können auch eine Überladung erstellen, die eine Sequenz generischer Objekte akzeptiert. Diese Überladung akzeptiert einen Delegaten als Parameter und verwendet diesen, um eine Sequenz von Objekten eines generischen Typs in einen bestimmten Typ zu konvertieren.

Im folgenden Code wird eine Überladung der Median-Methode veranschaulicht, die den Func<T, TResult>-Delegaten als Parameter akzeptiert. Dieser Delegat nimmt ein Objekt des generischen Typs T an und gibt ein Objekt des Typs double zurück.

' 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();
}

Jetzt können Sie die Median-Methode für eine Sequenz von Objekten eines beliebigen Typs aufrufen. Wenn der Typ keine eigene Methodenüberladung aufweist, müssen Sie einen Delegatparameter übergeben. In Visual Basic und C# können Sie zu diesem Zweck einen Lambda-Ausdruck verwenden. In Visual Basic können Sie außerdem bei Verwendung der Aggregate-Klausel oder der Group By-Klausel anstelle des Methodenaufrufs einen beliebigen Wert oder Ausdruck übergeben, der sich im Gültigkeitsbereich dieser Klausel befindet.

Im folgenden Beispielcode wird gezeigt, wie die Median-Methode für ein Array von ganzen Zahlen und für ein Zeichenfolgenarray aufgerufen wird. Für Zeichenfolgen wird der Median für die Längen der Zeichenfolgen im Array berechnet. Im Beispiel wird gezeigt, wie für jeden Fall der Func<T, TResult>-Delegatparameter an die Median-Methode übergeben wird.

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
*/

Hinzufügen einer Methode, die eine Auflistung zurückgibt

Sie können die IEnumerable<T>-Schnittstelle mit einer benutzerdefinierten Abfragemethode erweitern, die eine Sequenz von Werten zurückgibt. In diesem Fall muss die Methode eine Auflistung des Typs IEnumerable<T> zurückgeben. Solche Methoden können verwendet werden, um Filter oder Datentransformationen auf eine Sequenz von Werten anzuwenden.

Im folgenden Beispiel wird gezeigt, wie eine Erweiterungsmethode mit dem Namen AlternateElements erstellt wird, die jedes zweite Element in einer Auflistung zurückgibt, beginnend beim ersten Element.

' 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;
}

Sie können diese Erweiterungsmethode für jede aufzählbare Auflistung ebenso wie andere Methoden über die IEnumerable<T>-Schnittstelle aufrufen, wie im folgenden Code gezeigt:

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
*/

Siehe auch

Referenz

IEnumerable<T>

Erweiterungsmethoden (C#-Programmierhandbuch)

Konzepte

Erweiterungsmethoden (Visual Basic)