.net column

.NET開発者のためのブログメディア
ソフトウェア開発

C#で可変長引数のメソッドを作る方法|注意点も解説

2020年07月22日

SE
C#の可変長引数のメソッドを作りたいと思っています。

PM
可変長引数のparamsをintやstringなど他の型と合わせて使用する例をみてみましょう。

C#の可変長引数とは

C#でメソッドの引数を決まった数ではなく、そのメソッドを呼び出す側で好きなだけ追加できるようにしたいことがあります。そんな時に役立つのが可変長引数のparamsです。以下がサンプルです。なおソースの先頭には「using System;」が必要です(Visual Studioであれば最初から入っています)。また、そのまま実行するためにはSystemを付与する必要があります。使用する際は、全体的にSystemを付与し使用しましょう。

public static void DispValues(params int[] ary)
{
for (int i = 0; i < ary.Length; i++)
{
System.Console.Write(ary[i] + “” “”);
}
System.Console.WriteLine(); // 最後に改行する
}

static void Main(string[] args)
{
DispValues(1, 2, 3);
DispValues(4, 5, 6, 7, 8, 9, 10);
}

可変長引数を実現するparams

上のサンプルを実行すると以下のように表示されます。

1 2 3
4 5 6 7 8 9 10

DispValuesメソッドの引数のparamsによって、好きな数の引数を指定できるのです。int型だけでなく、以下のようにstringなどの他の型も使用できます。

public static void DispStrings(params string[] ary)
{
for (int i = 0; i < ary.Length; i++)
{
Console.Write(ary[i] + “” “”);
}
Console.WriteLine();
}

普通の引数と可変長引数を組み合わせて使うこともできる

以下のように普通の引数と可変長引数を組み合わせて使用することもできます。DispNamesの最初のstring keisyoは普通の引数です。

public static void DispNames(string keisyo, params string[] ary)
{
for (int i = 0; i < ary.Length; i++)
{
Console.WriteLine(ary[i] + keisyo);
}
}

static void Main(string[] args)
{
DispNames(“”さん””, “”太郎””, “”花子””, “”一郎””);
}

結果は以下になります。

太郎さん
花子さん
一郎さん

なおparamsは一番右端にしか使えません。paramsに続いて,(カンマ)で引数を追加しようとしても、エラーになります。

object型の可変長引数なら様々な型を指定できる

C#では以下のように可変長引数の型をobjectにすると、様々な型の値を引数として与えられます。このようにint型などの基本型をobject型にできるC#の機能を「ボックス化」「ボクシング」と呼びます。

public static void DispObjects(params object[] ary)
{
for (int i = 0; i < ary.Length; i++)
{
Console.Write(ary[i] + “” “”);
}
Console.WriteLine();
}

static void Main(string[] args)
{
DispObjects(“”太郎””, 30, ‘M’);
DispObjects(“”花子””, 24, ‘F’);
}

ボックス化は手軽だが必要な時のみ使おう

上のサンプルを実行すると以下のようになります。

太郎 30 M
花子 24 F

objectによるボックス化は型を考えなくてよくなるので楽ですが、パフォーマンス(処理速度)はintなどの基本型より悪くなります。またobjectにした値を元の型に戻したい場合、元はどんな型だったかをチェックしなければなりません。

以下はobject型のparamの元の型をisでチェックして、それに応じた処理を行うサンプルです。

public static void DispTypeAndSum(params object[] ary)
{
int sum = 0;
for (int i = 0; i < ary.Length; i++)
{
if (ary[i] is string)
{
Console.WriteLine(ary[i] + “” is string””);
}
if (ary[i] is int)
{
Console.WriteLine(ary[i] + “” is int””);
sum += (int)ary[i]; // int型の時だけ合計する
}
if (ary[i] is char)
{
Console.WriteLine(ary[i] + “” is char””);
}
}

Console.WriteLine(“”int value sum is “” + sum);
}

static void Main(string[] args)
{
DispTypeAndSum(“”桜””, 10, ‘S’, 20);
}

結果は以下のようになります。

桜 is string
10 is int
S is char
20 is int
int value sum is 30

可変長引数がオーバーロードと競合した場合

ここまで読んできて、C#をある程度理解している人は「可変長引数がオーバーロードと競合したらどうなるのだろう?」と思ったのではないでしょうか。C#はオーバーロードという、以下のようにメソッド名が同じでも引数の数や型の違いによって、行う処理を切り替えられる機能があります。

public static void IntWork(int a)
{
Console.WriteLine(a);
}

public static void IntWork(int a, int b)
{
Console.WriteLine(a+b);
}

public static void IntWork(string s)
{
Console.WriteLine(“”文字列には対応していません。””);
}

引数の数についての優先度

引数の数を複数持つことが出来るオーバーロードと可変長引数は機能として競合しています。C#ではどちらが優先されるか調べましょう。以下をご覧ください。

public static void DispVal(int a, int b)
{
Console.WriteLine(a + “” “” + b+”” 2params.””);
}

public static void DispVal(params int[] ary)
{
for (int i = 0; i < ary.Length; i++)
{
Console.Write(ary[i] + “” “”);
}
Console.WriteLine(ary.Length + “”params.””);
}

static void Main(string[] args)
{
DispVal(1, 2);
DispVal(1, 2, 3);
}

引数の型についての優先度

上のサンプルを実行すると以下になります。

1 2 2params.
1 2 3 3params.

引数が2つの時は、可変長引数ではない方が優先されていますね。では型の違いではどうでしょうか。以下のサンプルを見てください。

public static void DispNameAndAge(System.String n, int a)
{
Console.WriteLine(n + “”は”” + a +””歳です。””);
}

public static void DispNameAndAge(params object[] ary)
{
for (int i = 0; i < ary.Length; i++)
{
Console.Write(ary[i] + “” “”);
}
Console.WriteLine();
}

static void Main(System.String[] args)
{
DispNameAndAge(“”花子””, 20);
DispNameAndAge(20, “”花子””);
}

数でも型でも可変長引数の優先度は低い

上のサンプルの結果は以下になります。

花子は20歳です。
20 花子

引数がString型、int型の場合は可変引数ではない方が呼ばれ、そうでない場合は可変長引数の方が呼ばれます。可変長引数は数でも型でも優先度は低いということです。なおC#には「デフォルト引数」という機能がありますが、その場合の優先度はどうなるのでしょうか。

デフォルト引数と可変引数の優先度

以下のサンプルをご覧ください。1つ目のDispValは3つ目の引数にデフォルト値があります。

public static void DispVal(int a, int b, int c = 10)
{
Console.WriteLine(a + “” “” + b + “” “” + c + “” 第3引数はデフォルト値””);
}

public static void DispVal(params int[] ary)
{
for (int i = 0; i < ary.Length; i++)
{
Console.Write(ary[i] + “” “”);
}
Console.WriteLine();
}

static void Main(string[] args)
{
DispVal(1);
DispVal(1, 2);
DispVal(1, 2, 3);
DispVal(1, 2, 3, 4);
}

可変長引数の優先度は最低

上のサンプルを実行すると、以下の結果になります。

1
1 2 10 第3引数はデフォルト値
1 2 3 第3引数はデフォルト値
1 2 3 4

引数が2つと3つの時はデフォルト引数があるDispValが呼ばれています。可変長引数の優先度が低いことが分かりますね。ちなみにデフォルト引数ありと無しでは、無しの普通のメソッドの方が優先度が高くなります。つまり優先度は以下になります。

1 デフォルト引数無し
2 デフォルト引数あり
3 可変長引数

SE
可変長数は優先度が低いので、使える場所が限られています。

PM
確かに拡張性は高くありませんが、適所に使えるようになるといいですね。

可変長引数は特に必要な場所だけで使おう

C#の可変長引数について説明してきましたがいかがでしたでしょうか。C#の可変長引数はオーバーロードにおいて優先度が低いので、拡張性が高くありません。可変長引数がどうしても必要な場所だけで使用するのが良いでしょう。


.NET分野でのキャリアアップをお考えの方は、現在募集中の求人情報をご覧ください。

求人一覧

また、直接のエントリーも受け付けております。

エントリー(応募フォーム)