共用方式為


HOW TO:新增 LINQ 查詢的自訂方法

您可以將擴充方法加入至 IEnumerable<T> 介面,來擴充一組可用於 LINQ 查詢的方法。 例如,除了標準的平均值或最大值作業之外,您還可以建立自訂的彙總方法,從值序列中計算出單一值。 您也可以建立方法,用來當做值序列的自訂篩選條件或特定資料轉換,並傳回新的序列。 這類方法的範例有 DistinctSkip<TSource>Reverse<TSource>

當您擴充 IEnumerable<T> 介面時,可以將自訂方法套用至任何可列舉的集合。 如需詳細資訊,請參閱擴充方法 (C# 程式設計手冊)擴充方法 (Visual Basic)

加入彙總方法

彙總方法會從一組值計算出單一值。 LINQ 提供數個彙總方法,包括 AverageMinMax。 您可以將擴充方法加入至 IEnumerable<T> 介面,建立自己的彙總方法。

在下列程式碼範例中,會示範如何建立名為 Median 的擴充方法,計算型別為 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);
        }
    }
}

您可以對任何可列舉的集合呼叫這個擴充方法,其方式與您從 IEnumerable<T> 介面呼叫其他彙總方法一樣。

注意事項注意事項

在 Visual Basic 中,您可以使用 Aggregate 或 Group By 子句的方法呼叫或標準查詢語法。 如需詳細資訊,請參閱 Aggregate 子句 (Visual Basic)Group By 子句 (Visual Basic)

在下列程式碼範例中,會示範如何將 Median 方法用於型別為 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
*/

多載彙總方法以接受各種型別

您可以多載彙總方法,讓它接受各種型別的序列。 一般的方法是為每個型別建立多載, 而另一個方法則是建立可接受泛型型別的多載,再使用委派將此多載轉換為特定型別。 您也可以混合使用這兩種方法。

若要為每個型別建立多載

您可以為想要支援的每個型別建立特定多載。 下列程式碼範例示範 integer 型別之 Median 方法的多載。

' 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();
}

您現在可以呼叫 integer 及 double 型別的 Median 多載,如下列程式碼所示:

        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
*/

若要建立泛型多載

您也可以建立接受泛型物件序列的多載。 這個多載接受委派做為參數,並使用此委派將泛型型別物件序列轉換為特定型別。

下列程式碼示範接受 Func<T, TResult> 委派做為參數之 Median 方法的多載。 這個委派接受泛型型別 T 的物件,並傳回 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();
}

您現在可以對任何型別的物件序列呼叫 Median 方法。 如果型別本身沒有專屬方法多載,您就必須傳遞委派參數。 在 Visual Basic 和 C# 中,您可以使用 Lambda 運算式來達到這個目的。 此外,僅就 Visual Basic 而言,如果您使用 Aggregate 或 Group By 子句而非方法呼叫,就可以在這個子句的範圍內傳遞任何值或運算式。

在下列範例程式碼中,會示範如何針對整數陣列和字串陣列呼叫 Median 方法。 就字串的案例而言,程式碼會計算陣列中字串長度的中位數。 針對每個案例,範例會示範如何將 Func<T, TResult> 委派參數傳遞至 Median 方法。

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
*/

加入傳回集合的方法

您可以使用會傳回值序列的自訂查詢方法來擴充 IEnumerable<T> 介面。 在這種情況下,方法必須傳回 IEnumerable<T> 型別的集合。 您可以使用這類方法,將篩選條件或資料轉換套用至值的序列。

下列範例示範如何建立名為 AlternateElements 的擴充方法,該方法會從集合中的第一個項目開始,每隔一個項目傳回項目。

' 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;
}

您可以對任何可列舉的集合呼叫這個擴充方法,就像從 IEnumerable<T> 介面呼叫其他方法一樣,如下列程式碼所示:

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
*/

請參閱

參考

IEnumerable<T>

擴充方法 (C# 程式設計手冊)

概念

擴充方法 (Visual Basic)