コレクション【C#】

C#文法:コレクション:B05_メソッドの引数などにする場合の注意点

ここまでのレクチャーで、配列を生成する書き方をいくつか見てきました。要素数を指定して生成する書き方、生成と同時に値を設定する書き方、そして型推論を使った書き方です。今回は、これらの配列を「メソッドの引数として渡す」場面に注目します。

変数を宣言するときには問題なく書けていた書き方でも、メソッドの引数にそのまま書こうとするとコンパイルエラーになるものがあります。どの書き方が使えて、どの書き方が使えないのかを、実際にコードを書きながら確認していきましょう。

検証用のボタンを追加する

これまでと同じように、フォームに新しいボタンを追加し、ダブルクリックしてクリックイベントのメソッドを生成します。今回は4つ目のボタンなので、button4_Clickメソッドが作られます。

図1 フォームにボタンを追加してダブルクリックする

図1 フォームにボタンを追加してダブルクリックする

配列を引数に取るメソッドを用意する

まず、検証のための受け側となるメソッドを用意します。「何かメソッドがあって、intの配列が引数になっている」という状況を作りたいので、button4_Clickの下にSetDataという名前のメソッドを追加します。

リスト1 int型の配列を引数に取るSetDataメソッド

private void button4_Click(object sender, EventArgs e)
{
    //メソッドの引数などにする場合は

}

private void SetData(int[] values)
{

}

引数の型はint[]、引数名はvaluesとしました。メソッドの中身は空のままでかまいません。今回確認したいのは「呼び出し側でどう書けば配列を渡せるか」という一点だけだからです。

図2 int[]を引数に取るSetDataメソッドを追加した状態

図2 int[]を引数に取るSetDataメソッドを追加した状態

これまでの5つの書き方をおさらいする

次に、これまでのレクチャーで書いてきた配列の生成コードをbutton4_Clickに集めます。「生成と同時に値を設定する」のレクチャーで書いたhaiAからhaiDまでの4行と、「型推論による生成」のレクチャーで書いたhaiEの1行を、それぞれコピーして貼り付けます。

図3 button2_ClickからhaiA〜haiDの4行をコピーする

図3 button2_ClickからhaiA〜haiDの4行をコピーする

リスト2 これまでに登場した5つの書き方

private void button4_Click(object sender, EventArgs e)
{
    //メソッドの引数などにする場合は
    int[] haiA = new int[10];
    int[] haiB = new int[] { 11, 22, 33, 44 };
    var haiC = new int[] { 11, 22, 33, 44 };
    int[] haiD = { 11, 22, 33, 44 };
    var haiE = new[] { 10, 22, 99 };
}

図4 A〜Eの5つの書き方をbutton4_Clickに並べた状態

図4 A〜Eの5つの書き方をbutton4_Clickに並べた状態

それぞれの特徴を、右辺の書き方に注目して整理しておきます。

Aは、new int[10]のように要素数を指定して生成する書き方です。値は設定していないので、10個の要素はすべて既定値のままです。

Bは、new int[] { 11, 22, 33, 44 }のように、newに続けて「型+角括弧」を書き、中括弧で初期値を指定する書き方です。

Cは、右辺がBとまったく同じです。違いは左辺をint[]と書くかvarと書くかだけですので、「右辺の書き方」としてはBと同一と考えてかまいません。

Dは、newを書かず、中括弧と値の並びだけを右辺に書く書き方です。配列の型は左辺のint[]で指定しています。

Eは、new[] { 10, 22, 99 }のように配列の要素の型を書かず、中括弧の中の値から型を推論させる書き方です。

SetDataメソッドに渡してみる

では、このSetDataメソッドに配列を渡す場合、どの書き方が使えるのかを順番に試していきます。

Aの書き方:変数を渡す

まず、宣言済みの変数haiAを渡してみます。これはもちろん問題なく渡せます。

リスト3 宣言済みの変数を渡す

SetData(haiA);

図5 変数haiAをSetDataに渡す

図5 変数haiAをSetDataに渡す

ただし、haiAは要素数を指定して生成しただけで、まだ値をセットしていません。実際のプログラムでは、何かしらの値をセットしてから渡す形になるのが普通です。

Bの書き方:new int[] { … } を直接渡す

次に、変数を宣言せずに、Bの右辺の書き方をそのまま引数の位置に書いてみます。

リスト4 Bの書き方を引数に直接書く

SetData(new int[] { 11, 22, 33, 44 });

これはOKです。コンパイルエラーにはなりません。CはBと右辺が同じなので、結果も同じです。

図6 Bの書き方(new int[] { ... })は引数に直接書ける

図6 Bの書き方(new int[] { … })は引数に直接書ける

Dの書き方:{ … } だけを渡す

続いて、Dの右辺の書き方、つまり中括弧と値の並びだけを引数の位置に書いてみます。

リスト5 Dの書き方を引数に直接書く(コンパイルエラー)

//コンパイルエラーになる
SetData({ 11, 22, 33, 44 });

これはコンパイルエラーになります。変数の宣言では問題なく書けていた書き方ですが、メソッドの引数としては使えません。

図7 Dの書き方({ ... }のみ)を引数に書くとコンパイルエラーになる

図7 Dの書き方({ … }のみ)を引数に書くとコンパイルエラーになる

Eの書き方:new[] { … } を直接渡す

最後に、Eの右辺の書き方です。Dの行はコンパイルが通らないのでコメントアウトしておき、その下に書きます。

リスト6 4通りの渡し方をまとめたコード

private void button4_Click(object sender, EventArgs e)
{
    //メソッドの引数などにする場合は
    int[] haiA = new int[10];
    int[] haiB = new int[] { 11, 22, 33, 44 };
    var haiC = new int[] { 11, 22, 33, 44 };
    int[] haiD = { 11, 22, 33, 44 };
    var haiE = new[] { 10, 22, 99 };

    SetData(haiA);
    SetData(new int[] { 11, 22, 33, 44 });
    //SetData({ 11, 22, 33, 44 });
    SetData(new[] { 10, 22, 99 });
}

private void SetData(int[] values)
{

}

Eの書き方も問題なく書けました。このように、D以外はすべてOKという結果になります。

図8 Dをコメントアウトし、Eの書き方(new[] { ... })で渡す

図8 Dをコメントアウトし、Eの書き方(new[] { … })で渡す

デバッグ実行で渡された値を確認する

D以外は実際に動くことを、デバッグ実行で確かめてみます。SetData(haiA);の行と、SetDataメソッドの中にブレークポイントを置いて実行し、フォームの「メソッドの引数などにする場合」ボタンをクリックします。

図9 SetData(haiA)の行で実行が止まったところ

図9 SetData(haiA)の行で実行が止まったところ

実行を進めてSetDataメソッドの中で止まったところで、引数valuesにマウスカーソルを合わせて中身を確認します。Bの書き方で渡した呼び出しでは、valuesはint[4]になっており、インデックス0から順に11、22、33、44が入っていることがわかります。

図10 Bの書き方で渡した配列(11, 22, 33, 44)が届いている

図10 Bの書き方で渡した配列(11, 22, 33, 44)が届いている

さらに実行を進めると、今度はEの書き方で渡した呼び出しで止まります。valuesはint[3]で、10、22、99が入っています。型を書かずにnew[]とした場合でも、ちゃんとintの配列として渡ってきていますね。

図11 Eの書き方で渡した配列(10, 22, 99)が届いている

図11 Eの書き方で渡した配列(10, 22, 99)が届いている

もう一度ボタンをクリックして、最初のSetData(haiA)の呼び出しも見ておきます。こちらはint[10]、つまり要素数10の配列が渡ってきていますが、値は何も設定していないので中身は空っぽ(すべて既定値)の状態です。

図12 Aの書き方で渡した配列は要素数10で値が未設定

図12 Aの書き方で渡した配列は要素数10で値が未設定

Aの書き方は、値を設定しないままでも渡せてしまいます。それで問題ない場面であればかまいませんが、基本的には何かしら値をセットしてからhaiAを渡すことになるでしょう。

なぜDの書き方だけ渡せないのか

Dの書き方は、中括弧と値の羅列だけで成り立っています。変数の宣言で使う場合は、左辺にint[]という型が書いてあるので、コンパイラは「これはintの配列の初期値だな」と判断できます。

ところが、メソッドの引数の位置にいきなり{ 11, 22, 33, 44 }と書かれても、そこには型の情報もnewもありません。コンパイラからすると「これは何のこと?」という状態になり、配列を生成する式として理解できないのです。

リスト7 Dの書き方が使える場所と使えない場所

//変数の宣言と同時に書く場合はOK(左辺で型がわかる)
int[] haiD = { 11, 22, 33, 44 };

//宣言済みの変数を渡すのはもちろんOK
SetData(haiD);

//引数に直接書くのはNG(型もnewもないので配列と解釈できない)
//SetData({ 11, 22, 33, 44 });

一方、BやEの書き方にはnew int[]やnew[]が付いています。newがあることで「ここで配列を生成している」ということがコンパイラに伝わるため、変数宣言の右辺でなくても、引数の位置にそのまま書けるというわけです。

図13 引数に直接書けるのはBの書き方とEの書き方

図13 引数に直接書けるのはBの書き方とEの書き方

まとめ

一度変数として宣言してしまえば、SetData(haiA)やSetData(haiD)のように、どの書き方で作った配列でも引数に渡せます。違いが出るのは、変数を宣言せずに、いきなり値を指定して渡したい場合です。

書き方 右辺の形 変数宣言 引数に直接書く
A new int[10] ○ ○(値は未設定)
B/C new int[] { 11, 22, 33, 44 } ○ ○
D { 11, 22, 33, 44 } ○ ×(コンパイルエラー)
E new[] { 10, 22, 99 } ○ ○

変数を宣言せずに値の入った配列をそのまま渡したいときは、Bの書き方(new int[] { ... })か、Eの書き方(new[] { ... })を使う、と覚えておいてください。

補足:コレクション式での渡し方(参考)

動画の中で、配列の初期化部分にカーソルを合わせると「IDE0300: コレクションの初期化を簡素化できます」というヒントが表示されている場面がありました。これは、C# 12で追加された「コレクション式」という角括弧の書き方に置き換えられる、という提案です。

コレクション式は、Dの書き方と違って引数の位置にも直接書けます。渡す先の引数の型(ここではint[])から、コンパイラが生成する型を判断してくれるためです。

リスト8 コレクション式で配列を渡す(参考)

//コレクション式(C# 12以降)
int[] haiF = [11, 22, 33, 44];

//引数にも直接書ける
SetData([11, 22, 33, 44]);

本レクチャーでは扱っていない書き方ですが、新しいプロジェクトではこの形を目にする機会も増えていますので、参考として押さえておくとよいでしょう。

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新機能」を観る