C# 迭代器解析

迭代器是一种特殊类型的对象,可以遍历集合对象中的元素,不必在代码中显式使用for或while循环。迭代器通常由 IEnumerable 和 IEnumerator 接口共同实现。。

源码:

ienumerable.cs (microsoft.com)https://referencesource.microsoft.com/#mscorlib/system/collections/ienumerable.cs

ienumerator.cs (microsoft.com)https://referencesource.microsoft.com/#mscorlib/system/collections/ienumerator.cs

IEnumerable 接口只定义了一个方法 GetEnumerator(),该方法返回一个迭代器对象,该对象实现了 IEnumerator 接口。

    public interface IEnumerable
    {
        // Interfaces are not serializable
        // Returns an IEnumerator for this enumerable Object.  The enumerator provides
        // a simple way to access all the contents of a collection.

        IEnumerator GetEnumerator();
    }

//枚举器提供了一种访问集合的所有内容的简单方法。

IEnumerator 接口定义了三个方法: MoveNext() 在迭代器中将游标移动到下一个元素; Current 返回集合内位于游标当前位置的元素;Reset() 将游标返回到集合中的第一个元素之前;

    public interface IEnumerator
    {
        // Interfaces are not serializable
        // Advances the enumerator to the next element of the enumeration and
        // returns a boolean indicating whether an element is available. Upon
        // creation, an enumerator is conceptually positioned before the first
        // element of the enumeration, and the first call to MoveNext 
        // brings the first element of the enumeration into view.
        // 
        bool MoveNext();
    
        // Returns the current element of the enumeration. The returned value is
        // undefined before the first call to MoveNext and following a
        // call to MoveNext that returned false. Multiple calls to
        // GetCurrent with no intervening calls to MoveNext 
        // will return the same object.
        // 
        Object Current {
            get; 
        }
    
        // Resets the enumerator to the beginning of the enumeration, starting over.
        // The preferred behavior for Reset is to return the exact same enumeration.
        // This means if you modify the underlying collection then call Reset, your
        // IEnumerator will be invalid, just as it would have been if you had called
        // MoveNext or Current.
        // 如果修改了基础集合,然后调用Reset,则IEnumerator将无效
        void Reset();
    }

迭代器的使用:

1.C#里的集合(Array;List;Dictionary;Queue;Stack;HashSet...)都实现了迭代器(实现IEnumerable接口和IEnumerator接口);实现了迭代器的集合可以使用foreach来遍历访问。

2.异步编程(使用Task和async/await关键字来实现异步调用)

3.LINQ

4.Unity协程

*for循环是不需要使用迭代器来遍历的,它通过创建和维护一个索引变量并使用索引来获取集合中的元素。

*foreach循环需要使用迭代器来遍历集合中的元素,它使用GetEnumerator()方法来获取集合的迭代器,然后使用MoveNext和Current来遍历集合中的元素。

下面是foreach循环简化版的代码:

//获取迭代器并且遍历
IEnumerator enumerator = myCollection.GetEnumerator();
while (enumerator.MoveNext())
{
    object item = enumerator.Current;
    
    func() {

         ...自定义的方法... 
        }

}
enumerator.Dispose();

使用迭代器来实现异步编程:

// 定义一个返回IEnumerable<Task<string>> 类型的迭代器方法
public static IEnumerable<Task<string>> GetAsyncTasks(string[] urls)
{
    using (HttpClient httpClient = new HttpClient())
    {
        foreach (var url in urls)
        {
            // 发送异步请求
            Task<string> task = httpClient.GetStringAsync(url);
            // 将异步任务通过yield return语句返回  
            yield return task;
        }
    }
}

// 使用foreach语句遍历异步任务
public static async Task Main(string[] args)
{
    string[] urls = { "https://www.example.com", "https://www.google.com", "https://www.microsoft.com" };
    foreach (var task in GetAsyncTasks(urls))
    {
        // 等待每个异步任务结束
        string result = await task;
        Console.WriteLine(result);
    }
}

//使用yield return关键字将每个异步请求任务返回;
//使用foreach语句遍历每个异步任务
//并使用await关键字等待异步任务完成;
//这样就可以实现协作式异步调用,通过迭代器来逐个执行异步任务。

使用迭代器实现C#的LINQ的例子:(实现简单的Where和Select方法)

using System;
using System.Collections.Generic;

public static class LinqExtensions
{
    // 定义一个扩展方法Where,用于过滤数据
    public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
        foreach (TSource item in source)
        {
            if (predicate(item))
            {
                yield return item;
            }
        }
    }

    // 定义一个扩展方法Select,用于投影数据
    public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
    {
        foreach (TSource item in source)
        {
            yield return selector(item);
        }
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        int[] nums = { 1, 2, 3, 4, 5 };

        // 使用迭代器实现简单的Where和Select方法
        var result = nums.Where(n => n % 2 == 0)
                         .Select(n => n * n);

        // 输出结果
        foreach (var item in result)
        {
            Console.WriteLine(item);
        }
    }
}

//使用了扩展方法Where和Select,
//使用yield return语句将每次遍历的数据元素传递给下一次操作,
//实现了对数据的过滤和投影操作。
//这样,我们就可以通过组合这些操作来构建LINQ查询,而不必显式地创建中间集合,从而提高代码的性能表现。

//需要注意的是,在实现迭代器的操作时,需要正确处理资源的释放和异常处理,以免出现内存泄漏等问题。
//另外,需要充分考虑数据可枚举性和延迟执行等问题,以便实现最佳的代码性能和效率。

猜你喜欢

转载自blog.csdn.net/weixin_40695640/article/details/130326306