如何在C#中为LINQ查询添加自定义方法

363 阅读5分钟

在C#中为LINQ查询添加自定义方法

通过为IEnumerable接口引入扩展方法,你可以扩展用于LINQ查询的方法集。例如,你可以通过在SQL Server中创建你的自定义聚合方法将任何数据序列转换成一个值。

你也可以建立一个方法,返回一个新的数值系列,并作为一个自定义过滤器或一个专门的数据转换,用于变量序列。

为LINQ查询添加自定义方法(C#)

简介

一些例子包括Distinct和Skip技术,以及Reverse技术。

当你扩展IEnumerable接口时,你可以将你的方法应用于任何可枚举的集合。在这篇文章中,我们将看看C#中用于LINQ查询的各种自定义方法。

添加一个聚合方法

当一组数值被聚合时,可以生成一个单一的数值。平均值、最小值和最大值只是LINQ中可用的几种聚合技术。通过向IEnumerable接口引入一个扩展方法,你可以设计你的聚合方法。

中位数是一个扩展方法,它可以计算出一串双值数的中位数。

public static class LINQExtensionExample
{
    public static double Median(this IEnumerable<double>? source)
    {
        if (!(source?.Any() ?? false))
        {
            throw a new InvalidOperationException("A null or empty set cannot be used to compute the median..");
        }

        var sortedList = (from numberX in source
                          orderby numberX
                          select numberX).ToList();

        int itemIndex = sortedList.Count / 2;

        if (sortedList.Count % 2 == 0)
        {
            // Rerurning an even number of objects or items
            return (sortedList[itemIndex] + sortedList[itemIndex - 1]) / 2;
        }
        else
        {
            // Rerurning an odd number of objects or items
            return sortedList[itemIndex];
        }
    }
}

从IEnumerable接口调用这个扩展方法和调用其他聚合函数没有任何区别。

在一个双数组上,下面的代码演示了使用中位数方法。

double[] numbers1 = { 0.9, 5, 2, 9.3, 2.6, 7, 3.3, 4 };

var query = numbers1.Median();

Console.WriteLine("double: Median = " + query1);
/*
 The output of the above code is as shown below

 Double: Median = 5.95
*/

一个聚合方法通过接受各种类型而被重载

为了接受不同类型的序列,你可以重载你的聚合方法。重载每个类型是典型的技术。然而,也可以使用一个委托来为一个通用类型建立重载。将这两种方法结合起来也是一种选择。

建立一个类型重载

我们可以为我们想要支持的每一种类型创建一个单独的重载。这里有一个Median方法的int类型重载,你可以在下面的代码例子中看到。

//Overload of int
public static double Median(this IEnumerable<int> source) =>
    (from numX in source select (double)numX).Median();
With the new Median overloads available for both integer and double types, the following code shows how to use them:
double[] numbers2 = {  0.9, 5, 2, 9.3, 2.6, 7, 3.3, 4};

var query2 = numbers2.Median();

Console.WriteLine("double: Median = " + query2);

int[] numbers3 = { 14, 16, 15, 13, 11, 10, 9 };

var query3 = numbers3.Median();

Console.WriteLine("int: Median = " + query3);
/*
 Here's what it looks like when it runs:

 The Double part: Median = 5.95
 The Integer part: Median = 13
*/

启动一个泛型溢出

如果你想这么做,也可以用一个泛型对象的列表作为重载的输入。在这个重载中,Delegate是作为一个变量提供的,它使用Delegate将泛型对象转换为一个特定的类型。

下面的代码显示了Median的Func T, TResult委托的一个重载。一个通用类型T的对象被传递给这个委托,它返回一个双倍类型的对象作为结果。

// Let us take a look
public static double Median<T>(this IEnumerable<T> numbersY,
                       Func<T, double> selector) =>
    (from num in numbersY select selector(num)).Median();

当处理任何类型的对象序列时,你现在可以使用中值技术。如果该类型没有其重载的过程,我们必须传入一个委托参数。C#中的Lambda表达式可用于此目的。使用Aggregate或Group By子句来代替方法调用,只有在Visual Basic中才是真的。

你可以使用下面的代码在一个字符串数组和一个整数数组上调用Median方法。使用一个字符串数组,为数组中每个字符串的长度得出一个中值。

在代码中的每个场景都使用Median方法的委托参数。在每个使用该方法的场景中,为FuncT, TResult使用Median方法的委托参数。

int[] numbers4 = { 14, 16, 15, 13, 11, 10, 9 };

/*
   In this case, the compiler will implicitly convert num=>num's value to double when you pass it as a parameter to the Median method.
   Otherwise, the compiler will give the user an error message.
*/
var query4 = numbers4.Median(num => num);

Console.WriteLine("int: Median = " + query3);

string[] numbers5 = { "fourteen", "sixteen", "fifteen", "thirteen", "eleven", "ten", "nine" };

// A number of object properties are available with the generic overload.

var query5 = numbers5.Median(str => str.Length);

Console.WriteLine("String: Median = " + query5);

/*
Here's what it looks like when it runs:
 Integer: Median = 13
 String: Median = 13
*/

添加一个返回序列的方法

IENumerableT可以用自定义的查询方法进行扩展,以返回一个序列的值。在这种情况下必须返回一个IEnumerableT的集合。使用这些方法可以将过滤器或数据转换应用到一个值的序列。

使用AlternateElements扩展方法,可以返回一个集合中的每一个其他元素,从第一个元素开始。

//The IEnumerableT> interface has an extension method.
// Every other element in a sequence is returned by the method.
public static IEnumerable<T> AlternateElements<T>(this IEnumerable<T> source)
{
    int y = 3;
    foreach (var element in source)
    {
        if (y % 2 == 1)
        {
            yield return element;
        }
        y++;
    }
}

这个函数可以用来扩展任何可枚举的集合。

string[] strings = { "u", "v", "w", "x" };

var query4 = strings.AlternateElements();

foreach (var element in query6)
{
    Console.WriteLine(element);
}
/*
Here's what it looks like when it runs:
 u
 w
 
*/

作为一个实际的例子,让我们看一下下面的例子。这个例子使用JSON来序列化一些数据库。然后用JArray.Parse将存储一些数据,之后我们再将JSON应用于LINQ查询来读取数据。

using System;
using System.Linq;
 
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
 
namespace JSON_LINQ_TOJSON
{
class ProgramExample
{
    static void Main(string[] args)
    {
       
        // Here you are going to get the data in JSON serialized form
        
        string WorkersData = JsonConvert.SerializeObject(new WorkersDatabase(),Formatting.Indented);
        
 
        
        // This will convert the JSON string into an array
        JArray workersArray = JArray.Parse(WorkersData);
 
 
    
       // The total number of workers is read
 
        var resWorkers = (from w in workersArray
                           select w["WorkersName"]).ToList();
 
        Console.WriteLine("Only Workers Names");
        foreach (var item in resWorkers)
        {
            Console.WriteLine(item.Value<string>().ToString());
        }
 
        // Then one will get the work or the Job details
        Console.WriteLine();
        var result = (from w in workersArray.Workers()["Job"]
                      select w).ToList();
 
        Console.WriteLine("Job Details");
        foreach (var item in result.Wokers().ToList())
        {
            Console.WriteLine(item.ToObject<Job>().JobId + "\t" + item.ToObject<Job>().JobType);
        }
          Console.ReadLine();
    }
}
}

总结

我们已经看到我们如何在我们的C#应用程序中利用LINQ查询。开发一个从API中检索JSON数据的应用程序,使用LINQ查询来查询数据。