Core源码(四)IEnumerable

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

 Core源码(四)IEnumerable_第1张图片

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

 Core源码(四)IEnumerable_第2张图片

自己实现迭代器

  迭代器是通过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编译后生成的代码是相同的。

 Core源码(四)IEnumerable_第3张图片

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

Core源码(四)IEnumerable_第4张图片

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

 Core源码(四)IEnumerable_第5张图片

我们还记得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到底为我们做了什么呢?

Core源码(四)IEnumerable_第6张图片

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

 Core源码(四)IEnumerable_第7张图片

这就是所谓的状态机吧!

  我们调用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;
            }
        }
    }
}

本文参考《农码一生》

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

 

 

 

 

 

 

 

 

你可能感兴趣的:(Core源码(四)IEnumerable)