.NET中擴展方法和Enumerable(System.Linq)

LINQ是我最喜歡的功能之一,程序中到處是data.Where(x=x>5).Select(x)等等的代碼,她使代碼看起來更好,更容易編寫,使用起來也超級方便,foreach使循環更加容易,而不用for int..,linq用起來那麼爽,那麼linq內部是如何實現的?我們如何自定義linq?我們這裏說的linq不是from score in scores  where score > 80 select score;而是System.Linq哦。了解Ling之前先要了解擴展方法,因為linq的實質還是擴展方法。

擴展方法

擴展方法使你能夠向現有類型“添加”方法,而無需創建新的派生類型、重新編譯或以其他方式修改原始類型。 擴展方法是一種特殊的靜態方法,但可以像擴展類型上的實例方法一樣進行調用。 

例如:

namespace ExtensionMethods
{
    public static class MyExtensions
    {
        public static int WordCount(this string str)
        {
            return str.Split(new char[] { ' ', '.', '?' }, 
                             StringSplitOptions.RemoveEmptyEntries).Length;
        }
    }   
}
//添加引用
using ExtensionMethods;  
//使用
string s = "Hello Extension Methods";  
int i = s.WordCount();  

微軟擴展方法建議

微軟MSDN上的建議:通常,建議只在不得已的情況下才實現擴展方法,並謹慎地實現。只要有可能,都應該通過創建從現有類型派生的新類型來達到這一目的。

擴展方法建議

1. 當功能與擴展類型最相關時,可以考慮使用擴展方法。
2. 當對第三方庫進行擴充的時候,可以考慮使用擴展方法。
3. 當您不希望將某些依賴項與擴展類型混合使用時,可以使用擴展方法來實現關注點分離。
4. 如果不確定到底使用還是不使用擴展方法,那就不要用。

擴展方法是C#語言的一個很好的補充,她使我們能夠編寫更好,更容易讀的代碼,但是也應該小心使用,不恰當的使用擴展方法可能導致可讀性降低,使測試困難,容易出錯。

System.Linq

System.Linq用起來那麼好,她內部是如何實現的,當然是查看源碼了。

Where源碼

public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
    if (source == null) throw Error.ArgumentNull("source");
    if (predicate == null) throw Error.ArgumentNull("predicate");
    if (source is Iterator<TSource>) return ((Iterator<TSource>)source).Where(predicate);
    if (source is TSource[]) return new WhereArrayIterator<TSource>((TSource[])source, predicate);
    if (source is List<TSource>) return new WhereListIterator<TSource>((List<TSource>)source, predicate);
    return new WhereEnumerableIterator<TSource>(source, predicate);
}

這個方法就是一個擴展方法,對數據進行了處理,具體的處理都是在對象中的MoveNext中

public override bool MoveNext() {
    if (state == 1) {
        while (index < source.Length) {
            TSource item = source[index];
            index++;
            if (predicate(item)) {
                current = item;
                return true;
            }
        }
        Dispose();
    }
    return false;
}

可以看出就是一個循環處理,如果你覺得還是不清楚,可以看WhereIterator方法

static IEnumerable<TSource> WhereIterator<TSource>(IEnumerable<TSource> source, Func<TSource, int, bool> predicate) {
    int index = -1;
    foreach (TSource element in source) {
        checked { index++; }
        if (predicate(element, index)) yield return element;
    }
}

這下明白了,linq就是擴展方法,對數據進行處理,返回所需要的數據,知道了原理之後,可以寫自己的linq擴展方法了。
我想寫一個帶有控制台輸出的Where擴展方法

public static IEnumerable<TSource> WhereWithLog<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    if (source == null)
    {
        throw new ArgumentNullException("source", "不能為空");
    }

    if (predicate == null)
    {
        throw new ArgumentNullException("predicate", "不能為空");
    }


    int index = 0;
    foreach (var item in source)
    {
        Console.WriteLine($"Where item:{item},結果:{predicate(item)}");
        if (predicate(item))
        {
            yield return item;
        }
        index++;
    }
}

實現一個打亂數據的擴展方法,這裏的方法用了約束,只能是值類型。

public static IEnumerable<T> ShuffleForStruct<T>(this IEnumerable<T> source) where T : struct
{
    if (source == null)
        throw new ArgumentNullException("source", "不能為空");

    var data = source.ToList();
    int length = data.Count() - 1;
    for (int i = length; i > 0; i--)
    {
        int j = rd.Next(i + 1);
        var temp = data[j];
        data[j] = data[i];
        data[i] = temp;
    }

    return data;
}

到此為止是不是覺得Enumerable中的方法也就是那麼回事,沒有那麼難,我也可以實現。

【精選推薦文章】

智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

想知道網站建置、網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計及後台網頁設計

帶您來看台北網站建置台北網頁設計,各種案例分享

廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益