Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do C# Arrays use a reference type for Enumeration, but List<T> uses a mutable struct?

Tags:

From what I've read, a design decision was made for certain Collections's Enumerator Types to be mutable structs instead of reference types for performance reasons. List.Enumerator is the most well known.

I was investigating some old code that used arrays, and was surprised to discover that C# Arrays return the type SZGenericArrayEnumerator as their generic enumerator type, which is a reference type.

I am wondering if anyone knows why Array's generic iterator was implemented as a reference type when so many other performance critical collections used mutable structs instead.

like image 466
Chuu Avatar asked Feb 07 '12 21:02

Chuu


People also ask

Why do we write in C?

It was mainly developed as a system programming language to write an operating system. The main features of the C language include low-level memory access, a simple set of keywords, and a clean style, these features make C language suitable for system programmings like an operating system or compiler development.

Why do we use %d in C?

In C programming language, %d and %i are format specifiers as where %d specifies the type of variable as decimal and %i specifies the type as integer. In usage terms, there is no difference in printf() function output while printing a number using %d or %i but using scanf the difference occurs.

Why do we use semicolon in C?

Role of Semicolon in C: Semicolons are end statements in C. The Semicolon tells that the current statement has been terminated and other statements following are new statements. Usage of Semicolon in C will remove ambiguity and confusion while looking at the code.


2 Answers

From what I've read, a design decision was made for certain Collections's Enumerator Types to be mutable structs instead of reference types for performance reasons.

Good question.

First off, you are correct. Though in general, mutable value types are a bad code smell, in this case they are justified:

  • The mutation is almost entirely concealed from the user.
  • It is highly unlikely that anyone is going to use the enumerator in a confusing manner.
  • The use of a mutable value type actually does solve a realistic performance problem in an extremely common scenario.

I am wondering if anyone knows why Array's generic iterator was implemented as a reference type when so many other performance critical collections used mutable structs instead.

Because if you're the sort of person who is concerned about the performance of enumerating an array then why are you using an enumerator in the first place? It's an array for heaven's sake; just write a for loop that iterates over its indicies like a normal person and never allocate the enumerator. (Or a foreach loop; the C# compiler will rewrite the foreach loop into the equivalent for loop if it knows that the loop collection is an array.)

The only reason why you'd obtain an enumerator from an array in the first place is if you are passing it to a method that takes an IEnumerator<T>, in which case if the enumerator is a struct then you're going to be boxing it anyway. Why take on the expense of making the value type and then boxing it? Just make it a reference type to begin with.

like image 200
Eric Lippert Avatar answered Sep 21 '22 13:09

Eric Lippert


Arrays get some special treatment in the C# compiler. When you use foreach on them, the compiler translates it into a for loop. So there is no performance benefit in using struct enumerators.

List<T> on the other hand is a plain class without any special treatment, so using a struct results in better performance.

like image 27
CodesInChaos Avatar answered Sep 18 '22 13:09

CodesInChaos