Jeroen asked:

While we're on the subject of boxing, why doesn't foreach do the same optimization as using? foreach always seems to box the enumerator struct.

If you use a struct as an enumerator, you will always box when you go to IEnumerator. Interfaces are reference types, and you have to box to get an interface reference to a struct.

The fix is to implement the strongly-typed enumerator pattern. Start with the IEnumerable/IEnumerator version and:

1) Change the type of the Current property in the IEnumerator struct from object to the strong type.
2) Remove IEnumerator from the implementation list on the struct type.
3) Remove IEnumerable from the implementation list
4) Change GetEnumerator() so that it returns the struct type rather than IEnumerator

The compiler will then deal with the strongly-typed version. If you want to also allow the interface versions, you can implement them specifically.

Here's some code (sorry about the formatting):

public class IntegerListExplicit: IEnumerable
{
int count = 0;
int allocated = 10;
int[] elements = new int[10];
public IntegerListExplicit()
{
}
void Expand()
{
if (count == allocated)
{
int[] newElements = new int[allocated * 2];
for (int i = 0; i < count; i++)
newElements[i] = elements[i];
allocated = allocated * 2;
elements = newElements;
}
}
public int Add (int item)
{
lock(this)
{
Expand();
elements[count] = item;
count++;
}
return count - 1;
}
public int Count
{
get
{
return(count);
}
}
void CheckIndex(int index)
{
if (index < 0 || index > count - 1)
throw(new IndexOutOfRangeException(String.Format("Index {0} out of range", index)));
}
public int this[int index]
{
get
{
CheckIndex(index);
return(elements[index]);
}
set
{
CheckIndex(index);
elements[index] = value;
}
}
public override string ToString()
{
string[] s = new string[count];
for (int i = 0; i < count; i++)
s[i] = elements[i].ToString();
return(String.Join("\n", s));
}
IEnumerator IEnumerable.GetEnumerator()
{
return((IEnumerator) GetEnumerator());
}
public IntegerListEnumerator GetEnumerator()
{
return(new IntegerListEnumerator(this));
}
public class IntegerListEnumerator: IEnumerator
{
IntegerListExplicit list;
int index = -1;
public IntegerListEnumerator(IntegerListExplicit list)
{
this.list = list;
}
public bool MoveNext()
{
index++;
if (index == list.Count)
return(false);
else
return(true);
}
object IEnumerator.Current
{
get
{
return(Current);
}
}
public int Current
{
get
{
return(list[index]);
}
}
public void Reset()
{
index = -1;
}
}
}