連載:C# 4入門

第4回 引数の省略と順番の変更

株式会社ピーデー 川俣 晶
2010/10/15
Page1 Page2 Page3

可変値指定不可能

 ならば、外部から動的に省略時のデフォルト引数の値を指定できるように拡張してやろうと、以下のように書き直すとコンパイルできない。

using System;

class Program
{
  private static string defaultValue;

  private static void myMethod(int a, string b = defaultValue)
  {
    if (b != null)
      Console.WriteLine("{0}さんはテストを受けました。", b);

    if (a >= 0)
      Console.WriteLine("{0}点でした。", a);
  }

  static void Main(string[] args)
  {
    defaultValue = null;
    myMethod(100);
    myMethod(50, "タロウ");
  }
}
リスト4(コンパイルできない)

 なぜコンパイルできないのかといえば、引数の省略時の値は定数値に限られるからだ。

 なぜ定数値に限られるのかといえば、実はこの機能は「引数が省略されたときに補われる値」なのではなく、「引数が省略されたときに補ってほしい値をメタデータで公開している」からなのだ。メタデータに記録する値は定数値である必要がある。つまり、計算の結果として得られるいかなる値も、省略時のデフォルトとして指定できない。どうしてもそれが必要なら、省略時のデフォルトとして無効の値を指定して、その値が指定されたらメソッド内で計算するしかない。

 また、この前提から分かるとおり、デフォルト値は呼び出し側に記録される。だから、値を修正したとき、呼ばれる側だけでなく、呼ぶ側も再コンパイルが必要である。

定数の正しい意味

 以下のコードはコンパイルして実行できる。

using System;

class Program
{
  private const string data1 = "This is data1.";
  private static string data2 { get { return data1; } }
  private static readonly string data3 = data1;

  private static void MyMethod(string a = data1)
  {
    Console.WriteLine(a);
  }

  static void Main(string[] args)
  {
    MyMethod();
  }
}
リスト5

This is data1.
リスト5の実行結果

 しかし、MyMethodメソッドの引数を以下のように直すと、通る場合と通らない場合がある。

○ MyMethod(string a = "This is constant.")
○ MyMethod(string a = data1)
× MyMethod(string a = data2)
× MyMethod(string a = data3)

 これを見て分かるとおり、コンパイル時に確定する値はよいが、実行時に値が確定する方法は、たとえ固定値であっても指定できない。微妙で分かりにくい制約だが、同時に実装方法が透けて見えるような制約でもある。

無効値選定の重要性

 すでに説明したとおり、この機能は「引数が省略されたときに渡してほしい値をメタデータで指定する」機能であって、メソッドそのものは、完全な引数をすべて受け取ることになる。つまり、呼ばれる側から見ると「引数が省略されて補われたデフォルト値」と「それと同じ値を渡しただけ」という状況が判別できない。

 それ故に、以下のような条件を持つメソッドはうまく記述できない。

  • 引数にint型の値を1つ持つ
  • 引数の値はint型で表現できる範囲について全体で有効である
  • 引数が省略された場合は特定のフィールドの値を使用する

 このような場合は、null許容型を使うことで、完全ではないがうまく処理することができる。

using System;

class Program
{
  private static int defaultValue;

  private static void myMethod(int? x = null)
  {
    if (x == null)
      Console.WriteLine(defaultValue);
    else
      Console.WriteLine(x);
  }

  static void Main(string[] args)
  {
    defaultValue = 123;
    myMethod();
    myMethod(456);
  }
}
リスト6

123
456
リスト6の実行結果

 この場合、int型で表現できる数値はすべて有効であり、引数省略のケースと識別できる。唯一、以下の2つの呼び出しは区別できないことになるが、これは区別できなくてもそれほど困る問題ではないだろう。

myMethod();

myMethod(null);

 つまり、引数の省略とnull許容型の相性はとてもよい。null許容型を使わないプログラマーも、引数を省略する場面では使ってみるとよいのではないだろうか。


 INDEX
  C# 4入門
  第4回 引数の省略と順番の変更
    1.C#の弱点といえば/属性の事例/引数の省略
  2.可変値指定不可能/定数の正しい意味/無効値選定の重要性
    3.順番の変更/ピンポイントで指定せよ/まとめ
 
インデックス・ページヘ  「C# 4入門」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間