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;
    }

    多谢观看!

  • 相关阅读:
    嵌入式整体框架——总结
    DSP Bios记忆
    三遥
    usb设备 配置 接口 端点
    ARM, MIPS, Power PC的比较
    STM32 IAP
    FSMC 总结
    BCD码与十进制的相互转换
    读 “cortexM3” 权威指南 小记(一)
    crc校验码的16 32位 查表法 算法记载
  • 原文地址:https://www.cnblogs.com/yingxl/p/9009406.html
Copyright © 2011-2022 走看看