Přidání vlastních metod pro dotazy LINQ (C#)

Sadu metod, které používáte pro dotazy LINQ, rozšíříte přidáním rozšiřujících metod do IEnumerable<T> rozhraní. Kromě standardních průměrných nebo maximálních operací například vytvoříte vlastní agregační metodu pro výpočet jedné hodnoty z posloupnosti hodnot. Vytvoříte také metodu, která funguje jako vlastní filtr nebo konkrétní transformace dat pro sekvenci hodnot a vrací novou sekvenci. Příklady takových metod jsou Distinct, Skipa Reverse.

Když rozhraní rozšíříte IEnumerable<T> , můžete vlastní metody použít na libovolnou výčtové kolekce. Další informace naleznete v tématu Metody rozšíření.

Přidání agregační metody

Agregační metoda vypočítá jednu hodnotu ze sady hodnot. LINQ poskytuje několik agregačních metod, včetně Average, Mina Max. Vlastní agregační metodu můžete vytvořit přidáním rozšiřující metody do IEnumerable<T> rozhraní.

Následující příklad kódu ukazuje, jak vytvořit rozšiřující metodu volanou Median pro výpočet mediánu pro sekvenci čísel typu double.

public static class EnumerableExtension
{
    public static double Median(this IEnumerable<double>? source)
    {
        if (source is null || !source.Any())
        {
            throw new InvalidOperationException("Cannot compute median for a null or empty set.");
        }

        var sortedList =
            source.OrderBy(number => number).ToList();

        int itemIndex = sortedList.Count / 2;

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

Tuto metodu rozšíření voláte pro všechny výčtové kolekce stejným způsobem, jakým voláte jiné agregační metody z IEnumerable<T> rozhraní.

Následující příklad kódu ukazuje, jak použít metodu Median pro pole typu double.

double[] numbers = [1.9, 2, 8, 4, 5.7, 6, 7.2, 0];
var query = numbers.Median();

Console.WriteLine($"double: Median = {query}");
// This code produces the following output:
//     double: Median = 4.85

Přetížení agregační metody pro příjem různých typů

Agregační metodu můžete přetížit tak, aby přijímala sekvence různých typů. Standardním přístupem je vytvoření přetížení pro každý typ. Dalším přístupem je vytvoření přetížení, které bude mít obecný typ a převést ho na konkrétní typ pomocí delegáta. Můžete také kombinovat oba přístupy.

Vytvoření přetížení pro každý typ

Pro každý typ, který chcete podporovat, můžete vytvořit konkrétní přetížení. Následující příklad kódu ukazuje přetížení Median metody pro int typ.

// int overload
public static double Median(this IEnumerable<int> source) =>
    (from number in source select (double)number).Median();

Nyní můžete volat Median přetížení pro oba integer typy double , jak je znázorněno v následujícím kódu:

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
//     int: Median = 3

Vytvoření obecného přetížení

Můžete také vytvořit přetížení, které přijímá posloupnost obecných objektů. Toto přetížení přebírá delegáta jako parametr a používá ho k převodu posloupnosti objektů obecného typu na určitý typ.

Následující kód ukazuje přetížení Median metody, která přebírá delegáta Func<T,TResult> jako parametr. Tento delegát přebírá objekt obecného typu T a vrací objekt typu double.

// generic overload
public static double Median<T>(
    this IEnumerable<T> numbers, Func<T, double> selector) =>
    (from num in numbers select selector(num)).Median();

Nyní můžete volat metodu Median pro posloupnost objektů libovolného typu. Pokud typ nemá vlastní přetížení metody, musíte předat parametr delegáta. V jazyce C# můžete pro tento účel použít výraz lambda. V jazyce Visual Basic můžete také pouze v případě, že místo volání metody použijete Aggregate klauzuli nebo Group By klauzuli, můžete předat libovolnou hodnotu nebo výraz, který je v oboru této klauzule.

Následující příklad kódu ukazuje, jak volat metodu Median pro pole celých čísel a pole řetězců. U řetězců se vypočítá medián délky řetězců v poli. Příklad ukazuje, jak předat Func<T,TResult> parametr delegáta Median metodě pro každý případ.

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:
//     int: Median = 3
//     string: Median = 4

Přidání metody, která vrací sekvenci

Rozhraní můžete rozšířit IEnumerable<T> vlastní metodou dotazu, která vrací posloupnost hodnot. V tomto případě musí metoda vrátit kolekci typu IEnumerable<T>. Tyto metody lze použít k použití filtrů nebo transformací dat na sekvenci hodnot.

Následující příklad ukazuje, jak vytvořit rozšiřující metodu s názvem AlternateElements , která vrací všechny ostatní prvky v kolekci počínaje prvním prvkem.

// 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)
{
    int index = 0;
    foreach (T element in source)
    {
        if (index % 2 == 0)
        {
            yield return element;
        }

        index++;
    }
}

Tuto metodu rozšíření můžete volat pro libovolnou výčet kolekcí stejně, jako byste volali jiné metody z IEnumerable<T> rozhraní, jak je znázorněno v následujícím kódu:

string[] strings = ["a", "b", "c", "d", "e"];

var query5 = strings.AlternateElements();

foreach (var element in query5)
{
    Console.WriteLine(element);
}
// This code produces the following output:
//     a
//     c
//     e

Viz také