转换此迭代器块如何进行功能更改? [英] How is transforming this iterator block a functional change?

查看:29
本文介绍了转换此迭代器块如何进行功能更改?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给定以下代码片段:

public class Foo
{
    public IEnumerable<string> Sequence { get; set; }
    public IEnumerable<string> Bar()
    {
        foreach (string s in Sequence)
            yield return s;
    }
}

下面的代码片段在语义上是等价的,还是不同的?如果不同,它们的功能有何不同?

is the following snippet semantically equivalent, or is it different? If it is different, how do they function differently?

public class Foo2
{
    public IEnumerable<string> Sequence { get; set; }
    public IEnumerable<string> Bar2()
    {
        return Sequence;
    }
}

此问题的灵感来自 this question,该问题针对类似情况提出了不同的问题.

This question is inspired by this question which is asking a different question about a similar situation.

推荐答案

两者不等价.两个 Bar 方法之间如何延迟执行的语义是不同的.当您调用 Bar 时,Foo.Bar 会将 Sequence 评估为 IEnumerable.当您枚举 Bar2 返回的序列时,Foo2.Bar2 会将 Sequence 评估为该变量 中的值.

The two are not equivalent. The semantics of how execution is deferred between the two Bar methods is different. Foo.Bar will evaluate Sequence into an IEnumerable value when you call Bar. Foo2.Bar2 will evaluate Sequence into the value in that variable when you enumerate the sequence returned by Bar2.

我们可以编写一个足够简单的程序来观察这里的差异.

We can write a simple enough program to observe the differences here.

//Using iterator block
var foo = new Foo();
foo.Sequence = new[] { "Old" };
var query = foo.Bar();
foo.Sequence = new[] { "New" };
Console.WriteLine(string.Join(" ", query));

//Not using iterator block
var foo2 = new Foo2();
foo2.Sequence = new[] { "Old" };
var query2 = foo2.Bar2();
foo2.Sequence = new[] { "New" };
Console.WriteLine(string.Join(" ", query2));

打印出来:


在这种特殊情况下,我们的 Bar 方法也没有副作用.如果确实如此,那么理解程序具有的语义以及它应该具有的语义就不会变得更加重要.例如,让我们修改这两个方法,使其具有一些可观察到的副作用:

In this particular case our Bar method also has no side effects. If it did it would not be noticeably more important to understand the semantics that your program has, and what it should have. For example, let's modify the two methods so that they have some observable side effects:

public class Foo
{
    public IEnumerable<string> Sequence { get; set; }
    public IEnumerable<string> IteratorBlock()
    {
        Console.WriteLine("I'm iterating Sequence in an iterator block");
        foreach (string s in Sequence)
            yield return s;
    }
    public IEnumerable<string> NoIteratorBlock()
    {
        Console.WriteLine("I'm iterating Sequence without an iterator block");
        return Sequence;
    }
}

现在让我们尝试比较这两种方法,看看它们是如何工作的:

Now let's try comparing these two methods to see how they function:

var query = foo.IteratorBlock();
var query2 = foo.NoIteratorBlock();
Console.WriteLine("---");
query.Count();
query.Count();
query2.Count();
query2.Count();

这将打印出来:

我在没有迭代器块的情况下迭代序列
---
我正在迭代器块中迭代序列
我正在迭代器块中迭代序列

I'm iterating Sequence without an iterator block
---
I'm iterating Sequence in an iterator block
I'm iterating Sequence in an iterator block

这里我们可以看到非迭代器块的副作用发生在方法本身被调用时,而迭代器块的副作用不会在那个时间点发生.然后,稍后,每次我们迭代非迭代器块时,它根本不会引起副作用,但迭代器块每次迭代查询时都会引起副作用.

Here we can see that the non-iterator block's side effects happen when the method itself is called, and the iterator block's side effects don't happen at that point in time. Then, later on, each time we iterate the non-iterator block it doesn't cause the side effects at all, but the iterator block causes the side effects each time the query is iterated.

这篇关于转换此迭代器块如何进行功能更改?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆