Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Tutti i metodi basati su LINQ seguono uno dei due modelli simili. Accettano una sequenza enumerabile. Restituiscono una sequenza diversa o un singolo valore. La coerenza della forma consente di estendere LINQ scrivendo metodi con una forma simile. Infatti, le librerie .NET hanno ottenuto nuovi metodi in molte versioni .NET dalla prima introduzione di LINQ. In questo articolo vengono visualizzati esempi di estensione di LINQ scrivendo metodi personalizzati che seguono lo stesso modello.
Aggiungere metodi personalizzati per le query LINQ
Estendere il set di metodi usati per le query LINQ aggiungendo metodi di estensione all'interfaccia IEnumerable<T> . Oltre alle operazioni standard medie o massime, ad esempio, si crea un metodo di aggregazione personalizzato per calcolare un singolo valore da una sequenza di valori. Si crea anche un metodo che funziona come filtro personalizzato o come trasformazione dati specifica per una sequenza di valori e restituisce una nuova sequenza. Esempi di tali metodi sono Distinct, Skipe Reverse.
Quando si estende l'interfaccia IEnumerable<T> , è possibile applicare i metodi personalizzati a qualsiasi raccolta enumerabile. Per altre informazioni, vedere Membri dell'estensione.
Un metodo di aggregazione calcola un singolo valore da un set di valori. LINQ offre diversi metodi di aggregazione, tra cui Average, Mine Max. Creare un metodo di aggregazione personalizzato aggiungendo un metodo di estensione all'interfaccia IEnumerable<T> .
A partire da C# 14, è possibile dichiarare un blocco di estensione per contenere più membri di estensione. Dichiarare un blocco di estensione con la parola chiave extension seguita dal parametro receiver tra parentesi. Nell'esempio di codice seguente viene illustrato come creare un metodo di estensione denominato Median in un blocco di estensione. Il metodo calcola una median per una sequenza di numeri di tipo 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];
}
}
}
È anche possibile aggiungere il this modificatore a un metodo statico per dichiarare un metodo di estensione. Il codice seguente illustra il metodo di estensione equivalente Median :
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];
}
}
}
Chiamare uno dei metodi di estensione per qualsiasi raccolta enumerabile nello stesso modo in cui si chiamano altri metodi di aggregazione dall'interfaccia IEnumerable<T> .
Nell'esempio di codice seguente viene illustrato come usare il Median metodo per una matrice di tipo 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
Eseguire l'overload del metodo di aggregazione in modo che accetti sequenze di vari tipi. L'approccio standard consiste nel creare un overload per ogni tipo. Un altro approccio consiste nel creare un overload che accetta un tipo generico e convertirlo in un tipo specifico usando un delegato. È anche possibile combinare entrambi gli approcci.
Creare un overload specifico per ogni tipo che si desidera supportare. Nell'esempio di codice seguente viene illustrato un overload del Median metodo per il int tipo .
// int overload
public static double Median(this IEnumerable<int> source) =>
(from number in source select (double)number).Median();
È ora possibile chiamare gli Median overload per entrambi integer i tipi e double , come illustrato nel codice seguente:
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
È anche possibile creare un overload che accetta una sequenza generica di oggetti. Questo overload accetta un delegato come parametro e lo usa per convertire una sequenza di oggetti di un tipo generico in un tipo specifico.
Il codice seguente illustra un overload del Median metodo che accetta il Func<T,TResult> delegato come parametro. Questo delegato accetta un oggetto di tipo generico T e restituisce un oggetto di tipo double.
// generic overload
public static double Median<T>(
this IEnumerable<T> numbers, Func<T, double> selector) =>
(from num in numbers select selector(num)).Median();
È ora possibile chiamare il Median metodo per una sequenza di oggetti di qualsiasi tipo. Se il tipo non dispone di un proprio overload del metodo, passare un parametro delegato. In C# è possibile usare un'espressione lambda a questo scopo. Inoltre, solo in Visual Basic, se si utilizza la Aggregate clausola o Group By anziché la chiamata al metodo, è possibile passare qualsiasi valore o espressione nell'ambito di questa clausola.
Il codice di esempio seguente illustra come chiamare il Median metodo per una matrice di numeri interi e una matrice di stringhe. Per le stringhe, viene calcolata la mediano per le lunghezze delle stringhe nella matrice. Nell'esempio viene illustrato come passare il Func<T,TResult> parametro delegato al Median metodo per ogni caso.
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
Estendere l'interfaccia IEnumerable<T> con un metodo di query personalizzato che restituisce una sequenza di valori. In questo caso, il metodo deve restituire una raccolta di tipo IEnumerable<T>. Tali metodi possono essere usati per applicare filtri o trasformazioni di dati a una sequenza di valori.
Nell'esempio seguente viene illustrato come creare un metodo di estensione denominato AlternateElements che restituisce ogni altro elemento di una raccolta, a partire dal primo elemento.
// 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++;
}
}
Chiamare questo metodo di estensione per qualsiasi raccolta enumerabile esattamente come si chiamano altri metodi dall'interfaccia IEnumerable<T> , come illustrato nel codice seguente:
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
Ogni esempio illustrato in questo articolo ha un ricevitore diverso. Ciò significa che è necessario dichiarare ogni metodo in un blocco di estensione diverso che specifica il ricevitore univoco. L'esempio di codice seguente illustra una singola classe statica con tre blocchi di estensione diversi, ognuno dei quali contiene uno dei metodi definiti in questo articolo:
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++;
}
}
}
}
Il blocco di estensione finale dichiara un blocco di estensione generico. Il parametro di tipo per il ricevitore viene dichiarato sull'oggetto extension stesso.
Nell'esempio precedente viene dichiarato un membro di estensione in ogni blocco di estensione. Nella maggior parte dei casi, si creano più membri di estensione per lo stesso ricevitore. In questi casi, dichiarare le estensioni per tali membri in un singolo blocco di estensione.