C# Array.Copyの使い方を徹底解説|配列コピーの基本・範囲指定・エラー対策まで

はじめに

C#で配列を扱っていると、「配列の一部だけを別の配列にコピーしたい」「元の配列を変更せずにバックアップを作りたい」「要素をずらして挿入したい」といった場面がよくあります。そこで使える代表的なメソッドがArray.Copyです。

Array.Copyは、1つの配列から別の配列へ要素の範囲をコピーするためのメソッドです。単純な全体コピーだけでなく、コピー元の開始位置、コピー先の開始位置、コピーする要素数を指定できるため、柔軟な配列操作に向いています。また、必要に応じて型キャストやボックス化も行われます。

この記事では、c# array.copyを調べている方に向けて、Array.Copyの基本構文、範囲指定、オーバーロード、よくあるエラー、CloneCopyToなど似た方法との違いまで、実践コードを交えて解説します。

1. C#のArray.Copyとは?配列コピーでできること

1-1. Array.Copyの基本的な役割

Array.Copyは、System.Arrayクラスが提供する静的メソッドです。指定した配列の要素を、別の配列へコピーできます。

基本的には次のような処理に使います。

C#
int[] source = { 10, 20, 30 };
int[] destination = new int[3];

Array.Copy(source, destination, source.Length);

このコードでは、sourceの3つの要素をdestinationへコピーしています。実行後のdestinationは次のようになります。

C#
{ 10, 20, 30 }

ポイントは、コピー先配列を事前に用意しておく必要があることです。Array.Copyはコピー先配列を自動生成したり、自動拡張したりしません。

1-2. 代入による参照コピーとの違い

C#の配列は参照型です。そのため、単純に代入すると配列の中身がコピーされるのではなく、同じ配列を参照する変数が増えるだけです。

C#
int[] a = { 1, 2, 3 };
int[] b = a;

b[0] = 99;

Console.WriteLine(a[0]); // 99

b = aは、配列の実体をコピーしているわけではありません。abが同じ配列を指しているため、b[0]を変更するとa[0]も変更されたように見えます。

一方、Array.Copyを使うと、別の配列に要素をコピーできます。

C#
int[] a = { 1, 2, 3 };
int[] b = new int[a.Length];

Array.Copy(a, b, a.Length);

b[0] = 99;

Console.WriteLine(a[0]); // 1
Console.WriteLine(b[0]); // 99

この場合、abは別々の配列です。値型であるintの要素はコピー先に値として複製されるため、bを変更してもaの要素には影響しません。

1-3. どんな場面でArray.Copyを使うのか

Array.Copyは、次のような場面で役立ちます。

C#
// 配列の一部だけを取り出す
int[] source = { 10, 20, 30, 40, 50 };
int[] part = new int[3];

Array.Copy(source, 1, part, 0, 3);
// part: { 20, 30, 40 }

配列全体を複製したい場合はCloneToArrayでも対応できますが、「開始位置を指定して一部だけコピーしたい」「コピー先の途中から書き込みたい」という場合はArray.Copyが分かりやすい選択です。

また、固定長配列を扱う処理では、要素の挿入、削除、結合、バックアップなどにも活用できます。

1-4. Array.Copyでコピーできる配列の種類

Array.Copyは、int[]string[]object[]、独自クラスの配列など、さまざまな配列に使えます。値型配列と参照型配列の両方を扱えます。

C#
string[] names = { "Alice", "Bob", "Charlie" };
string[] copied = new string[names.Length];

Array.Copy(names, copied, names.Length);

また、型に互換性がある場合は、異なる型の配列間でもコピーできます。たとえば、string[]からobject[]へのコピーは可能です。

C#
string[] source = { "A", "B", "C" };
object[] destination = new object[3];

Array.Copy(source, destination, source.Length);

ただし、型に互換性がない場合や、コピー先の型へ変換できない要素が含まれている場合は例外が発生します。Array.Copyでは参照型配列同士、またはObject配列同士のコピーでは浅いコピーが行われ、参照先のオブジェクト自体までは複製されません。

2. Array.Copyの基本的な使い方

2-1. 最もシンプルな書き方

最もよく使う構文は次の形です。

C#
Array.Copy(コピー元配列, コピー先配列, コピーする要素数);

実際のコードは次のとおりです。

C#
int[] source = { 1, 2, 3, 4, 5 };
int[] destination = new int[5];

Array.Copy(source, destination, 5);

この書き方では、コピー元配列の先頭から、コピー先配列の先頭へ、指定した要素数だけコピーします。

2-2. コピー元配列とコピー先配列を用意する

Array.Copyを使う前に、コピー元とコピー先の配列を用意します。

C#
int[] source = { 100, 200, 300 };
int[] destination = new int[3];

この時点でdestinationには、intの初期値である0が入っています。

C#
// destination: { 0, 0, 0 }

ここにArray.Copyで要素をコピーします。

C#
Array.Copy(source, destination, source.Length);

実行後は次の状態になります。

C#
// destination: { 100, 200, 300 }

コピー先配列のサイズは、コピーする要素数以上である必要があります。サイズが足りない場合はArgumentExceptionが発生します。

2-3. 指定した要素数だけコピーする

Array.Copyの第3引数には、コピーする要素数を指定します。

C#
int[] source = { 10, 20, 30, 40, 50 };
int[] destination = new int[5];

Array.Copy(source, destination, 3);

この場合、先頭から3要素だけコピーされます。

C#
// destination: { 10, 20, 30, 0, 0 }

コピー先配列の残りの要素は、初期値のままです。

2-4. 基本コード例で動作を確認する

次のコードで、Array.Copyの基本動作を確認できます。

C#
using System;

class Program
{
static void Main()
{
int[] source = { 1, 2, 3, 4, 5 };
int[] destination = new int[5];

Array.Copy(source, destination, 3);

Console.WriteLine(string.Join(", ", destination));
}
}

実行結果は次のようになります。

1, 2, 3, 0, 0

Array.Copy(source, destination, 3)は、source[0]から3要素をdestination[0]以降へコピーする、という意味です。

3. Array.Copyで範囲を指定してコピーする方法

3-1. 開始位置を指定してコピーする構文

配列の途中からコピーしたい場合は、次のオーバーロードを使います。

C#
Array.Copy(
コピー元配列,
コピー元の開始インデックス,
コピー先配列,
コピー先の開始インデックス,
コピーする要素数
);

具体的には次のように書きます。

C#
Array.Copy(source, 2, destination, 0, 3);

これは、「sourceのインデックス2から3要素を、destinationのインデックス0からコピーする」という意味です。Array.Copy(Array, Int32, Array, Int32, Int32)は、コピー元の指定インデックスからコピーを開始し、コピー先の指定インデックスへ貼り付けるオーバーロードです。

3-2. コピー元の途中からコピーする方法

コピー元配列の途中から取り出す例を見てみましょう。

C#
int[] source = { 10, 20, 30, 40, 50 };
int[] destination = new int[3];

Array.Copy(source, 2, destination, 0, 3);

Console.WriteLine(string.Join(", ", destination));

実行結果は次のとおりです。

30, 40, 50

sourceIndex2を指定しているため、source[2]source[3]source[4]がコピーされます。

3-3. コピー先の途中にコピーする方法

コピー先配列の途中に書き込むこともできます。

C#
int[] source = { 1, 2, 3 };
int[] destination = { 10, 10, 10, 10, 10 };

Array.Copy(source, 0, destination, 1, 3);

Console.WriteLine(string.Join(", ", destination));

実行結果は次のようになります。

10, 1, 2, 3, 10

destinationIndex1を指定しているため、destination[1]からコピーが始まります。

3-4. 指定範囲コピーの実践コード例

配列の一部を別配列へ切り出す処理は、実務でもよく使います。

C#
int[] scores = { 55, 70, 82, 91, 66, 78 };
int startIndex = 2;
int count = 3;

int[] selectedScores = new int[count];

Array.Copy(scores, startIndex, selectedScores, 0, count);

Console.WriteLine(string.Join(", ", selectedScores));

実行結果は次のとおりです。

82, 91, 66

このように、startIndexcountを変数にしておくと、任意の範囲を取り出す処理として再利用しやすくなります。

4. Array.Copyのオーバーロード一覧と使い分け

4-1. Array.Copy(Array, Array, Int32)の使い方

Array.Copy(Array, Array, Int32)は、コピー元の先頭からコピー先の先頭へ、指定した要素数だけコピーするシンプルなオーバーロードです。

C#
int[] source = { 1, 2, 3, 4 };
int[] destination = new int[4];

Array.Copy(source, destination, 4);

配列全体をコピーする場合は、source.Lengthを指定すると安全です。

C#
Array.Copy(source, destination, source.Length);

ただし、コピー先配列の長さがsource.Length以上であることを確認しておきましょう。

4-2. Array.Copy(Array, Int32, Array, Int32, Int32)の使い方

Array.Copy(Array, Int32, Array, Int32, Int32)は、コピー元とコピー先の開始位置を指定できるオーバーロードです。

C#
int[] source = { 10, 20, 30, 40, 50 };
int[] destination = new int[5];

Array.Copy(source, 1, destination, 2, 3);

実行後のdestinationは次のようになります。

C#
// { 0, 0, 20, 30, 40 }

範囲指定が必要な場合は、このオーバーロードを使うのが基本です。

4-3. LongLengthに対応したオーバーロード

Array.Copyには、Int64を使うオーバーロードもあります。

C#
Array.Copy(Array sourceArray, long sourceIndex, Array destinationArray, long destinationIndex, long length);
Array.Copy(Array sourceArray, Array destinationArray, long length);

これは、インデックスや長さをlongで指定する形式です。公式ドキュメント上でも、Int64版のオーバーロードは、インデックスや長さを64ビット整数で指定する形式として定義されています。ただし、length0以上Int32.MaxValue以下である必要があります。

通常のC#アプリケーションではInt32版を使うことが多いですが、Array.LongLengthを扱うコードや、API上longでサイズを受け取る処理ではInt64版を検討できます。

4-4. 目的別に選ぶべきオーバーロード

配列全体、または先頭から指定数だけコピーしたい場合は、次の形式が簡単です。

C#
Array.Copy(source, destination, length);

コピー元やコピー先の開始位置を指定したい場合は、次の形式を使います。

C#
Array.Copy(source, sourceIndex, destination, destinationIndex, length);

longでインデックスや長さを扱う必要がある場合のみ、Int64版を選びます。通常は、読みやすく扱いやすいInt32版で十分です。

5. Array.Copyでよくあるエラーと対策

5-1. ArgumentNullExceptionが発生する原因

ArgumentNullExceptionは、コピー元配列またはコピー先配列がnullのときに発生します。

C#
int[] source = null;
int[] destination = new int[3];

Array.Copy(source, destination, 3); // ArgumentNullException

対策として、コピー前にnullチェックを行います。

C#
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}

if (destination == null)
{
throw new ArgumentNullException(nameof(destination));
}

Array.Copy(source, destination, source.Length);

Array.Copyでは、sourceArrayまたはdestinationArraynullの場合にArgumentNullExceptionが発生します。

5-2. ArgumentOutOfRangeExceptionが発生する原因

ArgumentOutOfRangeExceptionは、インデックスやコピーする長さが不正な場合に発生します。

C#
int[] source = { 1, 2, 3 };
int[] destination = new int[3];

Array.Copy(source, -1, destination, 0, 2); // ArgumentOutOfRangeException

sourceIndexdestinationIndexに負の値を指定したり、lengthに負の値を指定したりするとエラーになります。

対策は、次のように範囲を確認することです。

C#
if (sourceIndex < 0 || destinationIndex < 0 || length < 0)
{
throw new ArgumentOutOfRangeException();
}

5-3. ArgumentExceptionが発生する原因

ArgumentExceptionは、指定した要素数がコピー元またはコピー先の範囲を超えている場合に発生します。

C#
int[] source = { 1, 2, 3 };
int[] destination = new int[2];

Array.Copy(source, destination, 3); // ArgumentException

コピー先配列は2要素しかないのに、3要素コピーしようとしているためエラーになります。

安全に書くなら、次のようにコピー可能な要素数を計算します。

C#
int length = Math.Min(source.Length, destination.Length);
Array.Copy(source, destination, length);

範囲指定版では、開始位置から末尾までの残り要素数も考慮します。

C#
int copyLength = Math.Min(source.Length - sourceIndex, destination.Length - destinationIndex);
Array.Copy(source, sourceIndex, destination, destinationIndex, copyLength);

5-4. RankExceptionが発生する原因

RankExceptionは、コピー元配列とコピー先配列の次元数が異なる場合に発生します。

C#
int[] source = { 1, 2, 3 };
int[,] destination = new int[2, 2];

Array.Copy(source, destination, 3); // RankException

1次元配列から2次元配列へ、そのままArray.Copyでコピーすることはできません。Array.Copyでは、コピー元とコピー先の配列が同じ次元数である必要があります。

5-5. InvalidCastExceptionが発生する原因

InvalidCastExceptionは、コピー元の要素をコピー先配列の型へキャストできない場合に発生します。

C#
object[] source = { 1, "text", 3 };
int[] destination = new int[3];

Array.Copy(source, destination, 3); // InvalidCastException

"text"intへキャストできないため、コピー時に例外が発生します。

対策として、コピー前に型をチェックします。

C#
object[] source = { 1, 2, 3 };
int[] destination = new int[3];

if (source.All(x => x is int))
{
Array.Copy(source, destination, source.Length);
}

ただし、実務では異なる型の配列間コピーはエラーの原因になりやすいため、できるだけ同じ型の配列同士でコピーするのがおすすめです。

5-6. コピー先配列のサイズ不足を防ぐ方法

コピー先配列のサイズ不足を防ぐには、コピーする要素数に合わせて配列を作成します。

C#
int[] source = { 10, 20, 30, 40 };
int[] destination = new int[source.Length];

Array.Copy(source, destination, source.Length);

一部だけコピーする場合は、コピー数に合わせます。

C#
int startIndex = 1;
int count = 2;

int[] destination = new int[count];

Array.Copy(source, startIndex, destination, 0, count);

コピー先配列を既存の配列として受け取る場合は、事前にサイズチェックを入れると安全です。

C#
if (destination.Length < count)
{
throw new ArgumentException("コピー先配列のサイズが不足しています。");
}

Array.Copy(source, startIndex, destination, 0, count);

6. Array.Copyを使うときの注意点

6-1. 浅いコピーと深いコピーの違い

Array.Copyは、参照型の配列をコピーする場合、基本的に浅いコピーになります。浅いコピーとは、配列の要素として格納されている参照をコピーすることであり、参照先のオブジェクト自体を新しく複製することではありません。

たとえば、次のようなクラスがあるとします。

C#
class User
{
public string Name { get; set; }
}

配列をコピーします。

C#
User[] source =
{
new User { Name = "Alice" },
new User { Name = "Bob" }
};

User[] destination = new User[source.Length];

Array.Copy(source, destination, source.Length);

destination[0].Name = "Changed";

Console.WriteLine(source[0].Name); // Changed

source[0]destination[0]は同じUserオブジェクトを参照しています。そのため、コピー先のオブジェクトのプロパティを変更すると、コピー元から見える値も変わります。

6-2. 参照型配列をコピーするときの注意

参照型配列でArray.Copyを使うときは、「配列そのものは別になるが、中のオブジェクトは同じ」という点を理解しておく必要があります。

C#
destination[0] = new User { Name = "Carol" };

このように、コピー先配列の要素そのものを別オブジェクトに差し替えた場合、コピー元配列の要素は差し替わりません。

しかし、次のように参照先オブジェクトのプロパティを変更すると、同じオブジェクトを見ているコピー元にも影響します。

C#
destination[1].Name = "David";

参照型配列で完全な複製を作りたい場合は、各要素を個別に新しいオブジェクトとして作成する深いコピーが必要です。

C#
User[] deepCopied = source
.Select(user => new User { Name = user.Name })
.ToArray();

6-3. 型変換が必要なコピーの注意点

Array.Copyは、型に互換性がある場合、型変換やボックス化を伴うコピーに対応します。たとえば、値型配列からobject[]へコピーすると、各要素はボックス化されます。

C#
int[] numbers = { 1, 2, 3 };
object[] objects = new object[3];

Array.Copy(numbers, objects, numbers.Length);

一方、object[]からint[]へ戻す場合は、すべての要素がintとして扱える必要があります。

C#
object[] objects = { 1, 2, 3 };
int[] numbers = new int[3];

Array.Copy(objects, numbers, objects.Length);

次のように変換できない要素が混ざっていると例外が発生します。

C#
object[] objects = { 1, "2", 3 };
int[] numbers = new int[3];

Array.Copy(objects, numbers, objects.Length); // InvalidCastException

6-4. コピー元とコピー先が同じ配列の場合の挙動

Array.Copyは、コピー元とコピー先が同じ配列で、コピー範囲が重なっている場合にも使えます。この場合、元の値が一時的な場所に保持されてからコピー先が上書きされるかのように動作します。

C#
int[] numbers = { 1, 2, 3, 4, 5 };

Array.Copy(numbers, 0, numbers, 1, 4);

Console.WriteLine(string.Join(", ", numbers));

実行結果は次のようになります。

1, 1, 2, 3, 4

この挙動を利用すると、配列内の要素を右へずらす処理を簡単に書けます。

6-5. 多次元配列を扱うときの注意点

Array.Copyは多次元配列にも使えますが、扱いには注意が必要です。多次元配列間のコピーでは、配列は概念的に長い1次元配列のように扱われ、行または列が順につながっているものとしてコピーされます。

C#
int[,] source =
{
{ 1, 2, 3 },
{ 4, 5, 6 }
};

int[,] destination = new int[2, 3];

Array.Copy(source, destination, source.Length);

全体コピーなら分かりやすいですが、「2次元配列の特定の行だけをコピーしたい」といった用途では、for文を使ったほうが意図が明確になることがあります。

C#
int row = 1;
int[] copiedRow = new int[source.GetLength(1)];

for (int col = 0; col < source.GetLength(1); col++)
{
copiedRow[col] = source[row, col];
}

7. Array.Copyと似た配列コピー方法の違い

7-1. Cloneとの違い

Cloneは、配列全体の浅いコピーを作成するメソッドです。戻り値の型はobjectなので、通常はキャストが必要です。

C#
int[] source = { 1, 2, 3 };
int[] copied = (int[])source.Clone();

配列全体を簡単に複製したい場合はCloneが手軽です。ただし、範囲指定はできません。また、参照型配列ではArray.Copyと同様に浅いコピーです。Array.Cloneも、配列の要素はコピーしますが、参照先オブジェクト自体はコピーしない浅いコピーです。

7-2. CopyToとの違い

CopyToは、配列インスタンスから呼び出すコピー用メソッドです。

C#
int[] source = { 1, 2, 3 };
int[] destination = new int[5];

source.CopyTo(destination, 1);

実行後のdestinationは次のようになります。

C#
// { 0, 1, 2, 3, 0 }

CopyToは、現在の配列の全要素を、コピー先配列の指定インデックスからコピーします。コピー先配列は事前に十分なサイズで用意されている必要があります。

一方、Array.Copyは、コピー元の開始位置とコピーする要素数を指定できるため、部分コピーに向いています。

7-3. LINQのToArrayとの違い

ToArrayは、IEnumerable<T>などのシーケンスを配列に変換するLINQメソッドです。

C#
int[] source = { 1, 2, 3, 4, 5 };

int[] copied = source.ToArray();

条件に合う要素だけを配列化する場合にも便利です。

C#
int[] evenNumbers = source
.Where(x => x % 2 == 0)
.ToArray();

ToArrayはクエリの即時評価を行い、結果を含む配列を返します。

Array.Copyは既存配列から既存配列へコピーする方法で、ToArrayはシーケンスから新しい配列を作る方法だと考えると分かりやすいです。

7-4. Buffer.BlockCopyとの違い

Buffer.BlockCopyは、プリミティブ型の配列に対して、バイト単位でコピーするメソッドです。

C#
byte[] source = { 1, 2, 3, 4 };
byte[] destination = new byte[4];

Buffer.BlockCopy(source, 0, destination, 0, source.Length);

Buffer.BlockCopyは、コピーする単位が「要素数」ではなく「バイト数」です。公式ドキュメントでも、指定したバイト数をコピー元配列の特定のオフセットからコピー先配列の特定のオフセットへコピーするメソッドとして説明されています。また、コピー元またはコピー先がプリミティブ型配列でない場合はArgumentExceptionが発生します。

通常の配列要素のコピーならArray.Copy、バイナリデータやプリミティブ配列をバイト単位で扱いたいならBuffer.BlockCopyが候補になります。

7-5. Span<T>.CopyToとの違い

Span<T>.CopyToは、Span<T>の内容を別のSpan<T>へコピーするメソッドです。

C#
int[] source = { 1, 2, 3, 4, 5 };
int[] destination = new int[3];

source.AsSpan(1, 3).CopyTo(destination);

実行後のdestinationは次のようになります。

C#
// { 2, 3, 4 }

Span<T>.CopyToは、コピー先Span<T>がコピー元より短い場合にArgumentExceptionを投げます。また、コピー元とコピー先が重複している場合でもコピーできます。

Span<T>を使うと、配列の一部をビューとして扱えるため、高パフォーマンスなコードや割り当てを抑えたい処理で有効です。ただし、初心者向けの通常の配列コピーでは、まずArray.Copyを理解しておくとよいでしょう。

7-6. 用途別のおすすめコピー方法

配列全体を単純に複製したい場合は、CloneToArrayが簡単です。

C#
int[] copied1 = (int[])source.Clone();
int[] copied2 = source.ToArray();

既存のコピー先配列に一部だけコピーしたい場合は、Array.Copyが向いています。

C#
Array.Copy(source, 2, destination, 0, 3);

コピー元配列の全要素を、コピー先の指定位置から入れたい場合はCopyToが読みやすいです。

C#
source.CopyTo(destination, 2);

バイト単位でプリミティブ配列を扱う場合はBuffer.BlockCopySpan<T>を使った範囲操作や高パフォーマンスな処理ではSpan<T>.CopyToを検討します。

8. Array.Copyの実践例

8-1. 配列の一部を別配列にコピーする

配列の一部を取り出して別配列にコピーする例です。

C#
int[] source = { 100, 200, 300, 400, 500 };

int startIndex = 1;
int count = 3;

int[] result = new int[count];

Array.Copy(source, startIndex, result, 0, count);

Console.WriteLine(string.Join(", ", result));

実行結果は次のとおりです。

200, 300, 400

Array.Copyを使えば、for文を書かずに範囲コピーを表現できます。

8-2. 配列の要素をずらして挿入する

固定長配列で、要素を右へずらして値を挿入する例です。

C#
int[] numbers = { 1, 2, 3, 4, 0 };

int insertIndex = 2;
int insertValue = 99;

// index 2以降の要素を1つ右へずらす
Array.Copy(numbers, insertIndex, numbers, insertIndex + 1, numbers.Length - insertIndex - 1);

numbers[insertIndex] = insertValue;

Console.WriteLine(string.Join(", ", numbers));

実行結果は次のようになります。

1, 2, 99, 3, 4

同じ配列内で範囲が重なるコピーでも、Array.Copyは重なりを考慮して動作します。

8-3. 配列を結合する処理に活用する

2つの配列を結合して、新しい配列を作る例です。

C#
int[] first = { 1, 2, 3 };
int[] second = { 4, 5, 6 };

int[] merged = new int[first.Length + second.Length];

Array.Copy(first, 0, merged, 0, first.Length);
Array.Copy(second, 0, merged, first.Length, second.Length);

Console.WriteLine(string.Join(", ", merged));

実行結果は次のとおりです。

1, 2, 3, 4, 5, 6

1回目のArray.Copyで先頭側にfirstをコピーし、2回目でfirst.Lengthの位置からsecondをコピーしています。

8-4. 配列のバックアップを作成する

配列を変更する前にバックアップを作りたい場合にもArray.Copyは使えます。

C#
int[] original = { 10, 20, 30 };
int[] backup = new int[original.Length];

Array.Copy(original, backup, original.Length);

original[0] = 999;

Console.WriteLine(string.Join(", ", original));
Console.WriteLine(string.Join(", ", backup));

実行結果は次のようになります。

999, 20, 30
10, 20, 30

値型配列の場合、バックアップ配列は元配列の変更の影響を受けません。

8-5. 文字列配列やオブジェクト配列をコピーする

文字列配列も同じようにコピーできます。

C#
string[] source = { "red", "green", "blue" };
string[] destination = new string[source.Length];

Array.Copy(source, destination, source.Length);

Console.WriteLine(string.Join(", ", destination));

実行結果は次のとおりです。

red, green, blue

オブジェクト配列もコピーできます。

C#
object[] source = { 1, "text", true };
object[] destination = new object[source.Length];

Array.Copy(source, destination, source.Length);

ただし、参照型オブジェクトを含む場合は浅いコピーになるため、オブジェクト自体を完全に複製したい場合は別途深いコピーの処理を実装する必要があります。

9. Array.Copyのパフォーマンスと使いどころ

9-1. for文でコピーする場合との違い

配列コピーはfor文でも書けます。

C#
for (int i = 0; i < source.Length; i++)
{
destination[i] = source[i];
}

単純なコピーであれば、Array.Copyを使ったほうが意図が明確です。

C#
Array.Copy(source, destination, source.Length);

Array.Copyは「配列をコピーする」という目的がコードからすぐに分かるため、可読性の面でも有利です。公式ドキュメントでは、Array.Copyはコピーする要素数をnとしたO(n)の操作とされています。

9-2. 大量データをコピーするときの注意点

大量の配列データをコピーするときは、コピー回数に注意が必要です。Array.Copy自体は便利ですが、大きな配列を何度もコピーすると、メモリ使用量や処理時間が増えます。

たとえば、ループ内で毎回新しい配列を作ってコピーするような処理は避けたほうがよい場合があります。

C#
// 大量データでは非効率になりやすい例
for (int i = 0; i < 1000; i++)
{
int[] temp = new int[source.Length];
Array.Copy(source, temp, source.Length);
}

大量データを扱う場合は、コピーの必要性を見直す、同じバッファを再利用する、Span<T>を使って範囲ビューで処理する、といった工夫が有効です。

9-3. 可読性と速度のバランス

通常のアプリケーションでは、まず可読性を重視してArray.Copyを使うのがおすすめです。

C#
Array.Copy(source, sourceIndex, destination, destinationIndex, length);

このコードは、「どこからどこへ何個コピーするか」が明確です。

一方で、特殊な変換処理をしながらコピーしたい場合はfor文のほうが適しています。

C#
for (int i = 0; i < source.Length; i++)
{
destination[i] = source[i] * 2;
}

単純コピーならArray.Copy、加工しながらコピーするならfor文、と使い分けるとよいでしょう。

9-4. List<T>より配列コピーが向いているケース

List<T>は要素の追加や削除が多い場面に向いています。一方、配列はサイズが固定で、インデックスアクセスが中心の処理に向いています。

次のような場面では、Array.Copyを使った配列操作が適しています。

C#
int[] buffer = new int[1024];
int[] backup = new int[1024];

Array.Copy(buffer, backup, buffer.Length);

固定サイズのバッファ、数値データの一括処理、外部APIとのやり取り、パフォーマンスを意識したデータ処理では、配列とArray.Copyの組み合わせが扱いやすいことがあります。

ただし、要素数が頻繁に変わる場合は、配列よりもList<T>を使ったほうがコードが簡潔になります。

10. Array.Copyに関するよくある質問

10-1. Array.Copyは元の配列に影響する?

値型配列を別配列へコピーした場合、コピー先の要素を変更しても元の配列には影響しません。

C#
int[] source = { 1, 2, 3 };
int[] destination = new int[3];

Array.Copy(source, destination, source.Length);

destination[0] = 99;

Console.WriteLine(source[0]); // 1

ただし、参照型配列では、配列は別でも中のオブジェクト参照が同じになる場合があります。その場合、コピー先からオブジェクトの中身を変更すると、コピー元から見える内容も変わります。

10-2. コピー先配列は自動で拡張される?

自動で拡張されません。

C#
int[] source = { 1, 2, 3 };
int[] destination = new int[2];

Array.Copy(source, destination, 3); // エラー

Array.Copyを使う場合は、コピー先配列を十分なサイズで作成しておく必要があります。

C#
int[] destination = new int[source.Length];
Array.Copy(source, destination, source.Length);

10-3. 配列全体をコピーするならどの方法が簡単?

配列全体をコピーするだけなら、次の方法が簡単です。

C#
int[] copied = source.ToArray();

または、Cloneも使えます。

C#
int[] copied = (int[])source.Clone();

既存のコピー先配列へコピーしたい場合は、Array.Copyが適しています。

C#
Array.Copy(source, destination, source.Length);

10-4. nullを含む配列もコピーできる?

はい、コピーできます。配列自体がnullでなければ、要素にnullが含まれていてもコピー可能です。

C#
string[] source = { "A", null, "C" };
string[] destination = new string[3];

Array.Copy(source, destination, source.Length);

Console.WriteLine(destination[1] == null); // True

ただし、コピー元配列またはコピー先配列そのものがnullの場合はArgumentNullExceptionが発生します。

10-5. 参照型の中身まで完全にコピーできる?

Array.Copyだけでは、参照型オブジェクトの中身まで完全にはコピーできません。Array.Copyは参照をコピーするため、深いコピーが必要な場合は、各要素を個別に複製します。

C#
class User
{
public string Name { get; set; }
}

User[] source =
{
new User { Name = "Alice" },
new User { Name = "Bob" }
};

User[] copied = source
.Select(user => new User { Name = user.Name })
.ToArray();

このように、新しいUserオブジェクトを作成して配列化すれば、元のオブジェクトとは別のインスタンスを持つ配列を作れます。

まとめ

Array.Copyは、C#で配列をコピーするための基本的で便利なメソッドです。コピー元配列、コピー先配列、コピーする要素数を指定するシンプルな使い方に加えて、コピー元の開始位置やコピー先の開始位置を指定した範囲コピーにも対応できます。

基本形は次の2つです。

C#
Array.Copy(source, destination, length);
C#
Array.Copy(source, sourceIndex, destination, destinationIndex, length);

配列全体を複製するだけならCloneToArrayも選択肢になりますが、既存の配列に一部だけコピーしたい場合や、配列の途中に要素を挿入するためにずらしたい場合はArray.Copyが非常に便利です。

一方で、コピー先配列のサイズ不足、null、インデックス範囲外、型の不一致、参照型配列の浅いコピーには注意が必要です。特にオブジェクト配列を扱う場合は、Array.Copyでは参照先オブジェクトまでは複製されないことを理解しておきましょう。

c# array.copyを正しく使いこなせるようになると、配列の部分コピー、結合、バックアップ、要素移動などの処理を簡潔に書けるようになります。配列操作の基本として、ぜひ使い方と注意点を押さえておきましょう。