6

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

次に、Fooの配列を作成します。

Foo[] foos = new Foo[10];

これを行うとどうなりますか?

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

Fooがクラスの場合、Foo []はヒープ上のFooオブジェクトへのポインターを保持し、そのポインターは新しいFooオブジェクト(古いオブジェクトはリサイクルのために残されています)に変更されます。

しかし、構造体は値型であるため、新しいFoo(20、10)は、以前にfoos [1]が保持していたのと同じメモリ位置を物理的に上書きするだけでしょうか?

4

3 に答える 3

5

実際には、関連するアレイスロットに関連付けられているメモリに値が入力されます。あなたのコードを考えると、小さな例が何が起こっているかを示しています。インラインのコメントをご覧ください。これはリリースビルド用です。

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

上記のコードは、次のようにJITコンパイルされています

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

つまり、コードはレジスタに定数をロードしてから、これらのレジスタの値を配列インスタンスの関連部分に関連付けられたメモリにコピーします。

于 2012-07-06T02:45:35.517 に答える
1

foos[1]のビット単位のコピーが含まれますnew Foo(20, 10);

于 2012-07-06T02:08:30.493 に答える
0

構造体の配列を作成すると、各スロットにデフォルト値のインスタンスが作成されます。C#では、C#で構造体を呼び出すnewと、新しい一時インスタンス(スタック上にある可能性が高い)が作成され、ある構造体を別の構造体に割り当てると、後者のすべてのフィールドが前者の対応するフィールドの内容で上書きされるため、常に後者のインスタンスが変更されます。 。したがって、このステートメント は、デフォルト値foos[1] = new Foo(20,10);での新しい一時インスタンスを作成し、Fooそのインスタンスをパラメーター化されたコンストラクターに渡し、その一時インスタンスのすべてのフィールドを配列スロット1に保持されているインスタンスにコピーしてから、一時インスタンスを破棄します。

ちなみに、vb.netの対応するステートメントの動作は少し異なります。と言うfoos(1) = New Foo(20,10)と、のすべてのフィールドがデフォルト値にリセットされてから、パラメーター化されたコンストラクターfoos(1)に渡されます。この違いは、コンストラクターの実行中にfoos(1)コードがアクセスしようとした場合に重要になる可能性があります。foos(1)

于 2012-07-06T14:46:06.197 に答える