C# 迭代器块的正确使用
我之前在重构一些代码时,遇到了一个迭代器块的实现,我对此不太确定。在客户机为某些数据调用外部API的系统的集成层中,我有一组转换器,这些转换器将从API返回的数据转换为逻辑层中使用的业务实体集合。常见的translator类如下所示:C# 迭代器块的正确使用,c#,.net,iterator,C#,.net,Iterator,我之前在重构一些代码时,遇到了一个迭代器块的实现,我对此不太确定。在客户机为某些数据调用外部API的系统的集成层中,我有一组转换器,这些转换器将从API返回的数据转换为逻辑层中使用的业务实体集合。常见的translator类如下所示: // translate a collection of entities coming back from an extrernal source into business entities public static IEnumerable<MyBus
// translate a collection of entities coming back from an extrernal source into business entities
public static IEnumerable<MyBusinessEnt> Translate(IEnumerable<My3rdPartyEnt> ents) {
// for each 3rd party ent, create business ent and return collection
return from ent in ents
select new MyBusinessEnt {
Id = ent.Id,
Code = ent.Code
};
}
//将从外部源返回的实体集合转换为业务实体
公共静态IEnumerable Translate(IEnumerable ents){
//对于每个第三方ent,创建业务ent和退货托收
从耳鼻喉科返回耳鼻喉科
选择新的MyBusinessEnt{
Id=ent.Id,
代码=ent.Code
};
}
今天我遇到了以下代码。同样,它是一个translator类,其目的是将参数中的集合转换为方法返回类型。但是,这次它是一个迭代器块:
// same implementation of a translator but as an iterator block
public static IEnumerable<MyBusinessEnt> Translate(IEnumerable<My3rdPartyEnt> ents) {
foreach(var ent in ents)
{
yield return new MyBusinessEnt {
Id = ent.Id,
Code = ent.Code
};
}
}
//与转换器的实现相同,但作为迭代器块
公共静态IEnumerable Translate(IEnumerable ents){
foreach(在ents中的变量)
{
收益回报新业务{
Id=ent.Id,
代码=ent.Code
};
}
}
我的问题是:这是迭代器块的有效使用吗?我看不出以这种方式创建translator类的好处。这会导致一些意外行为吗?是的,这是有效的。foreach的
foreach
具有可调试的优点,因此我倾向于采用这种设计。第一个示例不是迭代器。它只是创建并返回一个IEnumerable
第二个是迭代器,我看不出有什么问题。每次调用方迭代该方法的返回值时,
yield
将返回一个新元素。当每个代码运行时,主要差异将启用第一个被延迟,直到返回值被迭代,而第二个立即运行。我的意思是for循环正在强制运行迭代。类公开了一个IEnumerable
,在本例中是延迟的,这是另一回事
这与简单的Select
相比没有任何好处<代码>收益率的真正力量是当涉及到一个条件时:
foreach(var ent in ents)
{
if(someCondition)
yield return new MyBusinessEnt {
Id = ent.Id,
Code = ent.Code
};
}
是的,效果很好,结果非常相似 两者都创建一个能够返回结果的对象。两者都依赖于源可枚举项保持不变,直到结果完成(或缩短)。两者都使用延迟执行,即在迭代结果时一次创建一个对象
不同之处在于,第一个示例返回一个使用库方法生成枚举数的表达式,而第二个示例创建一个自定义枚举数。您的两个示例执行的操作几乎完全相同。查询版本将被重写为Select调用,Select编写方式与第二个示例完全相同;它迭代源集合中的每个元素,并返回一个转换后的元素
这是迭代器块的一种完全有效的用法,尽管当然不再需要像这样编写自己的迭代器块,因为您可以只使用
Select
对我来说似乎完全有效-它提供了两个实体之间的编译安全转换。它有什么问题?因此迭代器块可能会给任何试图在系统中稍后调试foreach的人带来一些混乱?我不认为一定会有任何混乱;foreach
块可以更轻松地定位正在翻译的行项目。如果翻译变得非常琐碎,这允许进行更简单的调试。不过,这两种方法都是完美的find实现。请稍等——与简单的Where后跟Select相比,您的版本没有任何好处。是的,Chris Shouts的评论是正确的;两者都推迟执行。@EricLippert是的,因为他使用的是循环而不是选择。但正如我所说,在这种情况下,我只会使用普通Linq。你确定吗?我认为两者都是按需有效地创建了它们的值-即第二个值不会立即运行。@Aliostad:第一个值会立即返回一个对象,该对象会延迟其执行,直到调用MoveNext为止。第二个返回一个对象,该对象将延迟其执行,直到调用MoveNext。它们都使用延迟执行。但是是的,有“两个跃点”可以从第一个跃点中获取数据,而在第二个跃点中只有“一个跃点”。性能差异可能很小。@Aliostad:我假设下一票是“第二个立即运行”。该语句很容易解释为第二个没有延迟执行。好的,但在本例中不需要创建自定义迭代器,对吗?正如@Aliostad指出的,迭代器块最好与条件语句一起使用。因此,第二种方法的可读性\可调试性可能稍差,因此第一种方法可能更适合这种情况。另外,区别在于,自定义枚举器立即运行,而第一种方法(从该方法中选择此方法)运行延迟。@Aliostad-我不太明白。。。因此,第一个示例返回一个尚未枚举的集合。第二个是每次调用时枚举返回集合?“是这样吗?”詹姆斯刘易斯:林克处理条件句很好。这就是的作用。
的作用。@jameslewis:Theyield return
基本上暂停方法中的代码并返回项目,然后当您请求下一个项目时,代码将从该点开始循环,直到再次到达yield return
。因此,代码不会在每次需要时都枚举源代码