Comment : ajouter des méthodes personnalisées pour les requêtes LINQ
Vous pouvez étendre le jeu des méthodes que vous pouvez utiliser pour les requêtes LINQ en ajoutant des méthodes d'extension à l'interface IEnumerable. Par exemple, vous pouvez créer une méthode d'agrégation personnalisée pour calculer une valeur unique à partir d'une séquence de valeurs, en plus de la moyenne standard ou opérations maximales. Vous pouvez également créer une méthode qui fonctionne comme un filtre personnalisé ou une transformation des données spécifique pour une séquence de valeurs et retourne une nouvelle séquence. Distinct``1, Skip``1, et Reverse``1 en sont quelques exemples.
Lorsque vous étendez l'interface IEnumerable, vous pouvez appliquer vos méthodes personnalisées à n'importe quelle collection énumérable. Pour plus d'informations, consultez Méthodes d'extension (Guide de programmation C#) ou Méthodes d'extension (Visual Basic).
Ajout d'une méthode d'agrégation
Une méthode d'agrégation calcule une valeur unique d'un jeu de valeurs. LINQ fournit plusieurs méthodes d'agrégation, notamment Average``1, Min``1 et Max``1. Vous pouvez créer votre propre méthode d'agrégation en ajoutant une méthode d'extension à l'interface IEnumerable.
L'exemple de code suivant montre comment créer une méthode d'extension appelée Median pour calculer une valeur médiane pour une séquence de nombres de type 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);
}
}
}
Vous appelez cette méthode d'extension pour toute collection énumérable de la même façon que vous appelez d'autres méthodes d'agrégation depuis l'interface IEnumerable.
Notes
En Visual Basic, vous pouvez utiliser un appel de méthode ou une syntaxe de requête standard pour la clause Aggregate ou Group ByPour plus d'informations, consultez Aggregate, clause (Visual Basic) et Group By, clause (Visual Basic).
L'exemple de code suivant montre comment utiliser la méthode Median pour un tableau du type 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
*/
Surcharge d'une méthode d'agrégation pour accepter différents types
Vous pouvez surcharger votre méthode d'agrégation afin qu'elle accepte des séquences de différents types. L'approche standard est de créer une surcharge pour chaque type. Une autre approche est de créer une surcharge qui prendra un type générique pour le convertir en un type spécifique à l'aide d'un délégué. Vous pouvez également combiner ces deux approches.
Pour créer une surcharge pour chaque type
Vous pouvez créer une surcharge spécifique pour chaque type que vous souhaitez prendre en charge. L'exemple de code suivant montre une surcharge de la méthode Median du type integer.
' 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();
}
Vous pouvez maintenant appeler les surcharges Median pour les types integer et double, comme le montre le code suivant :
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
*/
Pour créer une surcharge générique
Vous pouvez également créer une surcharge qui accepte une séquence d'objets génériques. Cette surcharge prend un délégué comme un paramètre et l'utilise pour convertir une séquence d'objets d'un type générique en un type spécifique.
Le code suivant affiche une surcharge de la méthode Median qui prend le délégué Func en tant que paramètre. Ce délégué prend un objet de type générique T et retourne un objet de type 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();
}
Vous pouvez maintenant appeler la méthode Median pour une séquence d'objets de tout type. Si le type n'a pas sa propre surcharge de méthode, vous devez passer un paramètre délégué. Dans Visual Basic et C#, vous pouvez utiliser une expression lambda à cette fin. De plus, dans Visual Basic uniquement, si vous utilisez la clause Aggregate ou Group By au lieu de l'appel de méthode, vous pouvez passer n'importe quelle valeur ou expression qui est dans la portée de cette clause.
Le code d'exemple suivant montre comment appeler la méthode Median pour un tableau d'entiers et un tableau de chaînes. Pour les chaînes, la valeur médiane pour les longueurs de chaînes dans le tableau est calculée. L'exemple indique comment passer le paramètre délégué Func à la méthode Median pour chaque cas.
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
*/
Ajout d'une méthode qui retourne une collection
Vous pouvez étendre l'interface IEnumerable avec une méthode de requête personnalisée qui retourne une séquence de valeurs. Dans ce cas, la méthode doit retourner une collection de type IEnumerable. Ces méthodes peuvent être utilisées pour appliquer des filtres ou des transformations de données à une séquence de valeurs.
L'exemple suivant affiche comment créer une méthode d'extension nommée AlternateElements qui retourne chaque autre élément dans une collection, en commençant par le premier élément.
' 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;
}
Vous pouvez appeler cette méthode d'extension pour n'importe quelle collection énumérable comme vous appelleriez d'autres méthodes depuis l'interface IEnumerable, comme le montre le code suivant :
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
*/
Voir aussi
Référence
Méthodes d'extension (Guide de programmation C#)