コレクション【C#】

C#文法:コレクション:B13_Array_Copy

前回は、配列を代入でコピーしようとすると、コピー元とコピー先が連動してしまうという「誤ったコピー」を確認しました。今回はその解決策として、Array.Copyを使ったコピーの方法を見ていきます。

テスト用のボタンを作成する

まずはいつものように、フォームにボタンを1つ追加します。ボタンの表示は「Array.Copy」としておき、ダブルクリックしてクリックイベントを生成します。

図1 「誤ったコピー」の下に「Array.Copy」ボタンを追加

図1 「誤ったコピー」の下に「Array.Copy」ボタンを追加

前回のおさらい:代入によるコピーの問題

前回のコードを振り返っておきます。haiAと同じ要素数のhaiBを用意し、haiB = haiA;と代入していました。

図2 前回の代入によるコピー(haiB = haiA)

図2 前回の代入によるコピー(haiB = haiA)

リスト1 前回の誤ったコピー

//誤ったコピー
//参照代入問題
var haiA = new int[] { 10, 20, 30 };
var haiB = new int[haiA.Length];

haiB = haiA;
haiA[1] = 999;
//連動してhaiBの値も変わってしまう

このやり方では、haiAの値を変更すると、連動してhaiBの値も変わってしまうという問題がありました。代入しているのは配列の中身ではなく参照であるため、haiAとhaiBが同じ配列を指してしまうからです。今回はこれをArray.Copyで解決します。

コピー元とコピー先の配列を用意する

haiAとhaiBは前回と同じものを使うので、前回のコードからコピーしてきます。haiAは10、20、30の3要素を持つ配列、haiBはhaiA.Lengthを指定して同じ要素数で生成した配列です。

図3 button10_ClickにhaiAとhaiBの生成をコピー

図3 button10_ClickにhaiAとhaiBの生成をコピー

リスト2 コピー元とコピー先の配列

//Array.Copy
var haiA = new int[] { 10, 20, 30 };
var haiB = new int[haiA.Length];

ここでのポイントは、haiBをnewで別の配列として生成している点です。Array.Copyはコピー先の配列を自動では作ってくれないので、受け皿となる配列をあらかじめ用意しておく必要があります。

Array.Copyの書き方

それではArray.Copyを書いていきます。Array.Copy(まで入力すると、引数の説明が表示されます。

図4 Array.Copyの引数(sourceArray, destinationArray, length)

図4 Array.Copyの引数(sourceArray, destinationArray, length)

引数は3つです。1つ目のsourceArrayにコピー元の配列、2つ目のdestinationArrayにコピー先の配列、最後のlengthに「何個コピーするか」を指定します。

リスト3 Array.Copyの引数

Array.Copy(sourceArray, destinationArray, length);
//sourceArray      : コピー元の配列
//destinationArray : コピー先の配列
//length           : コピーする要素数

今回はコピー元がhaiA、コピー先がhaiBです。そして全件をコピーしたいので、3つ目の引数にはコピー元配列のLength、つまりhaiA.Lengthを指定します。これで全件コピーになります。

図5 3つ目の引数にhaiA.Lengthを指定

図5 3つ目の引数にhaiA.Lengthを指定

リスト4 Array.Copyで全件コピーする

//Array.Copy
var haiA = new int[] { 10, 20, 30 };
var haiB = new int[haiA.Length];

Array.Copy(haiA, haiB, haiA.Length);

件数を3のように直接書くこともできますが、haiA.Lengthとしておけば、コピー元の要素数が変わってもコードを修正する必要がありません。

コピー元を変更して動作を確認する

この状態でhaiAの値を変更したら、haiBがどうなるかを見てみます。前回と同じようにhaiA[1]に値を代入します。今回は888にしておきます。

リスト5 コピー後にhaiAの要素を変更する

Array.Copy(haiA, haiB, haiA.Length);
haiA[1] = 888;

haiA[1] = 888;の行にブレークポイントを置いて実行し、「Array.Copy」ボタンをクリックします。

図6 haiA[1] = 888; の行にブレークポイントを置いて実行

図6 haiA[1] = 888; の行にブレークポイントを置いて実行

ブレークポイントで止まったところでウォッチウィンドウを見ると、haiAもhaiBも10、20、30で同じ値になっています。Array.Copyによって、haiAの全要素がhaiBにコピーされていることがわかります。

図7 Array.Copy直後はhaiAもhaiBも10、20、30

図7 Array.Copy直後はhaiAもhaiBも10、20、30

ここでhaiA[1] = 888;の行を実行します。

図8 haiA[1]だけが888になり、haiB[1]は20のまま

図8 haiA[1]だけが888になり、haiB[1]は20のまま

haiAだけが、インデックス1の値(2番目の要素)が888になりました。haiBのほうは20のままです。つまり、前回のような参照の問題は起きていないということです。

まとめ

Array.Copyを使うと、haiAの値を変更してもhaiBは変化しません。代入の場合は2つの変数が同じ配列を参照していましたが、Array.Copyでは別々の配列に値だけがコピーされるからです。

図9 完成したコード

図9 完成したコード

リスト6 今回の完成コード

private void button10_Click(object sender, EventArgs e)
{
    //Array.Copy
    var haiA = new int[] { 10, 20, 30 };
    var haiB = new int[haiA.Length];

    Array.Copy(haiA, haiB, haiA.Length);
    haiA[1] = 888;
    //haiAの値を変更してもhaiBは変化しない
}

配列をコピーしたいときは、代入ではなくArray.Copyを使うようにしてください。

C#文法:コレクション

■非公開コース「C#14新機能」プレゼント:
非公開コース「C#14新機能」(80分)をご覧になりたい方は
こちらからURLとパスワードを発行していますので、ご覧になってみてください。
非公開コース「C#14新機能」を観る

A01_はじめに
A02_プロジェクトの作成
B01_配列とは
B02_配列の生成とアクセス
B03_生成と同時に値を設定する
B04_型推論による生成
B05_メソッドの引数などにする場合の注意点
B06_Length
B07_IndexOfでの検索
B08_FindIndexでの検索
B09_Find
B10_Exists
B11_FindAllとFindLast
B12_誤ったコピー
B13_Array.Copy
B14_範囲指定のコピー
B15_Resize
C01_ArrayList
C02_List
C03_List 動的な要素の変更
C04_Listのコンストラクタ
C05_Listのコンストラクタ_Capacity
C06_ListTからArrayクラスのメソッドが呼ばれている
D01_コレクションインタフェースとは
D02_異なるコレクションクラスに互換性を持たせる
D03_インタフェースの階層構造
D04_クライアントができる操作を制限させる
D05_クライアントができる操作を制限させる_後半
D06_Enumerableの拡張メソッドに関して
D07_ReadOnly系のコレクションインタフェース
D08_AsReadOnly
D09_ToListでコピーする
D10_ListTはprivateで使う

■非公開コース「C#14新機能」プレゼント:
非公開コース「C#14新機能」(80分)をご覧になりたい方は
こちらからURLとパスワードを発行していますので、ご覧になってみてください。
非公開コース「C#14新機能」を観る