LINQ 查询添加自定义方法

所有基于 LINQ 的方法都遵循两种类似的模式之一。 它们采用可枚举序列。 它们会返回不同的序列或单个值。 通过形状的一致性,可以通过编写具有类似形状的方法来扩展 LINQ。 事实上,自首次引入 LINQ 以来,.NET 库就在许多 .NET 版本中都获得了新的方法。 在本文中,你将看到通过编写遵循相同模式的自己的方法来扩展 LINQ 的示例。

通过向 IEnumerable<T> 接口添加扩展方法扩展可用于 LINQ 查询的方法集。 例如,除了标准平均值或最大值运算,还可创建自定义聚合方法,从一系列值计算单个值。 此外,还可创建一种方法,用作值序列的自定义筛选器或特定数据转换,并返回新的序列。 Distinct、Skip 和 Reverse 就是此类方法的示例。

扩展 IEnumerable<T> 接口时,可以将自定义方法应用于任何可枚举集合。 有关详细信息,请参阅扩展方法。

聚合方法可从一组值计算单个值。 LINQ 提供多个聚合方法,包括 Average、Min 和 Max。 可以通过向 IEnumerable<T> 接口添加扩展方法来创建自己的聚合方法。

下面的代码示例演示如何创建名为 Median 的扩展方法来计算类型为 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];
        }
    }
}
复制代码

使用从 IEnumerable<T> 接口调用其他聚合方法的方式为任何可枚举集合调用此扩展方法。

下面的代码示例说明如何为类型 double 的数组使用 Median 方法。

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

可以重载聚合方法,以便其接受各种类型的序列。 标准做法是为每种类型都创建一个重载。 另一种方法是创建一个采用泛型类型的重载,并使用委托将其转换为特定类型。 还可以将两种方法结合。

可以为要支持的每种类型创建特定重载。 下面的代码示例演示 int 类型的 Median 方法的重载。

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

现在便可以为 integer 和 double 类型调用 Median 重载了,如以下代码中所示:

复制代码
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
复制代码

还可以创建接受泛型对象序列的重载。 此重载采用委托作为参数,并使用该参数将泛型类型的对象序列转换为特定类型。

下面的代码展示 Median 方法的重载,该重载将 Func<T,TResult> 委托作为参数。 此委托采用泛型类型 T 的对象,并返回类型 double 的对象。

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

现在,可以为任何类型的对象序列调用 Median 方法。 如果类型没有它自己的方法重载,必须手动传递委托参数。 在 C# 中,可以使用 lambda 表达式实现此目的。 此外,仅限在 Visual Basic 中,如果使用 Aggregate 或 Group By 子句而不是方法调用,可以传递此子句范围内的任何值或表达式。

下面的代码示例演示如何为整数数组和字符串数组调用 Median 方法。 对于字符串,将计算数组中字符串长度的中值。 该示例演示如何将 Func<T,TResult> 委托参数传递给每个用例的 Median 方法。

复制代码
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> 接口。 在这种情况下,该方法必须返回类型 IEnumerable<T> 的集合。 此类方法可用于将筛选器或数据转换应用于值序列。

下面的示例演示如何创建名为 AlternateElements 的扩展方法,该方法从集合中第一个元素开始按相隔一个元素的方式返回集合中的元素。

复制代码
// 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++;
    }
}
复制代码

可使用从 IEnumerable<T> 接口调用其他方法的方式对任何可枚举集合调用此扩展方法,如下面的代码中所示:

复制代码
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
复制代码

 

posted @   每天进步多一点  阅读(6)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· 2 本地部署DeepSeek模型构建本地知识库+联网搜索详细步骤
历史上的今天:
2020-01-21 Oracle 日期操作
点击右上角即可分享
微信分享提示