zoukankan      html  css  js  c++  java
  • 委托发展史(Linq操作符)

    嗯~这篇就讲讲Linq吧!

    之前讲过Lambda最后进化到了令人发指的地步:

    Func<string, int> returnLength;
    returnLength = text => text.Length;
    
    Console.WriteLine(returnLength("Hellow"));

    来,把这个lambda表达式拿出来(text => text.Length),好眼熟啊,好像在某个地方用到过。

    *LINQ

    这个东西直到现在都是我见过的最好的表达式,他虽然不是银弹,但却是你开发"兵器库"中一个非常强大的兵器。

    Linq有两种写法,这篇来讲操作符。先说说组成哈。

    IEnumerable.linq操作符 (委托,方法,Lambda);

    例如:

    list<int>.Select(x => x + 1);

    Select:循环集合 条件是 集合里的每一个int + 1;

    来说说linq是怎么组成的吧。

    算了,直接看linq的源代码吧。Select的哈

    public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)

    静态方法有this说明是一个扩展方法,this在IEnumerable前,一个IEnumerable<TResult>的扩展方法,参数是一个Func<TSource, TResult>的泛型委托。

    由于参数是委托,所以linq的括号里可放入,符合条件的方法,一个类似的委托,或者Lambda表达式。

    其中我们最常用的就是Lambda表达式。因为是委托的终极进化,最简便的一个了。

    linq有一个延迟加载的特性,最初我以为是委托的原因(因为委托就是只有在执行Invoke的时候才执行),后来去网上查询资料。

    发现延迟加载完全拜一个 yield return 所实现的。

    来看一看这个诡异的东西吧。。。

    public IEnumerable<int> OutValue(List<int> list)
    {
         foreach (var a in list)
         {
            Console.WriteLine(a);
            yield return a;
         }
    }

    这是一个yield return返回IEnumerable的方法,内容很简单就是输出一下集合里的值,然后yield return了。

    然后我调用它的时候奇怪了。

    test.OutValue(list);

    输出:

    嗯~,换一个思路试一下,既然你不输出啥,那我再循环一次看看吧!

    var val = OutValue(list);
    
    foreach (var a in val){}

    我没有加任何操作,只是单纯的循环了一下。输出结果却是:

    哼哼,显而易见了。延迟加载拜这个yield 所致啊。

    如果我们要简单的实现一个Select的话,很简单:

      public static IEnumerable<TResult> Select<TSource,TResult>(this IEnumerable<TSource> source,Func<TSource,TResult> selector)
      {
          foreach (TSource element in source)
             yield return selector(element);
      }

    粗暴明了,当然源代码不是这样的。。。但意思差不多。foreach循环这个集合,yield return 执行这个selector 委托,委托实例是什么?就是你穿的Lambda,方法,委托啦~当然必须是满足条件的。

    yield 语句只能出现在 iterator 块中,该块可用作方法、运算符或访问器的体。

    当用到Select返回的值时,yield return 会去执行这个Func委托。这就是延迟加载的原理吗?(请各位大佬指正,小弟可能讲的不正确,欢迎指正哈。)

    看看别的操作符是怎么实现的

    *Max最大值

    [__DynamicallyInvokable]
    public static int Max(this IEnumerable<int> source)
    {
        if (source == null)
        {
            throw Error.ArgumentNull("source");
        }
        int num = 0;
        bool flag = false;
        foreach (int current in source)
        {
            if (flag)
            {
                if (current > num)
                {
                    num = current;
                }
            }
            else
            {
                num = current;
                flag = true;
            }
        }
        if (flag)
        {
            return num;
        }
        throw Error.NoElements();
    }

    Max只支持对int集合的扩展,内部是不是非常简单,就是生命一个int变量存最大值,一个bool类型来判断是否执行了这个foreach循环。

    来看看经常用的排序吧!

    *Groupby

    [__DynamicallyInvokable]
    public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
    {
        return new GroupedEnumerable<TSource, TKey, TSource>(source, keySelector, IdentityFunction<TSource>.Instance, null);
    }

    但最后执行的原理在这里。。。

    [__DynamicallyInvokable, IteratorStateMachine(typeof(Lookup<, >.<GetEnumerator>d__12))]
    public IEnumerator<IGrouping<TKey, TElement>> GetEnumerator()
    {
        Lookup<TKey, TElement>.Grouping next = this.lastGrouping;
        if (next != null)
        {
            do
            {
                next = next.next;
                yield return next;
            }
            while (next != this.lastGrouping);
        }
        yield break;
    }
    Lookup<Tkey,TValue>是.NET3.5中新增的,它类似与Dictionary<Tkey,TElement>,但把键映射带一个值集上.这个类在程序及System.Core中实现,用System,Linq命名空间定义.
    Grouping是最终实现排序的那个程序。取最后一个,判断非空,然后进行do{}while()循环。也是用了yield return延迟加载。

      *Where

    where里的委托与select是不一样的;
    public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    Where委托的返回值时bool类型,但扩展方法又要返回IEnumerable<TSource>类型的。
    大体上的意思就是这样的:
    public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
        foreach (TSource element in source)
        {
           if (predicate(element))
           {
              yield return element;
           }
        }
    }

    大致上是这样的,我理解的是.当然源代码不是这样的。当委托Func为true时才会yield return。也是有延迟加载的特性的。。。

    等等等。。。太多了就不一一列举了。

    那么会有人问了,我不想用这个延迟加载怎么办,我就想跟foreach循环一样,我就想立即加载。

    肯定是可以的,有要求肯定是会满足的。

    很简单,将返回值转换为另外一种类型即可,Linq的查询操作符中包含了转换操作的定义:ToArray(转换为数组)、ToDictionary(转换为字典)、ToList(转换为集合),使用这些转换运算就可以将延迟执行转换为立即执行,也就可以避免延迟执行了。

    *总结

    就写这么多吧。毕竟我的理解也是有限的,还请各位大佬们指正。

    有错误的地方欢迎各位大佬指出来,我会努力进步,努力改正的。

     给大家几段比较简单的linq的源码。

    SelectMany的源码:将序列的每个元素投影到 IEnumerable(T) 并将结果序列合并为一个序列。

    [IteratorStateMachine(typeof(Enumerable.<SelectManyIterator>d__17<, >))]
    private static IEnumerable<TResult> SelectManyIterator<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector)
    {
        foreach (TSource current in source)
        {
            foreach (TResult current2 in selector(current))
            {
                yield return current2;
            }
            IEnumerator<TResult> enumerator2 = null;
        }
        IEnumerator<TSource> enumerator = null;
        yield break;
    }

    简单的说一下下他的应用吧。看源代码应该能看出它是两个foreach,算了不说了直接上代码。

    //班级类
    public class Class
    {
        public List<Student> stuList { get; set; }  
    }
    //学生类
    public class Student
    {
        public string Name { get; set; }
        public string Sex { get; set; }
    }
    
    static void Main(string[] args)
    {
         List<Class> list = new List<Class>();
         //Select
         List<List<Student>> result = list.Select(m => m.stuList).ToList();
         //SelectMany
         List<Student>       result = list.SelectMany(m => m.stuList).ToList();  
    }

     Sum的源码:int集合求和

    [__DynamicallyInvokable]
    public static int Sum(this IEnumerable<int> source)
    {
        if (source == null)
        {
            throw Error.ArgumentNull("source");
        }
        int num = 0;
        checked
        {
            foreach (int current in source)
            {
                num += current;
            }
            return num;
        }
    }

    All的源码:All表示集合中的所有元素满足表达式条件,即返回true。

    [__DynamicallyInvokable]
    public static bool All<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
        if (source == null)
        {
            throw Error.ArgumentNull("source");
        }
        if (predicate == null)
        {
            throw Error.ArgumentNull("predicate");
        }
        foreach (TSource current in source)
        {
            if (!predicate(current))
            {
                return false;
            }
        }
        return true;
    }

    多谢观看!

  • 相关阅读:
    out/host/linuxx86/obj/EXECUTABLES/aapt_intermediates/aapt 64 32 操作系统
    linux 查看路由器 电脑主机 端口号 占用
    linux proc进程 pid stat statm status id 目录 解析 内存使用
    linux vim 设置大全详解
    ubuntu subclipse svn no libsvnjavahl1 in java.library.path no svnjavahl1 in java.library.path no s
    win7 安装 ubuntu 双系统 详解 easybcd 工具 不能进入 ubuntu 界面
    Atitit.json xml 序列化循环引用解决方案json
    Atitit.编程语言and 自然语言的比较and 编程语言未来的发展
    Atitit.跨语言  文件夹与文件的io操作集合  草案
    Atitit.atijson 类库的新特性设计与实现 v3 q31
  • 原文地址:https://www.cnblogs.com/yingxl/p/9009406.html
Copyright © 2011-2022 走看看