• 扩展方法和Enumerable


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

    应评论的需要增加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

  • 相关阅读:
    HDU 4611 Balls Rearrangement 数学
    Educational Codeforces Round 11 D. Number of Parallelograms 暴力
    Knockout.Js官网学习(简介)
    Entity Framework 关系约束配置
    Entity Framework Fluent API
    Entity Framework DataAnnotations
    Entity Framework 系统约定配置
    Entity Framework 自动生成CodeFirst代码
    Entity Framework CodeFirst数据迁移
    Entity Framework CodeFirst尝试
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/11105145.html
Copyright © 2020-2023  润新知