Click here to Skip to main content
15,886,137 members
Articles / Programming Languages / C# 4.0

Pick Your Enumerator & Me.Understand Yield and IEnumerable (C#)

Rate me:
Please Sign up or sign in to vote.
4.63/5 (16 votes)
21 Apr 2013CPOL18 min read 58.2K   410   25  
Using multiple enumerators and implementing IEnumerable with Yield or IEnumerator.
using System;
using System.Collections.Generic;

namespace IEnumerableCS
{
    /// <summary>
    /// An Enumerator for a 2D array of double(,), enumerates horizontally (by row)
    /// </summary>
    /// <remarks></remarks>
    public class HorizontalMatrixEnumerator : IEnumerator<double>
    {

        private double[,] _matrix;
        private int _colIndex;
        private int _rowIndex;
        private double _curItem;
        private int _lastCol;
        private int _lastRow;

        /// <summary>
        /// Creates the Enumerator using the supplied 2D array of double and initialises the fields
        /// </summary>
        /// <param name="matrix">The 2D array of double(,) to be enumerated</param>
        /// <remarks></remarks>
        public HorizontalMatrixEnumerator(double[,] matrix)
        {
            this._matrix = matrix;
            this._colIndex = -1;
            this._rowIndex = 0;
            this._curItem = double.NaN;
            this._lastCol = matrix.GetUpperBound(1);
            this._lastRow = matrix.GetUpperBound(0);
        }

        /// <summary>
        /// Returns the current item of the array during an enumeration
        /// </summary>
        /// <value>The current item of the array during an enumeration</value>
        /// <returns>A double representing the current item of the array during an enumeration</returns>
        /// <exception cref="InvalidOperationException">This exception is thrown if an attempt to access Current is made before MoveNext has been called - applies either immediately after object creation or after a call to <see cref="Reset">Reset()</see></exception>
        /// <remarks>Does not cause the position to change, repeated calls to Current will return the same value, use MoveNext to change position</remarks>
        public double Current
        {
            get
            {
                //If _curItem is NaN then throw an exception
                //this would happen if Current was referenced before MoveNext had been called
                if (double.IsNaN(this._curItem))
                {
                    throw new InvalidOperationException();
                }
                return this._curItem;
            }
        }

        /// <summary>
        /// [Use Current instead] Returns the current item of the array during an enumeration
        /// </summary>
        /// <value>[Use Current instead] The current item of the array during an enumeration</value>
        /// <returns>[Use Current instead] A double representing the current item of the array during an enumeration</returns>
        /// <remarks>[Use Current instead] Does not cause the position to change, repeated calls to Current will return the same value, use MoveNext to change position</remarks>
        object System.Collections.IEnumerator.Current
        {
            get { return this.Current; }
        }

        /// <summary>
        /// Moves to the next position in the array
        /// </summary>
        /// <returns><code>False</code> if the end of the array was reached after the porevious call to MoveNext, <code>True</code> otherwise</returns>
        /// <remarks></remarks>
        public bool MoveNext()
        {
            //Ensure we are not already at the bottom right corner of the array
            if (this._colIndex == this._lastCol & this._rowIndex == this._lastRow)
            {
                //Return False immediately
                return false;
            }
            //Check if we are at the end of a row
            if (this._colIndex == this._lastCol)
            {
                //Move to the next row, starting at column 0
                this._colIndex = 0;
                this._rowIndex += 1;
            }
            else
            {
                //Or just move to the next column in the row
                this._colIndex += 1;
            }
            //Set the value of _curItem to represent the new position in the array
            this._curItem = this._matrix[this._rowIndex, this._colIndex];
            //Returns true only if we have successfully changed position in the array
            return true;
        }

        /// <summary>
        /// Resets the Enumerator object to the state as immediately after creation
        /// </summary>
        /// <remarks>A call to <see cref="Current">Current</see> after Reset will throw an exception, a call to <see cref="MoveNext">MoveNext</see> must be made first</remarks>
        public void Reset()
        {
            this._colIndex = -1;
            this._rowIndex = 0;
            this._curItem = double.NaN;
        }

        #region "IDisposable Support"
        // To detect redundant calls
        private bool disposedValue;

        // IDisposable
        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposedValue)
            {
                if (disposing)
                {
                    // None
                }
            }
            this.disposedValue = true;
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        #endregion

    }


    /// <summary>
    /// An Enumerator for a 2D array of double(,), enumerates vertically (by column)
    /// </summary>
    /// <remarks></remarks>
    public class VerticalMatrixEnumerator : IEnumerator<double>
    {

        private double[,] _matrix;
        private int _colIndex;
        private int _rowIndex;
        private double _curItem;
        private int _lastCol;
        private int _lastRow;

        /// <summary>
        /// Creates the Enumerator using the supplied 2D array of double and initialises the fields
        /// </summary>
        /// <param name="matrix">The 2D array of double(,) to be enumerated</param>
        /// <remarks></remarks>
        public VerticalMatrixEnumerator(double[,] matrix)
        {
            this._matrix = matrix;
            this._colIndex = 0;
            this._rowIndex = -1;
            this._curItem = double.NaN;
            this._lastCol = matrix.GetUpperBound(1);
            this._lastRow = matrix.GetUpperBound(0);
        }

        /// <summary>
        /// Returns the current item of the array during an enumeration
        /// </summary>
        /// <value>The current item of the array during an enumeration</value>
        /// <returns>A double representing the current item of the array during an enumeration</returns>
        /// <exception cref="InvalidOperationException">This exception is thrown if an attempt to access Current is made before MoveNext has been called - applies either immediately after object creation or after a call to <see cref="Reset">Reset()</see></exception>
        /// <remarks>Does not cause the position to change, repeated calls to Current will return the same value, use MoveNext to change position</remarks>
        public double Current
        {
            get
            {
                //If _curItem is null then throw an exception
                //this would happen if Current was referenced before MoveNext had been called
                if (double.IsNaN(this._curItem))
                {
                    throw new InvalidOperationException();
                }
                return this._curItem;
            }
        }

        /// <summary>
        /// [Use Current instead] Returns the current item of the array during an enumeration
        /// </summary>
        /// <value>[Use Current instead] The current item of the array during an enumeration</value>
        /// <returns>[Use Current instead] A double representing the current item of the array during an enumeration</returns>
        /// <remarks>[Use Current instead] Does not cause the position to change, repeated calls to Current will return the same value, use MoveNext to change position</remarks>
        object System.Collections.IEnumerator.Current
        {
            get { return this.Current; }
        }

        /// <summary>
        /// Moves to the next position in the array
        /// </summary>
        /// <returns><code>False</code> if the end of the array was reached after the porevious call to MoveNext, <code>True</code> otherwise</returns>
        /// <remarks></remarks>
        public bool MoveNext()
        {
            //Ensure we are not already at the bottom right corner of the array
            if (this._colIndex == this._lastCol & this._rowIndex == this._lastRow)
            {
                //Return False immediately
                return false;
            }
            //Check if we are at the end of a row
            if (this._rowIndex == this._lastRow)
            {
                //Move to the next column, starting at row 0
                this._rowIndex = 0;
                this._colIndex += 1;
            }
            else
            {
                //Or just move to the next column in the row
                this._rowIndex += 1;
            }
            //Set the value of _curItem to represent the new position in the array
            this._curItem = this._matrix[this._rowIndex, this._colIndex];
            //Returns true only if we have successfully changed position in the array
            return true;
        }

        /// <summary>
        /// Resets the Enumerator object to the state as immediately after creation
        /// </summary>
        /// <remarks>A call to <see cref="Current">Current</see> after Reset will throw an exception, a call to <see cref="MoveNext">MoveNext</see> must be made first</remarks>
        public void Reset()
        {
            this._colIndex = 0;
            this._rowIndex = -1;
            this._curItem = double.NaN;
        }

        #region "IDisposable Support"
        // To detect redundant calls
        private bool disposedValue;

        // IDisposable
        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposedValue)
            {
                if (disposing)
                {
                    // None
                }
            }
            this.disposedValue = true;
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        #endregion

    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Engineer
France France
A hobbyist begin-again-er!

Spent a few years longer ago than I care to remember with BBC Basic, a couple of years with Delphi about 10-15 years ago with a smattering af MS Access applications along the way. Dropped out of it completely except for the occasional Excel macro.

Finally picked up the baton again with VB.Net in VS2010 and now VS 2012and have been playing around quite a bit with a few odds and sodds, learning much as I go - and long may it continue.

I don't work even close to the IT industry and probably never will, but I do enjoy it as a hobby.

Comments and Discussions