Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Všechny metody založené na LINQ se řídí jedním ze dvou podobných vzorů. Zpracovávají výčet sekvencí. Vrátí jinou sekvenci nebo jednu hodnotu. Konzistence obrazce umožňuje rozšířit LINQ zápisem metod s podobným obrazcem. Knihovny .NET skutečně získaly nové metody v mnoha .NET vydáních od chvíle, kdy bylo poprvé představeno LINQ. V tomto článku vidíte příklady rozšíření LINQ napsáním vlastních metod, které se řídí stejným vzorem.
Přidání vlastních metod pro dotazy LINQ
Rozšiřte sadu metod, které používáte pro dotazy LINQ 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ž rozšíříte rozhraní IEnumerable<T>, můžete vlastní metody použít na libovolnou výčtovou kolekci. Další informace naleznete v tématu Členové rozšíření.
Agregační metoda vypočítá jednu hodnotu ze sady hodnot. LINQ poskytuje několik agregačních metod, včetně Average, Mina Max. Vytvořte vlastní agregační metodu přidáním rozšiřující metody do IEnumerable<T> rozhraní.
Počínaje jazykem C# 14 můžete deklarovat blok rozšíření , který bude obsahovat více členů rozšíření. Deklarujte blok rozšíření s klíčovým slovem extension následovaným parametrem příjemce v závorkách. Následující příklad kódu ukazuje, jak vytvořit rozšiřující metodu volanou Median v bloku rozšíření. Metoda vypočítá medián pro sekvenci čísel typu double.
extension(IEnumerable<double>? source)
{
public double Median()
{
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];
}
}
}
Modifikátor můžete také přidat this do statické metody, která deklaruje metodu rozšíření. Následující kód ukazuje ekvivalentní Median metodu rozšíření:
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];
}
}
}
Volejte rozšiřující metodu pro libovolnou výčtovou kolekci stejným způsobem jako jiné agregační metody z rozhraní IEnumerable<T>.
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ěžte agregační metodu 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é přijímá obecný typ a převede ho na konkrétní typ pomocí delegáta. Můžete také kombinovat oba přístupy.
Vytvořte pro každý typ, který chcete podporovat, 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 přetížení Median pro oba typy integer a 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
Můžete také vytvořit přetížení, které přijímá obecnou posloupnost 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á přetíženou metodu, předejte parametr delegáta. V jazyce C# můžete pro tento účel použít výraz lambda. Jazyk Visual Basic také pouze umožňuje, že pokud použijete klauzuli Aggregate nebo klauzuli Group By místo volání metody, můžete předat libovolnou hodnotu nebo výraz, který spadá do rozsahu 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
IEnumerable<T> Rozšiřte rozhraní vlastní metodou dotazu, která vrací sekvenci 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++;
}
}
Volejte tuto metodu rozšíření pro libovolnou výčtovou kolekci stejně, jako voláte jiné metody z rozhraní IEnumerable<T>, 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
Každý příklad uvedený v tomto článku má jiný příjemce. To znamená, že musíte deklarovat každou metodu v jiném rozšiřujícím bloku, který určuje jedinečný příjemce. Následující příklad kódu ukazuje jednu statickou třídu se třemi různými rozšiřujícími bloky, z nichž každá obsahuje jednu z metod definovaných v tomto článku:
public static class EnumerableExtension
{
extension(IEnumerable<double>? source)
{
public double Median()
{
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];
}
}
}
extension(IEnumerable<int> source)
{
public double Median() =>
(from number in source select (double)number).Median();
}
extension<T>(IEnumerable<T> source)
{
public double Median(Func<T, double> selector) =>
(from num in source select selector(num)).Median();
public IEnumerable<T> AlternateElements()
{
int index = 0;
foreach (T element in source)
{
if (index % 2 == 0)
{
yield return element;
}
index++;
}
}
}
}
Poslední blok rozšíření deklaruje obecný blok rozšíření. Parametr typu pro příjemce je deklarován na extension samotném.
Předchozí příklad deklaruje jeden člen rozšíření v každém bloku rozšíření. Ve většině případů vytvoříte pro stejného příjemce více rozšiřujících členů. V těchto případech deklarujte rozšíření pro tyto členy v jednom bloku rozšíření.