With that saidEDIT
In light of @HenrikHansen 's answer about GetEnumerator, I offer the following modifications:have changed my answer to provide a custom enumerator. I see some discussion between Henrik and @sleptic, so let me add some context.
Microsoft has this to say about Stack.GetEnumerator :
public class StackV1 : IEnumerable
{
private List<object> list = new List<object>();
public IEnumerator GetEnumerator()
{
// No need for LINQ list.Any() here.
if (list.Count > 0)
{
yield return Pop();
}
}
public object Pop()
{
if (list.Count == 0)
{
throw new InvalidOperationException("Empty stack. There is nothing to Pop().");
}
// Pop by removing from end of list.
var index = list.Count - 1;
var item = list[index];
list.RemoveAt(index);
return item;
}
public void Push(object item)
{
if (item == null)
{
throw new InvalidOperationException($"{nameof(item)} is null. There is nothing to Push().");
}
// Push by adding to end of list.
list.Add(item);
}
public void Clear()
{
// list will never be null, and it shouldEnumerators notcan be a crimeused to clear an empty list.
list.Clear();
}
// The original exercise did not require a Print() or ToString() method.
public override string ToString()
{
if (list.Count == 0)
{
return "{ empty stack }"; // or perhaps "{ }"
}
// Each Console.WriteLine has a tiny cost. Let'sread trythe notdata evenin callthe itcollection, but rather comnpose a string.
they cannot be //used Thisto honorsmodify the OP's order and display.
var sb = new StringBuilder();
for (var i = list.Count - 1; i >= 0; i--)
{
sb.AppendLine(list[i].ToString());
}
returnunderlying sbcollection.ToString();
}
}
Also, out of furthering my own skills and providing a better answer, I borrowed some code from Microsoft's Stack implementation, which uses an array BTW.
With that said, I offer the following modifications with updated code which now includes a better GetEnumerator:
public class StackV1 : IEnumerable
{
private List<object> list = new List<object>();
private int _version = 0; // used to keep in-sync with enumerator
public Enumerator GetEnumerator()
{
return new Enumerator(this);
}
IEnumerator IEnumerable.GetEnumerator()
{
return new Enumerator(this);
}
public object Pop()
{
if (list.Count == 0)
{
throw new InvalidOperationException("Empty stack. There is nothing to Pop().");
}
// Pop by removing from end of list.
var index = list.Count - 1;
var item = list[index];
list.RemoveAt(index);
_version++;
return item;
}
public void Push(object item)
{
if (item == null)
{
throw new InvalidOperationException($"{nameof(item)} is null. There is nothing to Push().");
}
_version++;
// Push by adding to end of list.
list.Add(item);
}
public void Clear()
{
// list will never be null, and it should not be a crime to clear an empty list.
if (list.Count > 0)
{
list.Clear();
_version++;
}
}
public int Size => list.Count;
// The original exercise did not require a Print() or ToString() method.
public override string ToString()
{
if (list.Count == 0)
{
return "{ empty stack }"; // or perhaps "{ }"
}
// Each Console.WriteLine has a tiny cost. Let's try not even call it, but rather comnpose a string.
// This honors the OP's order and display.
var sb = new StringBuilder();
for (var i = list.Count - 1; i >= 0; i--)
{
sb.AppendLine(list[i].ToString());
}
return sb.ToString();
}
// See https://referencesource.microsoft.com/#System/compmod/system/collections/generic/stack.cs,8865095e0bceeafd
public struct Enumerator : IEnumerator
{
private StackV1 _stack;
private int _version;
private int _index;
private object currentElement;
internal Enumerator(StackV1 stack)
{
_stack = stack;
_version = stack._version;
_index = -2;
currentElement = default(object);
}
public void Dispose()
{
_index = -1;
}
public bool MoveNext()
{
bool retval;
if (_version != _stack._version)
{
throw new Exception("Out-of-sync Enumerator due to a modified Stack.");
}
if (_index == -2)
{ // First call to enumerator.
_index = _stack.Size - 1;
retval = (_index >= 0);
if (retval)
{
currentElement = _stack.list[_index];
}
return retval;
}
if (_index == -1)
{ // End of enumeration.
return false;
}
retval = (--_index >= 0);
currentElement = retval ? _stack.list[_index] : default(object);
return retval;
}
public object Current
{
get
{
if (_index == -2)
{
throw new Exception("Pointer is before top of the stack.");
}
if (_index == -1)
{
throw new Exception("Pointer is past the bottom of the stack.");
}
return currentElement;
}
}
public void Reset()
{
if (_version != _stack._version)
{
throw new Exception("Out-of-sync Enumerator due to the Stack being modified externally.");
}
_index = -2;
currentElement = default(object);
}
}
}
When I first looked at this exercise, I wondered why it was for Intermediates. It seemed too easy. After updating with a customer Enumerator, I can know see why its Intermediate.