List<T>
's GetEnumerator method actually is quite efficient.
When you loop through the elements of a List<T>
, it calls GetEnumerator. This, in turn, generates an internal struct
which holds a reference to the original list, an index, and a version ID to track for changes in the list.
However, since a struct is being used, it's really not creating "garbage" that the GC will ever deal with.
As for "create a new enumerator for each derived class" - .NET generics works differently than C++ templates. In .NET, the List<T>
class (and it's internal Enumerator<T>
struct) is defined one time, and usable for any T. When used, a generic type for that specific type of T is required, but this is only the type information for that newly created type, and quite small in general. This differs from C++ templates, for example, where each type used is created at compile time, and "built in" to the executable.
In .NET, the executable specifies the definition for List<T>
, not List<int>
, List<Entity2D>
, etc...