HOWTO · Csharp

C# でオブジェクトの配列を宣言する

この記事では、C# でオブジェクトの配列を宣言する方法を学習します。

このページの内容

C#では、配列は固定数の要素を格納し、宣言された要素型によって各スロットが参照できる対象が決まります。Employee[] のような型付き配列には Employee インスタンスへの参照が格納されるため、コンパイラーはそのメンバーへの直接アクセスを許可します。配列の確保とオブジェクトの生成は別の処理です。配列を確保するとスロットが作られ、new Employee(...) によって各従業員が生成されます。

C#におけるオブジェクト配列の意味

「オブジェクトの配列」は、Employee[] のように1つのクラスのインスタンスを格納する配列を指す場合と、異なる型の値を参照できる要素を持つ object[] を指す場合があります。どちらも配列ですが、コンパイル時に保証される内容は異なります。いずれの場合も、配列の長さは確保後に固定されます。要素数を変えるには別の配列を作成するか、List<T> のようなコレクションを使います。

クラスインスタンスの型付き配列を宣言して初期化する

すべての要素が同じクラスのインスタンスである場合は、型付き配列を使います。この簡潔なプログラムでは、2つの要素を初期化し、配列の要素を通じてプロパティを読み取ります。

次の例を実行してください。

このセクションのコードと表示されるラベルや出力は英語のままです。

using System;

class Employee
{
    public string Name { get; }

    public Employee(string name) => Name = name;
}

class Program
{
    static void Main()
    {
        Employee[] employees = new Employee[2];
        employees[0] = new Employee("Shan");
        employees[1] = new Employee("Casper");

        Console.WriteLine(employees[0].Name);
        Console.WriteLine(employees[1].Name);
    }
}
Shan
Casper

new Employee[2] は参照を格納するスロットを2つ確保します。続く2つの代入でインスタンスを生成し、それぞれの参照をスロットに格納します。各要素のコンパイル時の型は Employee なので、employees[0].Name のようなメンバーアクセスにキャストは必要ありません。引数なしのコンストラクターを持つクラスなら、同じ初期化を配列初期化子で記述できます。Employee[] employees = { new Employee(), new Employee() };。出力される Shan と Casper は、コード内の文字列のとおり英語で表示されます。

object[] に異なる型の値を格納する

object[] には型の異なる値を格納できます。これは、C#のすべての型が object を継承するか、値型の場合はボックス化によって object として表されるためです。この例では、代入時に double、char、int がボックス化され、最後のスロットには null 参照が格納されます。出力では、オブジェクトの既定の表示形式に頼らず、それぞれの実行時の型を示します。

次の例を実行してください。

このセクションのコードと表示されるラベルや出力は英語のままです。

using System;

class Program
{
    static void Main()
    {
        object[] values = { 2.15, 'S', 27, null, new object() };

        for (int i = 0; i < values.Length; i++)
        {
            if (values[i] == null)
                Console.WriteLine("null");
            else
                Console.WriteLine(values[i].GetType().Name);
        }
    }
}
Double
Char
Int32
null
Object

配列には実行時の型が異なる値が格納されていますが、コンパイル時の要素型は object のままです。たとえば、コンパイラーはスロットの具体的な型を知らないため、values[0].Name はコンパイルできません。パターンマッチングで型を確認するか、値が期待する型だと分かっている場合に限ってキャストします。if (values[0] is double amount) Console.WriteLine(amount);。要素が同じドメイン型を表すなら、無関係な値の代入を防ぎ、繰り返し型を確認する必要もない型付き配列を優先してください。

要素型、参照、null 値を理解する

参照型の配列を作成すると、すべてのスロットは null に設定されます。そのため、new Employee[2] だけでは従業員が2人生成されるわけではありません。スロットを通じてインスタンスメンバーを呼び出す前に、そのスロットを初期化してください。スロットを意図的に null のままにすることもできますが、参照をたどる前にコードで確認する必要があります。同じオブジェクト参照を2つのスロットに代入すると、両方のスロットが同じインスタンスを参照します。そのため、どちらの参照を通じて変更しても、その変更はもう一方からも見えます。

int、char、double などの値型は、値をオブジェクトとして包むボックス化によって object[] に格納できます。ボックス化された値を元の値型として読み取るには、適切な型判定またはキャストが必要です。一方、型付きの int[] は整数値を直接格納し、整数以外の値の代入を制限します。格納するデータを表す型を選んでください。

型付き配列と object[] を使い分ける

すべての要素で同じメンバーを使う場合は、Employee[](または特定のクラスの配列)を選びます。コンパイル時に有用なチェックが行われ、メンバーにも簡単にアクセスできます。object[] は、オブジェクトを受け取る API に値を渡す場合など、異なる型の値が本当に必要なときに限って使います。その場合、受け取る側で各値の実行時の型を判別して処理する必要があります。同じ型の要素からなるサイズ変更可能な列が必要なら、固定長配列を繰り返し確保する代わりに List<Employee> を使います。いずれの場合も、クラスインスタンスは配列とは別に生成し、使用前に null のスロットを考慮してください。