简体   繁体   English

IEnumerable怎么样? <T> 在后台工作

[英]How does IEnumerable<T> work in background

I am wandering about the more in-depth functionality of the IEnumerable<T> interface. 我正在徘徊IEnumerable<T>接口的更深入的功能。

Basically, it works as an intermediary step in execution. 基本上,它作为执行中的中间步骤。 For example, if you write: 例如,如果你写:

IEnumerable<int> temp = new int[]{1,2,3}.Select(x => 2*x);

The result of the Select function will not be calculated (enumerated) until something is done with temp to allow it (such as List<int> list = temp.ToList() ). 在使用temp进行某些操作(例如List<int> list = temp.ToList() )之前,不会计算(枚举) Select函数的结果。

However, what puzzles me is, since IEnumerable<T> is an interface, it cannot, by definition, be instantiated. 然而,让我感到困惑的是,由于IEnumerable<T>是一个接口,根据定义,它不能被实例化。 So, what is the collection the actual items (in the example 2*x items) reside in? 那么,实际项目(在示例2*x项目中)所在的集合是什么?

Moreover, if we were to write IEnumerable<int> temp = Enumerable.Repeat(1, 10); 而且,如果我们要编写IEnumerable<int> temp = Enumerable.Repeat(1, 10); , what would be the underlying collection where the 1s are stored (array, list, something else)? ,什么是存储1s的底层集合(数组,列表,其他东西)?

I cannot seem to find a thorough (more in-depth) explanation as to the actual implementation of this interface and its functionality (for example, if there is an underlying collection, how does the yield keyword work). 我似乎无法找到关于此接口的实际实现及其功能的全面(更深入)解释(例如,如果存在底层集合, yield关键字如何工作)。

Basically, what I am asking for is a more elaborate explanation on the functionality of IEnumerable<T> . 基本上,我要求的是对IEnumerable<T>的功能进行更详细的解释。

Implementation shouldn't matter. 实施无关紧要。 All these (LINQ) methods return IEnumerable<T> , interface members are the only members you can access, and that should be enough to use them. 所有这些(LINQ)方法都返回IEnumerable<T> ,接口成员是您可以访问的唯一成员,这应该足以使用它们。

However, if you really have to know, you can find actual implementations on http://sourceof.net . 但是,如果您真的必须知道,可以在http://sourceof.net上找到实际的实现。

But, for some of the methods you won't be able to find explicit class declaration, because some of them use yield return , which means proper class (with state machine) is generated by compiler during compilation. 但是,对于某些方法,您将无法找到显式类声明,因为其中一些使用yield return ,这意味着编译期间编译器会生成正确的类(带状态机)。 eg Enumerable.Repeat is implemented that way: 例如Enumerable.Repeat就是这样实现的:

public static IEnumerable<int> Range(int start, int count) {
    long max = ((long)start) + count - 1;
    if (count < 0 || max > Int32.MaxValue)
        throw Error.ArgumentOutOfRange("count");
    return RangeIterator(start, count); 
}

static IEnumerable<int> RangeIterator(int start, int count) {
    for (int i = 0; i < count; i++)
        yield return start + i;
}

You can read more about that on MSDN: Iterators (C# and Visual Basic) 您可以在MSDN上阅读更多相关信息: 迭代器(C#和Visual Basic)

Not all objects that implement IEnumerable defer execution in some way. 并非所有实现IEnumerable对象都以某种方式延迟执行。 The API of the interface makes it possible to defer execution, but it doesn't require it. 接口的API 可以延迟执行,但它不需要它。 There are likewise implementations that don't defer execution in any way. 同样的实现不会以任何方式推迟执行。

So, what is the collection the actual items (in the example 2*x items) reside in? 那么,实际项目(在示例2 * x项目中)所在的集合是什么?

There is none. 空无一人。 Whenever the next value is requested it computes that one value on demand , gives it to the caller, and then forgets the value. 每当请求下一个值它计算的是需求一个价值,它给调用者,然后忘记了价值。 It doesn't store it anywhere else. 它不会将其存储在任何其他地方。

Moreover, if we were to write IEnumerable<int> temp = Enumerable.Repeat(1, 10); 而且,如果我们要编写IEnumerable<int> temp = Enumerable.Repeat(1, 10); , what would be the underlying collection where the 1s are stored (array, list, something else)? ,什么是存储1s的底层集合(数组,列表,其他东西)?

There wouldn't be one. 没有一个。 It would compute each new value immediately when you ask for the next value and it won't remember it afterward. 当你要求下一个值时 ,它会立即计算每个新值,之后就不会记住它。 It only stores enough information to be able to compute the next value, which means it only needs to store the element and the number of values left to yield. 它只存储足够的信息以便能够计算下一个值,这意味着它只需要存储元素和剩余产生的值的数量。

While the actual .NET implementations will use much more concise means of creating such a type, creating an enumerable that defers execution is not particularly hard. 虽然实际的.NET实现将使用更简洁的方法来创建这样的类型,但创建一个延迟执行的枚举并不是特别困难。 Doing so even the long way is more tedious than difficult. 这样做即使是漫长的道路也比艰难更乏味。 You simply compute the next value in the MoveNext method of the iterator. 您只需计算迭代器的MoveNext方法中的下一个值。 In the example you asked of, Repeat , this is easy as you only need to compute if there is another value, not what it is: 在你问的例子中, Repeat ,这很容易,因为你只需要计算是否有另一个值,而不是它是什么:

public class Repeater<T> : IEnumerator<T>
{
    private int count;
    private T element;

    public Repeater(T element, int count)
    {
        this.element = element;
        this.count = count;
    }
    public T Current { get { return element; } }

    object IEnumerator.Current
    {
        get { return Current; }
    }

    public void Dispose() { }

    public bool MoveNext()
    {
        if (count > 0)
        {
            count--;
            return true;
        }
        else
            return false;
    }

    public void Reset()
    {
        throw new NotSupportedException();
    }
}

(I've omitted an IEnumerable type that just returns a new instance of this type, or a static Repeat method that creates a new instance of that enumerable. There isn't anything particularly interesting to see there.) (我省略了一个只返回此类型的新实例的IEnumerable类型,或者一个静态Repeat方法,它创建了一个可枚举的新实例。没有什么特别有趣的东西可以看到。)

A slightly more interesting example would be something like Count : 一个稍微有趣的例子就像Count

public class Counter : IEnumerator<int>
{
    private int remaining;

    public Counter(int start, int count)
    {
        Current = start;
        this.remaining = count;
    }
    public int Current { get; private set; }

    object IEnumerator.Current
    {
        get { return Current; }
    }

    public void Dispose() { }

    public bool MoveNext()
    {
        if (remaining > 0)
        {
            remaining--;
            Current++;
            return true;
        }
        else
            return false;
    }

    public void Reset()
    {
        throw new NotSupportedException();
    }
}

Here we're not only computing if we have another value, but what that next value is, each time a new value is requested of us. 在这里,我们不仅计算我们是否有另一个值,而是每次为我们请求新值时,下一个值是什么。

So, what is the collection the actual items (in the example 2*x items) reside in? 那么,实际项目(在示例2 * x项目中)所在的集合是什么?

It is not residing anywhere. 它不在任何地方。 There is code that will produce the individual items "on demand" when you iterate, but the 2*x numbers are not computed upfront. 迭代时会有“按需”生成单个项目的代码,但不会预先计算2*x数字。 They are also not stored anywhere, unless you call ToList or ToArray . 除非您调用ToListToArray ,否则它们也不会存储在任何位置。

Moreover, if we were to write IEnumerable temp = Enumerable.Repeat(1, 10);, what would be the underlying collection where the 1s are stored (array, list, something else)? 而且,如果我们要编写IEnumerable temp = Enumerable.Repeat(1,10);那么存储1s的底层集合(数组,列表,其他东西)是什么?

The same picture is here: the returned implementation of IEnumerable is not public, and it returns its items on demand, without storing them anywhere. 同样的图片在这里: IEnumerable的返回实现不是公共的,它按需返回其项目,而不将它们存储在任何地方。

C# compiler provides a convenient way to implement IEnumerable without defining a class for it. C#编译器提供了一种实现IEnumerable的便捷方式,而无需为其定义类。 All you need is to declare your method return type as IEnumerable<T> , and use yield return to supply values on as-needed basis. 您所需要的只是将方法返回类型声明为IEnumerable<T> ,并根据需要使用yield return来提供值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM