Allowing iteration without generating any garbage

前端 未结 7 1202
时光取名叫无心
时光取名叫无心 2021-02-01 04:49

I have the following code in an object pool that implements the IEnumerable interface.

public IEnumerable ActiveNodes
{
    get
    {
        for (int i         


        
7条回答
  •  一整个雨季
    2021-02-01 05:19

    Iterating items will in any 'normal' design usually result in the creation of a new enumerable object. Creating and disposing objects is very fast, so only in very special scenarios (where low latency is the top most priority) garbage collections could (I say 'could') be a problem.

    A design without garbage is possible by returning structures that don't implement IEnumerable. The C# compiler can still iterate such objects, because the foreach statement uses duck typing. .NET's List, for instance, takes this approach.

    When using foreach over both an array and List, no garbage will be generated. When using foreach on an array, C# will transform the operation to a for statement, while List already implements a struct enumerator, causing the foreach to produce no garbage.

    Here is a struct enumerable and struct enumerator. When you return the enumerable, the C# compiler can foreach over it:

    public struct StructEnumerable
    {
        private readonly List pool;
    
        public StructEnumerable(List pool)
        {
            this.pool = pool;
        }
    
        public StructEnumerator GetEnumerator()
        {
            return new StructEnumerator(this.pool);
        }
    }
    

    Here is the StructEnumerator:

    public struct StructEnumerator
    {
        private readonly List pool;
        private int index;
    
        public StructEnumerator(List pool)
        {
            this.pool = pool;
            this.index = 0;
        }
    
        public T Current
        {
            get
            {
                if (this.pool == null || this.index == 0)
                    throw new InvalidOperationException();
    
                return this.pool[this.index - 1];
            }
        }
    
        public bool MoveNext()
        {
            this.index++;
            return this.pool != null && this.pool.Count >= this.index;
        }
    
        public void Reset()
        {
            this.index = 0;
        }
    }
    

    You can simply return the StructEnumerable as follows:

    public StructEnumerable Items
    {
        get { return new StructEnumerable(this.pool); }
    }
    

    And C# can iterate over this with a normal foreach:

    foreach (var item in pool.Items)
    {
        Console.WriteLine(item);
    }
    

    Note that you can't LINQ over the item using System.Linq.Enumerable> You need the IEnumerable interface for that, and that involves creating enumerators and, therefore, garbage collection. You could, of course, build your own LINQ extension methods, but that will unlikely help, because that will often still result in new objects being created (when closures are being generated for used delegates).

提交回复
热议问题