List <T> creates garbage in C # in foreach

Correct me if I am wrong, but when doing foreach a IEnumerable<T> creates garbage, regardless of what T. is. But I am wondering if you have a List<T> where T is Entity. Then let's say that in a list like Entity2D there is a derived class. Should she create a new counter for each derived class? Why the creation of garbage?

Also does it have an interface, say IEntity , how can T create garbage?

+4
source share
5 answers

List<T> The GetEnumerator method is really efficient.

When scrolling through List<T> elements, it calls GetEnumerator. This, in turn, creates an internal struct that contains a link to the source list, index, and version identifier to track changes to the list.

However, since the structure is used, it really does not create the β€œgarbage” that the GC will ever handle.


Regarding "creating a new counter for each derived class", .NET generics work differently than C ++ templates. In .NET, the List<T> class (and the internal Enumerator<T> structure) is defined once and can be used for any T. When used, a generic type is required for this particular type T, but this is only the information type for this newly created type and completely small overall. This differs from C ++ templates, for example, when each type used is created at compile time and is "embedded" in the executable.

In .NET, the executable defines the definition for List<T> , not List<int> , List<Entity2D> , etc.

+22
source

I think you might be interested in this article , which explains why List (T) will not create garbage, as opposed to Collection (T):

Now here is the hard part. Rumor has it that many of the types in System.Collections.Generic will not allocate an enumerator when using foreach. The GetEnumerator list, for example, returns a structure that will just sit on the stack. See yourself using .NET Reflector if you don't believe me. To prove to myself that foreach above the list does not cause any heap allocations, I changed the entities as List, made the same foreach loop, and started the profiler. No enumerator!

[...]

However, there is a certain reservation to the foregoing. Foreach loop over Lists can still generate garbage. [Casting List to IEnumerable] Although we still do foreach on the List when the list is passed to the interface, the value type enumerator should be put in the heap and put in the heap.

+7
source

Interesting note: as Reed Copsie pointed out , the List<T>.Enumerator type is actually a struct . It is both good and terrible .

It is good in the sense that calling foreach on a List<T> does not actually create garbage, because the garbage collector is not worried about new objects of reference type.

It is terrible in the sense that the sudden return value of GetEnumerator is a type of value against almost every intuition of the .NET developer ( GetEnumerator descript IEnumerator<T> is usually expected, as this is guaranteed by the contract IEnumerable<T> ; List<T> bypasses this, explicitly implementing IEnumerable<T>.GetEnumerator and publicly disclosing a more specific implementation of IEnumerator<T> , which is a value type).

Thus, any code that, for example, passes List<T>.Enumerator separate method, which in turn calls MoveNext on this enumerator object, faces a potential infinite loop problem. Like this:

 int CountListMembers<T>(List<T> list) { using (var e = list.GetEnumerator()) { int count = 0; while (IncrementEnumerator(e, ref count)) { } return count; } } bool IncrementEnumerator<T>(IEnumerator<T> enumerator, ref int count) { if (enumerator.MoveNext()) { ++count; return true; } return false; } 

The above code is very stupid; this means only a trivial example of one scenario in which the return value of List<T>.GetEnumerator can cause extremely unintuitive (and potentially catastrophic) behavior.

But, as I said, it’s still good that it does not create garbage;)

+3
source

Regardless of whether it is a List<Entity> , List<Entity2D> or List<IEntity> , GetEnumerator called once at a time. Also, it doesn't matter, for example, List<Entity> contains Entity2D instances. The implementation of IEnumerable<T> GetEnumerator can create reference objects to be assembled. As Reed noted, List<T> in MS.NET avoids this by using only value types.

+1
source

The List<T> class explicitly implements IEnumerator<T> , so calling GetEnumerator on a variable of type List<T> will return List<T>.Enumerator , which has value type semantics, while calling it on a variable of type IEnumerator<T> , which contains a reference to List<T> , will force it to return a value of type IEnumerator<T> , which will have reference semantics.

0
source

Source: https://habr.com/ru/post/1315841/


All Articles