zoukankan      html  css  js  c++  java
  • Core源码(四)IEnumerable

    首先我们去core的源码中去找IEnumerable发现并没有,如下

     

    Core中应该是直接使用.net中对IEnumerable的定义

     

    自己实现迭代器

      迭代器是通过IEnumerable和IEnumerator接口来实现的,今天我们也来尝试实现自己的迭代器。

      首先来看看这两个接口:

    internal interface IEnumerable
    {
        [DispId(-4)]
        System.Collections.IEnumerator GetEnumerator();
    }
    public interface IEnumerator
    {
        object Current { get; }
        bool MoveNext();
        void Reset();
    }

      并没有想象的那么复杂。其中IEnumerable只有一个返回IEnumerator的GetEnumerator方法。而IEnumerator中有两个方法加一个属性。

      接下来,我们继承IEnumerable接口并实现:

    public class MyIEnumerable : IEnumerable
    {
        private string[] strList;
        public MyIEnumerable(string[] strList)
        {
            this.strList=strList;
        }
        public IEnumerator GetEnumerator()
        {
            return new MyIEnumerator(strList);
        }
    }
    public class MyIEnumerator:IEnumerator
    {
        private string[] strList;
        private int position;
        public MyIEnumerator(string[] strList)
        {
            this.strList=strList;
            position=-1;
        }
        public object Current
        {
            get{ return strList[position];}
        }
        public bool MoveNext()
        {
            position++;
            if (position<strList.Length)
            {
                return true;
            }
            return false;
        }
        public void Reset()
        {
            position=-1;
        }
    }

    下面使用原始的方式调用:

    static void Main(string[] args)
    {
        string[] strList=new string[]{"123","1231"};
        MyIEnumerable my =new MyIEnumerable(strList);
        var enumerator=my.GetEnumerator();
        while (enumerator.MoveNext())
        {
            Console.WriteLine(enumerator.Current);
            //enumerator.Current="";  这会报错
        }
        Console.WriteLine("-------------------------------");
        foreach (var item in my)
        {
            Console.WriteLine(item);
        }
    }

    这两种取值方式基本等效,因为实际clr编译后生成的代码是相同的。

     

    由此可见,两者有这么个关系:

    我们可以回答一个问题了“为什么在foreach中不能修改item的值?”:

     

    我们还记得IEnumerator的定义吗,接口的定义就只有get没有set。所以我们在foreach中不能修改item的值。

    yield的使用

      你肯定发现了我们自己去实现IEnumerator接口还是有些许麻烦,并且上面的代码肯定是不够健壮。对的,.net给我们提供了更好的方式。

    public IEnumerator GetEnumerator()
    {
        //return new MyIEnumerator(strList);
        for (int i = 0; i < strList.Length; i++)
        {
            yield return strList[i];
        }
    }

    你会发现我们连MyIEnumerator都没要了,也可以正常运行。太神奇了。yield到底为我们做了什么呢?

    好家伙,我们之前写的那一大坨。你一个yield关键字就搞定了。最妙的是这块代码:

     

    这就是所谓的状态机吧!

      我们调用GetEnumerator的时候,看似里面for循环了一次,其实这个时候没有做任何操作。只有调用MoveNext的时候才会对应调用for循环。

    为什么Linq to Object中要返回IEnumerable?

    因为IEnumerable是延迟加载的每次访问的时候才取值。也就是我们在Lambda里面写的where、select并没有循环遍历(只是在组装条件)只有在ToList或foreache的时候才真正去集合取值了。这样大大提高了性能。

    自己实现MyWhere:

    public class MyIEnumerable : IEnumerable
    {
        private string[] strList;
        public MyIEnumerable(string[] strList)
        {
            this.strList=strList;
        }
        public IEnumerator GetEnumerator()
        {
            //return new MyIEnumerator(strList);
            for (int i = 0; i < strList.Length; i++)
            {
                yield return strList[i];
            }
        }
        public IEnumerable<string> MyWhere(Func<string,bool> func)
        {
            foreach (string item in this)
            {
                if (func(item))
                {
                    yield return item;
                }
            }
        }
    }

    FirstOrDefault的实现

    内部调用了TryGetFirst。

    private static TSource TryGetFirst<TSource>(this IEnumerable<TSource> source, out bool found)
    {
        if (source == null)
        {
            throw Error.ArgumentNull(nameof(source));
        }
    
        if (source is IPartition<TSource> partition)
        {
            return partition.TryGetFirst(out found);
        }
    
        if (source is IList<TSource> list)
        {
            if (list.Count > 0)
            {
                found = true;
                return list[0];
            }
        }
        else
        {
            using (IEnumerator<TSource> e = source.GetEnumerator())
            {
                //同样调用了MoveNext方法
                if (e.MoveNext())
                {
                    found = true;
                    //Current属性在我们的自定义实现里面也有出现
                    return e.Current;
                }
            }
        }
    
        found = false;
        return default(TSource);
    }
    不传入筛选的实现
    private static TSource TryGetFirst<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, out bool found)
    {
        if (source == null)
        {
            throw Error.ArgumentNull(nameof(source));
        }
    
        if (predicate == null)
        {
            throw Error.ArgumentNull(nameof(predicate));
        }
    
        if (source is OrderedEnumerable<TSource> ordered)
        {
            return ordered.TryGetFirst(predicate, out found);
        }
    
        foreach (TSource element in source)
        {
            //循环,直接返回第一个符合条件的对象
            if (predicate(element))
            {
                found = true;
                return element;
            }
        }
    
        found = false;
        return default(TSource);
    }
    传入筛选的实现

    源码地址

    https://gitee.com/qixinbo/MyKestrelServer/tree/master/DataStruct/EnumerableStudy

    本文参考《农码一生》

    https://www.cnblogs.com/zhaopei/p/5769782.html

  • 相关阅读:
    迷宫
    小猫
    数位DP模板
    摘桃子
    [USACO10HOL]牛的政治Cow Politics
    ArcEngine中合并断开的线要素(根据几何判断)
    ArcEngine环境下合并断开的线要素(根据属性)
    Web Service和WCF的到底有什么区别
    OGC 的 WMS、WFS 及WCS服务(转)
    ArrayList的使用方法详解(转)
  • 原文地址:https://www.cnblogs.com/qixinbo/p/11705197.html
Copyright © 2011-2022 走看看