6

Say I have a C# struct:

struct Foo{ int mA; public int A {get {return mA;}} int mB; public int B {get {return mB;}} public Foo(int a, int b) { mA = a; mB = b; } } 

And then I create and array of Foo's:

Foo[] foos = new Foo[10]; 

What happens when I do this?

foos[1] = new Foo(20, 10); 

If Foo was a class, the Foo[] would hold a pointer to a Foo object on the heap, and that pointer would be changed to the new Foo object (the old one being left for recycling).

But since structs are value types, would the new Foo(20, 10) just physically overwrite the same memory location previously held by foos[1]?

4
  • 1
    As far as I remember, yes. Structs are value-types, so instead of overwriting the reference at foos[i] (as you would see with a class), you're overwriting the whole struct at that position in the array. The garbage collector won't have to clean up the struct as it was physically overwritten. To test, calculate the size of an array of Foo when Foo is declared as a struct and when it is declared as a class, theoretically, the size should be nElements * IntPtr.Size for classes and nElements * sizeof(Foo) for structs; but I've never tried this in C# so I could be wrong. Commented Jul 6, 2012 at 2:11
  • I wish you could somehow make it so the new will actually override a Foo object directly, instead of creating and then copying. Commented Jul 6, 2012 at 2:13
  • @Yorye I think it would be about the same as the statement long i = 1 + 2; The value 3 is created from the expression (1+2) and then copied into i. So really, it's no different from how C style languages normally behave. Commented Jul 6, 2012 at 2:18
  • 2
    @Yorye: Eliminating that copy is the responsibility of the optimizer. Commented Jul 6, 2012 at 3:19

3 Answers 3

5

In practice the memory associated with the relevant array slot is populated by the values. Given your code a small example shows what goes on. Please see comments inline. This is for a release build.

static void Main(string[] args) { Foo[] foos = new Foo[10]; foos[1] = new Foo(127, 255); Console.ReadLine(); } 

The code above is JIT compiled as follows

// Method setup 00280050 55 push ebp 00280051 8bec mov ebp,esp 00280053 56 push esi // Create instance of Foo[] 00280054 b98a141d00 mov ecx,1D148Ah 00280059 ba0a000000 mov edx,0Ah 0028005e e8b121f4ff call CORINFO_HELP_NEWARR_1_VC (001c2214) 00280063 8bd0 mov edx,eax // Array range check 00280065 837a0401 cmp dword ptr [edx+4],1 00280069 7624 jbe // Assign foos[1] = new Foo(127, 255) 0028006b 8d4210 lea eax,[edx+10h] <-- load location of foos[1] in eax 0028006e ba7f000000 mov edx,7Fh <-- load 127 in edx 00280073 beff000000 mov esi,0FFh <-- load 255 in esi 00280078 8910 mov dword ptr [eax],edx <-- move the value 127 to foos[1] 0028007a 897004 mov dword ptr [eax+4],esi <-- move the value 255 to foos[1] + offset // This is just for the Console.ReadLine() part + rest of Main 0028007d e8d2436305 call mscorlib_ni!System.Console.get_In() (058b4454) 00280082 8bc8 mov ecx,eax 00280084 8b01 mov eax,dword ptr [ecx] 00280086 8b402c mov eax,dword ptr [eax+2Ch] 00280089 ff501c call dword ptr [eax+1Ch] // Epilog 0028008c 5e pop esi 0028008d 5d pop ebp 0028008e c3 ret //Exception handling 0028008f e8f05e7f70 call clr!JIT_RngChkFail (70a75f84) 00280094 cc int 3 

So in short, the code loads the constants in registers and then copies values of these registers to the memory associated with the relevant part of the array instance.

Sign up to request clarification or add additional context in comments.

Comments

1

foos[1] will contain bit-wise copy of new Foo(20, 10);.

2 Comments

so the expression (new Foo(20, 10)) evaluates to the value and then it is copied into the location foos[1]?
Yes. Brian Rasmussen's answer shows what happens deep inside code.
0

Creating an array of struct creates in each slot a default-value instance. In C#, calling new on a struct in C# creates a new temporary instance (most likely on the stack) and assigning one struct to another always mutates the latter instance by overwriting all the fields of the latter with the contents of the corresponding fields in the former. Thus, the statement foos[1] = new Foo(20,10); creates a new temporary instance of Foo with default values, passes that instance to the parameterized constructor, copies all of the fields of that temporary instance to the instance held in array slot 1, and then discards the temporary instance.

Incidentally, the behavior of the corresponding statement in vb.net is slightly different. Saying foos(1) = New Foo(20,10) will reset all the fields of foos(1) to their default values, and then pass foos(1) to the parameterized constructor. This difference may be significant if any code tries to access foos(1) while the constructor is running.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.