C#迭代器与LINQ查询操作符

简单实例:

static void Main(string[] args)
        {
            foreach (var number in EvenSequence(1,5))
            {
                Console.WriteLine(number);
            }
        }

        public static IEnumerable EvenSequence(int firstNumber,int lastNumber)
        {
            for (int number = firstNumber ; number <= lastNumber; number++)
            {
                if (number % 2 == 0)
                {
                    yield return number;
                }
            }
        }

迭代器方法或 get 访问器可对集合执行自定义迭代。 迭代器方法使用 yield return 语句返回元素,每次返回一个。 到达 yield return 语句时,会记住当前在代码中的位置。 下次调用迭代器函数时,将从该位置重新开始执行。


手动实现一个繁琐的迭代器:
假设实现一个基于环形缓冲的集合类型,实现IEbunmerable接口,用户可以获得集合中全部元素。
我们提供一个设置值和起始点的构造函数

            object[] values = { "a", "b", "c" };
            IterationSample collection = new IterationSample(values, 1);
            foreach (object col in collection)
            {
                Console.WriteLine(col);
            }

IterationSample类继承IEnumerable的实现

class IterationSample : IEnumerable
    {
        public Object[] values;
        public Int32 startingPoint;
        public IterationSample(Object[] values,Int32 startingPoint)
        {
            this.values = values;
            this.startingPoint = startingPoint;
        }
        public IEnumerator GetEnumerator()
        {
            throw new NotImplementedException();
        }
    }

迭代器不是一次返回全部数据,而是一次请求一个数据,因此,我们需要记录客户请求到了集合的哪个记录。我们创建另一个类来实现迭代器本身,来保证GetEbumerator迭代对象的独立

class IterationSampleEnumerator : IEnumerator
    {
        IterationSample parent;
        Int32 position;
        internal IterationSampleEnumerator(IterationSample parent)
        {
            this.parent = parent;
            position = -1;
        }
        public bool MoveNext()
        {
            if (position != parent.values.Length)
            {
                position++;
            }
            return position < parent.values.Length;
        }
        public object Current
        {
            get
            {
                if (position == -1 || position == parent.values.Length)
                {
                    throw new InvalidOperationException();
                }
                Int32 index = position + parent.startingPoint;
                index = index % parent.values.Length;
                return parent.values[index];
            }
        }
        public void Reset()
        {
            position = -1;
        }
    }

迭代器:记录迭代的原始集合,记录当前游标,根据当前游标,和数组定义的起始位置设置迭代器在数组中的位置。初始化将当前迭代器设定在第一个元素之前,第一次调用迭代器首先调用MoveNext,然后调用Current.游标自增时进行条件判断。
IterationSimple类中的GetEnumerator中返回迭代类。


通过yield语句进行简化迭代

public IEnumerator GetEnumerator()
        {
            for (int index = 0; index < this.values.Length; index++)
            {
                yield return values[(index + startingPoint) % values.Length];
            }
        }

上面的语句简化了手动实现IEnumerator的过程,这条语句告诉编译器这不是一个简单的方法,而是执行一个迭代块(yield block),它返回一个IEnumerator对象,你能够使用迭代块来执行迭代方法并返回一个IEnumerator需要实现的类型。yield return 语句必须返回和块的最后的返回类型兼容的类型。迭代块实际上是让编译器为我们创建了一个状态机,它创建了一个实现状态机的内部类。这个类记住了我们迭代器的准确当前位置以及本地变量,包括参数。

如下的代码,展示了迭代器的执行流程

class Program
{
    static readonly String Padding = new String(' ', 30);
    static IEnumerable CreateEnumerable()
    {
        Console.WriteLine("{0} CreateEnumerable()方法开始", Padding);
        for (int i = 0; i < 3; i++)
        {
            Console.WriteLine("{0}开始 yield {1}", i);
            yield return i;
            Console.WriteLine("{0}yield 结束", Padding);
        }
        Console.WriteLine("{0} Yielding最后一个值", Padding);
        yield return -1;
        Console.WriteLine("{0} CreateEnumerable()方法结束", Padding);
    }

    static void Main(string[] args)
    {
        IEnumerable iterable = CreateEnumerable();
        IEnumerator iterator = iterable.GetEnumerator();
        Console.WriteLine("开始迭代");
        while (true)
        {
            Console.WriteLine("调用MoveNext方法……");
            Boolean result = iterator.MoveNext();
            Console.WriteLine("MoveNext方法返回的{0}", result);
            if (!result)
            {
                break;
            }
            Console.WriteLine("获取当前值……");
            Console.WriteLine("获取到的当前值为{0}", iterator.Current);
        }
        Console.ReadKey();
    }
}

如果只是想从一个方法中返回一些数据,那么使用IEnumerable。在Main中

IEnumerable iterable = CreateEnumerable();

这里是一个语法糖,CreateEnumerable直接返回一个继承IEnumerable的实例。
从迭代快调用顺序中可以看出:

  • 直到第一次调用MoveNext,实际方法才会被调用
  • 在调用MoveNext的时候,已经做好了所有操作,返回Current属性并没有执行任何代码

yield break 结束一个迭代
他能够马上结束迭代,使得下一次调用MoveNext返回false

参考资料:https://www.cnblogs.com/yangecnu/archive/2012/03/17/2402432.html


(1)使用
针对集合类型编写foreach代码块,都是在使用迭代器
集合类型实现了IEnumerable接口
都有一个GetEnumerator方法
(2)迭代器优点
假如要遍历一个庞大的集合,只要其中一个元素满足条件,据完成了任务。
(3)yield关键字
MSDN中:
在迭代器块中用于向枚举数对象提供值或发出迭代结束信号。
(4)注意事项:
1.在foreach循环式多考虑线程安全性,在foreach时不要试图对便利的集合进行remove和add操作,任何集合,即使被标记为线程安全,在foreach时,增加项和移除项都会导致异常。
2.IEnumerable接口是LINQ特性的核心接口
只有实现了IEnumerable接口的集合,才能执行相关的LINQ操作,比如select,where等

LINQ
1.查询操作符
(1)源起
.net的设计者在类库中定义了一系列拓展的方法,方便用户操作集合对象。
(2)使用
一些系列拓展方法,eg:Where,Max,Select,Sum,Any,Average,All,Concat等都是针对IEnumerable的对象进行拓展,

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _1.LINQLearn
{
    class Program
    {
        static void Main(string[] args)
        {
            List ltemp = new List { 1, 23, 4, 5, 6 };
            var res = ltemp
                .Where(a => { return a > 4; })
                .Sum();
            Console.WriteLine(res);
            Console.ReadKey();
        }
    }
}

<1>上面的代码中用了两个拓展方法。
Where拓展方法,需要传入一个Func类型的泛型委托,此泛型委托,需要一个int的输入参数和一个bool类型的返回值。
<2>Sum拓展方法计算了Where拓展方法返回的集合的和
(3)好处
上面的代码中
ltemp
.Where(a => { return a > 4; })
.Sum();
上面的一句可以完全写成
(from v in arr where v > 3 select v).Sum();
(4)标准查询操作符说明
<1>过滤
Where
用法:arr.Where(a=>{ return a>3; })
说明:找到集合中满足指定条件的元素
OfType
用法:arr.OfType()
说明:根据类型,筛选集合中的元素
<2>投影

Select
用法:arr.Select(a=>a.ToString());
说明:将集合中的每个元素投影到进的集合中。上例子中新集合是一个IEnumerable的集合
SelectMany
用法arr.SelectMany(a=>{return new List(){a.Tostring();}};

查询表达式
(1)源起
查询早做副表示扩张方法来操作及和,虽然已经比较方便,但可读性和代码的语义来考虑,仍有不足,于是产生了查询表达式的写法。虽然很像SQL,但是用本质不同
(2)用法
from v in arr where v>3 select v
(3)说明:
... ...
参考文档:
https://blog.csdn.net/snakorse/article/details/44171295

你可能感兴趣的:(C#迭代器与LINQ查询操作符)