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中返回迭代类。【C#迭代器与LINQ查询操作符】通过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属性并没有执行任何代码
他能够马上结束迭代,使得下一次调用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
<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
说明:将集合中的每个元素投影到进的集合中。上例子中新集合是一个IEnumerable的集合
SelectMany
用法arr.SelectMany
查询表达式
(1)源起
查询早做副表示扩张方法来操作及和,虽然已经比较方便,但可读性和代码的语义来考虑,仍有不足,于是产生了查询表达式的写法。虽然很像SQL,但是用本质不同
(2)用法
from v in arr where v>3 select v
(3)说明:
... ...
参考文档:
https://blog.csdn.net/snakorse/article/details/44171295
推荐阅读
- JAVA(抽象类与接口的区别&重载与重写&内存泄漏)
- Docker应用:容器间通信与Mariadb数据库主从复制
- 《真与假的困惑》???|《真与假的困惑》??? ——致良知是一种伟大的力量
- 第326天
- Shell-Bash变量与运算符
- 人生感悟记#环境仪器宋庆国成长记#072
- 标签、语法规范、内联框架、超链接、CSS的编写位置、CSS语法、开发工具、块和内联、常用选择器、后代元素选择器、伪类、伪元素。
- 逻辑回归的理解与python示例
- Guava|Guava RateLimiter与限流算法
- 我和你之前距离