連載
» 2017年06月07日 05時00分 UPDATE

.NET TIPS:オプション引数が使えるメソッドを作るには?[C#/VB]

メソッド呼び出し時に引数を省略できるようにするには、その引数をオプション引数とするか、メソッドをオーバーロードする。それらの方法と注意点を解説する。

[山本康彦,BluewaterSoft/Microsoft MVP for Windows Development]
「.NET TIPS」のインデックス

連載目次

 オプション引数(省略可能な引数)とは、メソッドを呼び出すときにその引数の一部を省略できる機能である。本稿では、オプション引数が使えるメソッドの作り方と、その使い方を解説する。

オプション引数が使えるメソッドを作るには?

 メソッドの引数に既定値を与えればよい。Visual Basic(以降、VB)では、引数の前にOptionalキーワードも必要だ(次のコード)。

 ただし、オプション引数の途中や後ろに通常の引数は置けない。通常の引数を並べてから、その後ろにオプション引数を並べる。

// 呼び出すときに引数x/y/zのいずれも省略可能(オプション引数)
public static int SampleMethod1(int x = 0, int y = 0, int z = 0)
  => x + y * 2 + z * 3;

// オプション引数(y/z)は通常の引数(x)の後ろにしか置けない
public static int SampleMethod1a(int x, int y = 0, int z = 0)
  => x + y * 2 + z * 3;

// オプション引数の途中や後ろに通常の引数を置くとコンパイルエラー
//public static int SampleMethod1b(int x = 0, int y, int z = 0)
//  => x + y * 2 + z * 3;

' 呼び出すときに引数x/y/zのいずれも省略可能(オプション引数)
Public Function SampleMethod1(Optional x As Integer = 0,
                              Optional y As Integer = 0,
                              Optional z As Integer = 0) As Integer
  Return x + y * 2 + z * 3
End Function

' オプション引数(y/z)は通常の引数(x)の後ろにしか置けない
Public Function SampleMethod1a(x As Integer,
                                Optional y As Integer = 0,
                                Optional z As Integer = 0) As Integer
  Return x + y * 2 + z * 3
End Function

' オプション引数の途中や後ろに通常の引数を置くとコンパイルエラー
'Public Function SampleMethod1b(Optional x As Integer = 0,
'                               y As Integer,
'                               Optional z As Integer = 0) As Integer
'  Return x + y * 2 + z * 3
'End Function

オプション引数を持つメソッドの例(上:C#、下:VB)
最初のメソッド「SampleMethod1」は、呼び出すときに引数x/y/zのいずれも省略可能だ。省略した場合は、引数リストで与えている既定値(ここではどれも0)が引数として使われる。

メソッドを呼び出すときのオプション引数の使い方

 メソッドを呼び出すとき、引数の位置や引数の名前を使って必要な引数だけを与える(次のコード)。省略した引数は、メソッド側に定義してある既定値になる。

 引数の位置によって省略するとき、C#では途中の引数を省略できない。VBでは途中の引数も省略できる。

// 全ての引数を与える例
WriteLine($"SampleMethod1(1,2,3)={SampleMethod1(1, 2, 3)}");
// 出力:SampleMethod1(1,2,3)=14

// 1a. 引数順序で省略(第3引数を省略する例)
WriteLine($"SampleMethod1(1,2)={SampleMethod1(1,2)}");
// 出力:SampleMethod1(1,2)=5

// 1b. 第2引数を省略する例(C#ではコンパイルエラー)
//WriteLine($"SampleMethod1(1,,3)={SampleMethod1(1,,3)}");

// 2. 名前付き引数を使って省略(第1引数xを省略する例)
WriteLine($"SampleMethod1(y:1,z:2)={SampleMethod1(y:1,z:2)}");
// 出力:SampleMethod1(y:1,z:2)=8

' 全ての引数を与える例
WriteLine($"SampleMethod1(1,2,3)={SampleMethod1(1, 2, 3)}")
' 出力:SampleMethod1(1,2,3)=14

' 1a. 引数順序で省略(第3引数を省略する例)
WriteLine($"SampleMethod1(1,2)={SampleMethod1(1, 2)}")
' 出力:SampleMethod1(1,2)=5

' 1b. 第2引数を省略する例(VBでは可能)
WriteLine($"SampleMethod1(1,,3)={SampleMethod1(1, , 3)}")
' 出力:SampleMethod1(1,,3)=10

' 2. 名前付き引数を使って省略(第1引数xを省略する例)
WriteLine($"SampleMethod1(y:=1,z:=2)={SampleMethod1(y:=1, z:=2)}")
' 出力:SampleMethod1(y:=1,z:=2)=8

オプション引数を持つメソッドを呼び出す例(上:C#、下:VB)
先ほどの「SampleMethod1」メソッドを呼び出している。

オーバーロードとの違い

 オプション引数と同様なことは、メソッドのオーバーロードでも可能だ。C# 4.0以前では、オプション引数がなかったので、先の「SampleMethod1」メソッドは次のコードのようにしていた(C#のみ示す)。

 オーバーロードとの相違点は、オプション引数の方がコードの記述が少なくて済むことや、名前付き引数を使えば途中の引数も省略できることなどが挙げられる。

 相違点で特に気を付けなければならないのは、既定値がコンパイル後にどこに配置されるかである。オプション引数の場合は、コンパイル時に既定値が解決され、呼び出し側に埋め込まれる。オーバーロードの場合は、メソッド内部だ。オプション引数の場合は、既定値を変更しても、呼び出し側をコンパイルするまで反映されない。十分に注意してほしい(クラスライブラリを作るときにはオプション引数は避けた方が無難である)。

public static int SampleMethod1()
  => SampleMethod1(0);

public static int SampleMethod1(int x)
  => SampleMethod1(x, 0);

public static int SampleMethod1(int x, int y)
  => SampleMethod1(x, y, 0);

public static int SampleMethod1(int x, int y, int z)
  => x + y * 2 + z * 3;

「SampleMethod1」メソッドをオーバーロードで実装した例(C#)

他の機能との併用

 オーバーロードや参照渡しとは併用しないようにしよう。

 VBでは、オプション引数と参照渡しを併用できる(C#ではコンパイルできない)。すると、オプション引数を出力用(メソッドから結果を受け取る)としても使えてしまう。オプション引数=省略可能であるのに、結果を受け取るにはオプション引数が必要というのでは、使う側は混乱してしまうだろう。

 オーバーロードとオプション引数は、C#でもVBでも併用できる(次のコード)。両方に当てはまる呼び出し方をした場合、実際にはどちらが呼び出されるのだろうか? 原則はオプション引数のない方である。ただし、名前付き引数を使えば、優先順序を引っ繰り返せる。コードを読むときにそのよう難しいルールを思い出さねばならないというのは、あまりうれしくないだろう。

// オプション引数を持つメソッド
public static int SampleMethod1(int x = 0, int y = 0, int z = 0)
  => x + y * 2 + z * 3;

// メソッドのオーバーロード
public static int SampleMethod1(int n)
  => -n;

……省略……

// オーバーロードとオプション引数の優先順位
WriteLine($"SampleMethod1(1)={SampleMethod1(1)}");
// 出力:SampleMethod1(1)=-1 ⇒ オーバーロードの方が呼び出された
WriteLine($"SampleMethod1(x:1)={SampleMethod1(x:1)}");
// 出力:SampleMethod1(x:1)=1 ⇒ オプション引数の方が呼び出された

' オプション引数を持つメソッド
Public Function SampleMethod1(Optional x As Integer = 0,
                              Optional y As Integer = 0,
                              Optional z As Integer = 0) As Integer
  Return x + y * 2 + z * 3
End Function

' メソッドのオーバーロード
Public Function SampleMethod1(n As Integer) As Integer
  Return -n
End Function

……省略……

' オーバーロードとオプション引数の優先順位
WriteLine($"SampleMethod1(1)={SampleMethod1(1)}")
' 出力:SampleMethod1(1)=-1 ⇒ オーバーロードの方が呼び出された
WriteLine($"SampleMethod1(x:=1)={SampleMethod1(x:=1)}")
' 出力:SampleMethod1(x:=1)=1 ⇒ オプション引数の方が呼び出された

オーバーロードとオプション引数を併用しているよくない例(上:C#、下:VB)
原則はオプション引数よりもオプション引数なしのオーバーロードが優先する。ただし、名前付き引数を使えばオプション引数の方のメソッドを呼び出せる。そんなルールは覚えていられないだろう。このような混在は避けた方がよい。

まとめ

 オプション引数が使えるメソッドを作るには、メソッドの引数に既定値を与える。VBでは、引数の前にOptionalキーワードも付ける。オーバーロードや参照渡しとの併用は、混乱を招くので行わないようにしよう。

利用可能バージョン:Visual Basic .NET以降/Visual Studio 2010(C# 4.0)以降
カテゴリ:C# 処理対象:言語構文
カテゴリ:Visual Basic .NET 処理対象:言語構文
関連TIPS:数値を右詰めや0埋めで文字列化するには?[C#、VB]


「.NET TIPS」のインデックス

.NET TIPS

Copyright© 1999-2017 Digital Advantage Corp. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。