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中的方法也就是那么回事,没有那么难,我也可以实现。