Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why have Enumerator struct and EnumeratorImpl class?

Tags:

c#

roslyn

I am looking at the Roslyn September 2012 CTP with Reflector, and I noticed that the ChildSyntaxList struct has the following:

public struct ChildSyntaxList : IEnumerable<SyntaxNodeOrToken>
{
    private readonly SyntaxNode node;
    private readonly int count;

    public Enumerator GetEnumerator()
    {
        return node == null ? new Enumerator() : new Enumerator(node, count);
    }

    IEnumerator<SyntaxNodeOrToken> IEnumerable<SyntaxNodeOrToken>.GetEnumerator()
    {
        return node == null
            ? SpecializedCollections.EmptyEnumerator<SyntaxNodeOrToken>()
            : new EnumeratorImpl(node, count);
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return node == null
            ? SpecializedCollections.EmptyEnumerator<SyntaxNodeOrToken>()
            : new EnumeratorImpl(node, count);
    }

    public struct Enumerator
    {
        internal Enumerator(SyntaxNode node, int count)
        {
            /* logic */
        }

        public SyntaxNodeOrToken Current { get { /* logic */ } }

        public bool MoveNext()
        {
            /* logic */
        }

        public void Reset()
        {
            /* logic */
        }
    }

    private class EnumeratorImpl : IEnumerator<SyntaxNodeOrToken>
    {
        private Enumerator enumerator;

        internal EnumeratorImpl(SyntaxNode node, int count)
        {
            enumerator = new Enumerator(node, count);
        }

        public SyntaxNodeOrToken Current { get { return enumerator.Current; } }

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

        public void Dispose()
        {
        }

        public bool MoveNext()
        {
            return enumerator.MoveNext();
        }

        public void Reset()
        {
            enumerator.Reset();
        }
    }
}

That is, there is a GetEnumerator method which returns a struct.

It looks like that

  1. using a struct is a performance gain similar to the BCL List<T>.Enumerator struct, as noted in this answer, and that
  2. the struct does not implement IDisposable so as to not have to worry about bugs that could arise from doing so, as noted on Eric Lippert's blog.

However, unlike the BCL List<T> class, there is a nested EnumeratorImpl class. Is the purpose of this to

  1. avoid having a disposable struct, and
  2. avoid boxing within the explicitly implemented IEnumerable<SyntaxNodeOrToken>.GetEnumerator and IEnumerable.GetEnumerator methods?

Are there any other reasons?

like image 921
cubetwo1729 Avatar asked Sep 11 '13 14:09

cubetwo1729


People also ask

Is list<T> enumerator a struct?

During my investigations of List<T> enumeration I noticed List<T>.Enumerator is struct. In opposite for instance to System.Array.SZArrayEnumerator or System.SZArrayHelper.SZGenericArrayEnumerator<T> that are class es.

What is enumeration in C++11?

C++11 has introduced enum classes (also called scoped enumerations ), that makes enumerations both strongly typed and strongly scoped. Class enum doesn’t allow implicit conversion to int, and also doesn’t compare enumerators from different enumerations. To define enum class we use class keyword after enum keyword.

Why struct enumerator is not used for value type?

It seems this not typical usage of struct since enumerator has no traits of value type. For instance it modifies its state, it is never passed "by value" anywhere, etc. What is design intention here? Fewer allocations means less memory pressure for the garbage collector.

How to define enum class in C++?

Class enum doesn’t allow implicit conversion to int, and also doesn’t compare enumerators from different enumerations. To define enum class we use class keyword after enum keyword. // Declaration enum class EnumName { Value1, Value2, ... ValueN}; // Initialisation EnumName ObjectName = EnumName::Value;


1 Answers

Are there any other reasons?

None come to mind. You seem to have accurately described the purposes of this rather odd implementation of the sequence pattern.

I hasten to add: Roslyn is an unusual .NET application in its complexity, in its performance requirements, and in the number of objects that it generates. A compiler that analyzes programs with thousands of files, millions of lines and tens of millions of characters while the user is typing has to do some pretty unusual things to ensure that it does not overwhelm the garbage collector. Roslyn therefore uses pooling strategies, uses mutable value types, and other out-of-the-mainstream practices to help achieve these performance goals. I do not recommend taking on the expense and difficulty associated with these practices unless you have empirical evidence identifying a serious performance problem that these practices mitigate. Just because this code was written by the C# compiler team does not mean that this is the gold standard for how you should be writing your mainstream business objects.

like image 88
Eric Lippert Avatar answered Oct 19 '22 23:10

Eric Lippert