迭代器块的正确用法

本文关键字:用法 迭代器 | 更新日期: 2023-09-27 18:23:59

我之前重构了一些代码,遇到了一个我不太确定的迭代器块的实现。在系统的集成层中,客户端正在为某些数据调用外部API,我有一组翻译器,它们将从API返回的数据转换为逻辑层中使用的业务实体集合。一个常见的翻译器类如下所示:

// 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
           };
}

今天我遇到了以下代码。同样,它是一个转换器类,其目的是将参数中的集合转换为方法返回类型。然而,这一次它是一个迭代器块:

// 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
        };
    }
}

我的问题是:这是迭代器块的有效使用吗?我看不出以这种方式创建翻译类有什么好处。这会导致一些意想不到的行为吗

迭代器块的正确用法

您的两个样本做的事情几乎完全相同。查询版本将被重写为对Select的调用,Select的编写方式与第二个示例完全相同;它对源集合中的每个元素进行迭代,并返回一个转换后的元素。

这是对迭代器块的一种非常有效的使用,当然不再需要像这样编写自己的迭代器区块,因为您只需要使用Select

是的,这是有效的。foreach具有可调试的优点,所以我倾向于使用这种设计。

第一个例子不是迭代器。它只是创建并返回一个IEnumerable<MyBusinessEnt>

第二个是迭代器,我认为它没有任何问题。每次调用方迭代该方法的返回值时,yield都会返回一个新元素。

是的,这很好,结果非常相似。

两者都创建了一个能够返回结果的对象。两者都依赖于可枚举的源保持完整,直到结果完成(或缩短)。两者都使用延迟执行,即在迭代结果时一次创建一个对象。

不同之处在于,第一个返回使用库方法生成枚举器的表达式,而第二个创建自定义枚举器。

每个代码运行时,的主要区别第一个延迟到迭代返回值,而第二个立即运行我的意思是for循环正在强制运行迭代。该类公开了一个IEnumerable<T>,并且在这种情况下被延迟,这是另一回事。

这与简单的Select相比没有任何好处。yield的真正力量是当涉及一个条件时:

foreach(var ent in ents)
{
    if(someCondition)
    yield return new MyBusinessEnt {
        Id = ent.Id,
        Code = ent.Code
    };
}