.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的實質仍是擴展方法。html

擴展方法

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

例如:測試

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. 若是不肯定到底使用仍是不使用擴展方法,那就不要用。this

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

System.Linq

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

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中htm

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擴展方法blog

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中的方法也就是那麼回事,沒有那麼難,我也能夠實現。

應評論的須要增長whereif,條件爲true執行左邊的條件,false執行右邊的條件,例如:data.WhereIf(x => x > 5, x => x + 10, x => x - 10)

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

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

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

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

    foreach (var item in source)
    {
        if (predicate(item))
        {
            yield return truePredicate(item);
        }
        else {
            yield return falsePredicate(item);
        }
    }
}

或者簡單的whereif,true執行條件,false不執行,例如:data.WhereIf(x => x > 5,true)

public static IEnumerable<TSource> WhereIf<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, bool condition)
{
    return condition ? source.Where(predicate) : source;
}

 其餘的linq文章

1. Linq的執行效率及優化

2. C#中linq

相關文章
相關標籤/搜索