class Person {
public string
FirstName { get; set; }
public string LastName { get; set; }
public override string
ToString() { return $"{FirstName} {LastName}"; }
}
class People
{
Person[] _ppl;
public People(params Person[] ppl) { _ppl = ppl; }
public Person this[int Index]
{
get { return
_ppl[Index]; }
set { _ppl[Index] = value; }
}
public int Length { get { return _ppl == null ? 0 : _ppl.Length; } }
}
Let's get started by checking out the IEnumerable<T> Interface.
using System.Runtime.CompilerServices;
namespace System.Collections.Generic
{
//
Summary:
// Exposes the enumerator, which supports a
simple iteration over a collection of
// a specified type.
// Type
parameters:
// T:
// The type of objects to enumerate.This type
parameter is covariant. That is, you
// can use either the type you specified or
any type that is more derived. For more
// information about covariance and
contravariance, see Covariance and Contravariance
// in Generics.
[TypeDependencyAttribute("System.SZArrayHelper")]
public interface IEnumerable<out T> : IEnumerable
{
//
Summary:
// Returns an enumerator that iterates
through the collection.
//
Returns:
// A System.Collections.Generic.IEnumerator`1
that can be used to iterate through
// the collection.
IEnumerator<T>
GetEnumerator();
}
}
class People : IEnumerable<Person>
{
Person[] _ppl;
public People(params Person[] ppl) { _ppl = ppl; }
public Person this[int Index]
{
get { return
_ppl[Index]; }
set { _ppl[Index] = value; }
}
public int Length {
get { return _ppl == null ? 0 :
_ppl.Length; } }
public IEnumerator<Person> GetEnumerator() { throw new NotImplementedException(); }
IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); }
}
namespace System.Collections.Generic
{
//
Summary:
// Supports a simple iteration over a generic
collection.
// Type
parameters:
// T:
// The type of objects to enumerate.This type
parameter is covariant. That is, you
// can use either the type you specified or
any type that is more derived. For more
// information about covariance and
contravariance, see Covariance and Contravariance
// in Generics.
public interface IEnumerator<out T> : IDisposable, IEnumerator
{
// Summary:
// Gets the element in the collection at the
current position of the enumerator.
//
//
Returns:
// The element in the collection at the
current position of the enumerator.
T Current { get; }
}
}
- IEnumerator<T>
- Current: which returns the current item as its type.
- IEnumerator
- Current: wich return the current item as an object. (from IEnumerator)
- MoveNext: which returns true if there's another element and false if current is at the last
- Reset: which just sets the enumerator to the initial position.
- IDisposable
- Dispose: cleans up and disposes and unsafe pointers and removes any attached events or delegates.
so let's go ahead and implement our IEnumerator<T> on our PeopleEnumerator.
class PeopleEnumerator : IEnumerator<Person>
{
Person[] _nodes;
int _currentIndex;
public PeopleEnumerator(Person[] People) {
_nodes = People;
Reset();
}
public Person Current { get { return _nodes[_currentIndex]; } }
object IEnumerator.Current { get { return Current; } }
public void
Dispose() { //throw new
NotImplementedException(); }
public bool
MoveNext() { return (++_currentIndex < _nodes.Length); }
public void Reset() { _currentIndex = -1; }
}
Person[] _nodes;
int _currentIndex;
Next we have our constructor
public PeopleEnumerator(Person[] People) { _nodes = People; Reset(); }
the reset method
public void Reset() { _currentIndex = -1; }
public bool MoveNext() { return (++_currentIndex < _nodes.Length); }
We have our Current property that has a return type of Person
public Person Current { get { return _nodes[_currentIndex]; } }
all it does is uses the current index with our collection of nodes to return the current Person. It's followed by the explicit implementation that comes form the non-generic IEnuerator interface of current which returns an object.
object IEnumerator.Current { get { return Current; } }
because of polymorphism we can just return the value of Current as a person and it'll get cast to an object
and finally we have the dispose method which is called every time a foreach loop is completed,
public void Dispose() { //throw new NotImplementedException(); }
so in our case since we don't need to dispose anything we just comment out the not implemented exception.
Now let's take a look at our implementation
class Program
{
static void Main(string[] args)
{
var p1 = new Person { FirstName = "Pawel", LastName = "Ciucias" };
var p2 = new Person { FirstName = "Tomek", LastName = "Ciucias" };
var p3 = new Person { FirstName = "Jakub", LastName = "Tywoniuk" };
var p4 = new Person { FirstName = "Magda", LastName = "Tywoniuk" };
var ppl = new People(p1, p2, p3, p4);
for (var i = 0; i
< ppl.Length; i++)
Console.WriteLine(ppl[i].ToString());
foreach(var p in ppl)
Console.WriteLine(p.ToString());
}
}
- Our people class returns a new instance of the PeopleEnumerator, which fires our constructor setting our nodes and reseting our current index.
- next our PeopleEnumerator's Move next function fires, which first increments our current index by one then returns whether our current index is less than the number of elements we have
- if yes it returns true, the current element and increments again
- if no it returns false, stops and calls the dispose method.