配列

配列データ構造体には、同じ型の複数の変数を格納できます。 配列は、要素の型を指定することで宣言します。 配列に任意の型の要素を格納する場合は、その型として object を指定できます。 C# の統一型システムでは、すべての型 (定義済み、ユーザー定義、参照型、および値の型) が、直接または間接的に Object を継承します。

type[] arrayName;

配列には、次の特徴があります。

  • 配列は、1 次元多次元、またはジャグのいずれかになります。
  • 次元数は、配列変数の宣言時に設定されます。 各次元の長さは、配列インスタンスの作成時に設定されます。 インスタンスの有効期間中にこれらの値を変更することはできません。
  • ジャグ配列は配列の配列であり、各メンバー配列の既定値は null になります。
  • 配列には、ゼロから始まるインデックスが付けられます。n 個の要素を含む配列には、0 から n-1 までのインデックスが付けられます。
  • 配列の要素および配列型は、どのような型でもかまいません。
  • 配列型は、抽象基本型 Array から派生した参照型です。 すべての配列は IList および IEnumerable を実装します。 配列を反復処理するための foreach ステートメントを追加することができます。 1 次元配列でも IList<T> および IEnumerable<T> が実装されています。

配列の要素は、配列の作成時に既知の値に初期化できます。 C# 12 以降では、コレクション式を使用して、すべてのコレクション型を初期化できます。 初期化されていない要素には、既定値が設定されます。 既定値は、0 ビット パターンです。 すべての参照型 (null 非許容型を含む) に、null 値が含まれます。 すべての値型に 0 ビット パターンが含まれます。 つまり、Nullable<T>.HasValue プロパティが false で、Nullable<T>.Value プロパティが未定義ということです。 .NET 実装では、 Value プロパティは例外をスローします。

次の例では、1 次元配列、多次元配列、およびジャグ配列を作成しています。

// Declare a single-dimensional array of 5 integers.
int[] array1 = new int[5];

// Declare and set array element values.
int[] array2 = [1, 2, 3, 4, 5, 6];

// Declare a two dimensional array.
int[,] multiDimensionalArray1 = new int[2, 3];

// Declare and set array element values.
int[,] multiDimensionalArray2 = { { 1, 2, 3 }, { 4, 5, 6 } };

// Declare a jagged array.
int[][] jaggedArray = new int[6][];

// Set the values of the first array in the jagged array structure.
jaggedArray[0] = [1, 2, 3, 4];

1 次元配列

1 次元配列は、同様の要素のシーケンスです。 インデックスを使用して要素にアクセスします。 インデックスは、シーケンス内の序数位置です。 配列における最初の要素は、インデックス 0 です。 配列要素の型と要素の数を指定する new 演算子を使用して、1 次元配列を作成します。 次の例では、1 次元配列を宣言して初期化しています。

int[] array = new int[5];
string[] weekDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];

Console.WriteLine(weekDays[0]);
Console.WriteLine(weekDays[1]);
Console.WriteLine(weekDays[2]);
Console.WriteLine(weekDays[3]);
Console.WriteLine(weekDays[4]);
Console.WriteLine(weekDays[5]);
Console.WriteLine(weekDays[6]);

/*Output:
Sun
Mon
Tue
Wed
Thu
Fri
Sat
*/

最初の宣言では、array[0] から array[4]までの 5 つの整数の初期化されていない配列を宣言します。 配列の要素は、要素型の既定値である整数 0 に初期化されます。 2 番目の宣言では、文字列の配列を宣言し、その配列の 7 つの値をすべて初期化します。 foreach ステートメントは、weekday 配列の要素を反復処理し、すべての値を出力します。 1 次元配列の場合、foreach ステートメントは、インデックス 0 から始まりインデックス Length - 1 で終わるインデックスの昇順で要素を処理します。

1 次元配列を引数として渡す

初期化された 1 次元配列をメソッドに渡すことができます。 次の例では、文字列の配列が初期化され、引数として文字列の DisplayArray メソッドに渡されます。 このメソッドは、配列の要素を表示します。 次に、ChangeArray メソッドで配列の要素を反転させた後、ChangeArrayElements メソッドで配列の最初の 3 つの要素を変更します。 各メソッドから戻った後、DisplayArray メソッドで、配列を値で渡すと配列要素の変更が禁止されないことを示します。

class ArrayExample
{
    static void DisplayArray(string[] arr) => Console.WriteLine(string.Join(" ", arr));

    // Change the array by reversing its elements.
    static void ChangeArray(string[] arr) => Array.Reverse(arr);

    static void ChangeArrayElements(string[] arr)
    {
        // Change the value of the first three array elements.
        arr[0] = "Mon";
        arr[1] = "Wed";
        arr[2] = "Fri";
    }

    static void Main()
    {
        // Declare and initialize an array.
        string[] weekDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
        // Display the array elements.
        DisplayArray(weekDays);
        Console.WriteLine();

        // Reverse the array.
        ChangeArray(weekDays);
        // Display the array again to verify that it stays reversed.
        Console.WriteLine("Array weekDays after the call to ChangeArray:");
        DisplayArray(weekDays);
        Console.WriteLine();

        // Assign new values to individual array elements.
        ChangeArrayElements(weekDays);
        // Display the array again to verify that it has changed.
        Console.WriteLine("Array weekDays after the call to ChangeArrayElements:");
        DisplayArray(weekDays);
    }
}
// The example displays the following output:
//         Sun Mon Tue Wed Thu Fri Sat
//
//        Array weekDays after the call to ChangeArray:
//        Sat Fri Thu Wed Tue Mon Sun
//
//        Array weekDays after the call to ChangeArrayElements:
//        Mon Wed Fri Wed Tue Mon Sun

多次元配列

配列は 1 つ以上の配列を持つことができます。 たとえば、次の宣言では 4 つの配列が作成されます。2 つには 2 つの次元があり、2 つには 3 つの次元があります。 最初の 2 つの宣言は各次元の長さを宣言しますが、配列の値は初期化しません。 2 番目の 2 つの宣言では、初期化子を使用して、多次元配列内の各要素の値を設定しています。

int[,] array2DDeclaration = new int[4, 2];

int[,,] array3DDeclaration = new int[4, 2, 3];

// Two-dimensional array.
int[,] array2DInitialization =  { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } };
// Three-dimensional array.
int[,,] array3D = new int[,,] { { { 1, 2, 3 }, { 4,   5,  6 } },
                                { { 7, 8, 9 }, { 10, 11, 12 } } };

// Accessing array elements.
System.Console.WriteLine(array2DInitialization[0, 0]);
System.Console.WriteLine(array2DInitialization[0, 1]);
System.Console.WriteLine(array2DInitialization[1, 0]);
System.Console.WriteLine(array2DInitialization[1, 1]);

System.Console.WriteLine(array2DInitialization[3, 0]);
System.Console.WriteLine(array2DInitialization[3, 1]);
// Output:
// 1
// 2
// 3
// 4
// 7
// 8

System.Console.WriteLine(array3D[1, 0, 1]);
System.Console.WriteLine(array3D[1, 1, 2]);
// Output:
// 8
// 12

// Getting the total count of elements or the length of a given dimension.
var allLength = array3D.Length;
var total = 1;
for (int i = 0; i < array3D.Rank; i++)
{
    total *= array3D.GetLength(i);
}
System.Console.WriteLine($"{allLength} equals {total}");
// Output:
// 12 equals 12

多次元配列の場合、右端の次元のインデックスが最初に加算されていき、次にその左の次元、またその左、左端のインデックスまでというような方法で各要素がトラバースされます。 次の例では、2 次元配列と 3 次元配列の両方を列挙しています。

int[,] numbers2D = { { 9, 99 }, { 3, 33 }, { 5, 55 } };

foreach (int i in numbers2D)
{
    System.Console.Write($"{i} ");
}
// Output: 9 99 3 33 5 55

int[,,] array3D = new int[,,] { { { 1, 2, 3 }, { 4,   5,  6 } },
                        { { 7, 8, 9 }, { 10, 11, 12 } } };
foreach (int i in array3D)
{
    System.Console.Write($"{i} ");
}
// Output: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12

2 次元配列では、左のインデックスは、右のインデックスはと考えることができます。

しかし、多次元配列では、入れ子になった for ループを使用した方が、配列要素を処理する順序をより厳密に制御できます。

int[,,] array3D = new int[,,] { { { 1, 2, 3 }, { 4,   5,  6 } },
                        { { 7, 8, 9 }, { 10, 11, 12 } } };

for (int i = 0; i < array3D.GetLength(0); i++)
{
    for (int j = 0; j < array3D.GetLength(1); j++)
    {
        for (int k = 0; k < array3D.GetLength(2); k++)
        {
            System.Console.Write($"{array3D[i, j, k]} ");
        }
        System.Console.WriteLine();
    }
    System.Console.WriteLine();
}
// Output (including blank lines): 
// 1 2 3
// 4 5 6
// 
// 7 8 9
// 10 11 12
//

多次元配列を引数として渡す

1 次元配列を渡すのと同じ方法で、初期化された多次元配列をメソッドに渡します。 次のコードに、2 次元配列を引数として受け取る print メソッドの宣言の一部を示します。 次の例に示すように、一度に新しい配列を初期化して渡すことができます。 次の例では、整数の 2 次元配列が初期化され、Print2DArray メソッドに渡されます。 このメソッドは、配列の要素を表示します。

static void Print2DArray(int[,] arr)
{
    // Display the array elements.
    for (int i = 0; i < arr.GetLength(0); i++)
    {
        for (int j = 0; j < arr.GetLength(1); j++)
        {
            System.Console.WriteLine("Element({0},{1})={2}", i, j, arr[i, j]);
        }
    }
}
static void ExampleUsage()
{
    // Pass the array as an argument.
    Print2DArray(new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } });
}
/* Output:
    Element(0,0)=1
    Element(0,1)=2
    Element(1,0)=3
    Element(1,1)=4
    Element(2,0)=5
    Element(2,1)=6
    Element(3,0)=7
    Element(3,1)=8
*/

ジャグ配列

ジャグ配列は、その要素がサイズが異なる場合がある配列からなる配列です。 ジャグ配列は、"配列の配列" と呼ばれることがあります。その要素は参照型であり、null に初期化されます。 次の例では、ジャグ配列の宣言、初期化、およびアクセスの方法について説明します。 最初の例の jaggedArray は、1 つのステートメントで宣言されています。 含まれる各配列は、後続のステートメントで作成されます。 2 番目の例の jaggedArray2 は、1 つのステートメントで宣言され、初期化されます。 ジャグ配列と多次元配列を混在させることができます。 最後の例の jaggedArray3 は、異なるサイズの 3 つの 2 次元の配列要素を含む、1 次元のジャグ配列の宣言と初期化です。

int[][] jaggedArray = new int[3][];

jaggedArray[0] = [1, 3, 5, 7, 9];
jaggedArray[1] = [0, 2, 4, 6];
jaggedArray[2] = [11, 22];

int[][] jaggedArray2 = 
[
    [1, 3, 5, 7, 9],
    [0, 2, 4, 6],
    [11, 22]
];

// Assign 77 to the second element ([1]) of the first array ([0]):
jaggedArray2[0][1] = 77;

// Assign 88 to the second element ([1]) of the third array ([2]):
jaggedArray2[2][1] = 88;

int[][,] jaggedArray3 =
[
    new int[,] { {1,3}, {5,7} },
    new int[,] { {0,2}, {4,6}, {8,10} },
    new int[,] { {11,22}, {99,88}, {0,9} }
];

Console.Write("{0}", jaggedArray3[0][1, 0]);
Console.WriteLine(jaggedArray3.Length);

ジャグ配列の要素は、使用する前に初期化する必要があります。 各要素はそれ自体が配列です。 初期化子を使用して、配列要素に値を入力することもできます。 初期化子を使用する場合、配列サイズは必要ありません。

この例では、要素自体が配列である配列を構築します。 配列の要素のそれぞれのサイズが異なります。

// Declare the array of two elements.
int[][] arr = new int[2][];

// Initialize the elements.
arr[0] = [1, 3, 5, 7, 9];
arr[1] = [2, 4, 6, 8];

// Display the array elements.
for (int i = 0; i < arr.Length; i++)
{
    System.Console.Write("Element({0}): ", i);

    for (int j = 0; j < arr[i].Length; j++)
    {
        System.Console.Write("{0}{1}", arr[i][j], j == (arr[i].Length - 1) ? "" : " ");
    }
    System.Console.WriteLine();
}
/* Output:
    Element(0): 1 3 5 7 9
    Element(1): 2 4 6 8
*/

暗黙的に型指定される配列

暗黙的に型指定された配列を作成できます。この場合、配列インスタンスの型が、配列初期化子で指定された要素から推論されます。 暗黙的に型指定された変数の規則は、暗黙的に型指定された配列にも適用されます。 詳細については、「暗黙的に型指定されるローカル変数」を参照してください。

次の例では、暗黙的に型指定された配列を作成する方法を示します。

int[] a = new[] { 1, 10, 100, 1000 }; // int[]

// Accessing array
Console.WriteLine("First element: " + a[0]);
Console.WriteLine("Second element: " + a[1]);
Console.WriteLine("Third element: " + a[2]);
Console.WriteLine("Fourth element: " + a[3]);
/* Outputs
First element: 1
Second element: 10
Third element: 100
Fourth element: 1000
*/

var b = new[] { "hello", null, "world" }; // string[]

// Accessing elements of an array using 'string.Join' method
Console.WriteLine(string.Join(" ", b));
/* Output
hello  world
*/

// single-dimension jagged array
int[][] c =
[
    [1,2,3,4],
    [5,6,7,8]
];
// Looping through the outer array
for (int k = 0; k < c.Length; k++)
{
    // Looping through each inner array
    for (int j = 0; j < c[k].Length; j++)
    {
        // Accessing each element and printing it to the console
        Console.WriteLine($"Element at c[{k}][{j}] is: {c[k][j]}");
    }
}
/* Outputs
Element at c[0][0] is: 1
Element at c[0][1] is: 2
Element at c[0][2] is: 3
Element at c[0][3] is: 4
Element at c[1][0] is: 5
Element at c[1][1] is: 6
Element at c[1][2] is: 7
Element at c[1][3] is: 8
*/

// jagged array of strings
string[][] d =
[
    ["Luca", "Mads", "Luke", "Dinesh"],
    ["Karen", "Suma", "Frances"]
];

// Looping through the outer array
int i = 0;
foreach (var subArray in d)
{
    // Looping through each inner array
    int j = 0;
    foreach (var element in subArray)
    {
        // Accessing each element and printing it to the console
        Console.WriteLine($"Element at d[{i}][{j}] is: {element}");
        j++;
    }
    i++;
}
/* Outputs
Element at d[0][0] is: Luca
Element at d[0][1] is: Mads
Element at d[0][2] is: Luke
Element at d[0][3] is: Dinesh
Element at d[1][0] is: Karen
Element at d[1][1] is: Suma
Element at d[1][2] is: Frances
*/

前の例で、暗黙的に型指定された配列では、初期化ステートメントの左側では角かっこが使用されていないことに注意してください。 また、ジャグ配列も、1 次元の配列と同じように new [] を使用して初期化されます。

配列を含む匿名型を作成するときには、型のオブジェクトの初期化子で配列を暗黙的に型指定する必要があります。 次の例では、contacts は、匿名型の暗黙的に型指定された配列で、それぞれが PhoneNumbers という名前の配列を含んでいます。 var キーワードは、オブジェクト初期化子内で使用されません。

var contacts = new[]
{
    new
    {
        Name = "Eugene Zabokritski",
        PhoneNumbers = new[] { "206-555-0108", "425-555-0001" }
    },
    new
    {
        Name = "Hanying Feng",
        PhoneNumbers = new[] { "650-555-0199" }
    }
};