2012-07-06 5 views
6

decir que tengo una estructura de C#:¿Qué ocurre cuando se asigna una nueva estructura a la matriz en C#?

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; 
    } 
} 

Y luego creo y variedad de Foo:

Foo[] foos = new Foo[10]; 

lo que sucede cuando hago esto?

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

Si Foo era una clase, el Foo [] sostendría un puntero a un objeto de Foo en el montón, y que el puntero se cambia al nuevo objeto de Foo (el anterior se dejó para el reciclaje).

Pero como las estructuras son tipos de valores, ¿el nuevo Foo (20, 10) sobrescribiría físicamente la misma ubicación de memoria que tenía anteriormente foos [1]?

+1

Por lo que recuerdo, sí. Las estructuras son tipos de valores, así que en lugar de sobreescribir la referencia en foos [i] (como verías con una clase), estás sobreescribiendo la estructura completa en esa posición en la matriz. El recolector de basura no tendrá que limpiar la estructura ya que fue sobrescrita físicamente. Para probar, calcule el tamaño de una matriz de Foo cuando Foo se declare como 'struct' y cuando se declare como' clase', teóricamente, el tamaño debe ser 'nElements * IntPtr.Size' para las clases y' nElements * sizeof (Foo) 'para las estructuras; pero nunca lo intenté en C#, así que podría estar equivocado. –

+0

Me gustaría que pudieras hacerlo de alguna manera para que el 'nuevo' en realidad anule un objeto Foo directamente, en lugar de crear y luego copiar. – SimpleVar

+0

@Yorye Creo que sería casi lo mismo que el enunciado largo i = 1 + 2; El valor 3 se crea a partir de la expresión (1 + 2) y luego se copia en i. Entonces, realmente, no es diferente de cómo se comportan normalmente los lenguajes de estilo C. – CodeFusionMobile

Respuesta

5

En la práctica, la memoria asociada a la ranura de la matriz correspondiente está poblada por los valores. Dado su código, un pequeño ejemplo muestra lo que sucede. Por favor, vea los comentarios en línea. Esto es para una versión de lanzamiento.

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

El código anterior se JIT compilado como sigue

// 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 

Así pues, en resumen, el código carga las constantes en registros y luego copia valores de estos registros a la memoria asociada con la parte pertinente de la instancia de matriz

1

foos[1] contendrá la copia bit-sabia de new Foo(20, 10);.

+0

por lo que la expresión (nuevo Foo (20, 10)) evalúa el valor y luego se copia en la ubicación foos [1]? – CodeFusionMobile

+0

Sí. La respuesta de Brian Rasmussen muestra lo que ocurre en el interior del código. –

0

Creando una matriz de struct crea en cada ranura una instancia de valor predeterminado. En C#, llamar al new en una estructura en C# crea una nueva instancia temporal (muy probablemente en la pila) y asignar una estructura a otra siempre muta la última instancia al sobrescribir todos los campos de esta última con los contenidos de los campos correspondientes en el ex. Por lo tanto, la instrucción foos[1] = new Foo(20,10); crea una nueva instancia temporal de Foo con valores predeterminados, pasa esa instancia al constructor parametrizado, copia todos los campos de esa instancia temporal a la instancia contenida en la ranura 1, y luego descarta la instancia temporal.

Dicho sea de paso, el comportamiento de la declaración correspondiente en vb.net es ligeramente diferente. Al decir foos(1) = New Foo(20,10) se restablecerán todos los campos de foos(1) a sus valores predeterminados, y luego se pasará foos(1) al constructor parametrizado. Esta diferencia puede ser significativa si algún código intenta acceder al foos(1) mientras el constructor se está ejecutando.

Cuestiones relacionadas