連載:[完全版]究極のC#プログラミング

Chapter7 ラムダ式(後編)

川俣 晶
2009/11/02

7.9 ジェネリックメソッドと型推論

 ジェネリックメソッドや型推論とラムダ式は密接な関係がある。ラムダ式が導入されたことで、型推論のルールが追加されているためだ。

 まず、ラムダ式の引数がシンプルに推論されている例を見てみよう。

 次ページのリスト7.21は、「LINQのメソッド構文」と呼ばれる機能を用いて、配列nから10よりも小さい値だけを抜き出すコードである(LINQについては第16章で取り上げる)。ここでポイントになるのは、引数によって指定された条件を満たす要素を選び出す「Whereメソッド」である。

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
  static void Main(string[] args)
  {
    int[] n = { 2, 3, 5, 7, 11, 13 };
    IEnumerable<int> numQuery2 = n.Where(num => num < 10);

    foreach (int i in numQuery2)
    {
      Console.WriteLine(i);
      // 出力:
      // 2
      // 3
      // 5
      // 7
    }
  }
}
リスト7.21 LINQを用いて10よりも小さい値だけ抜き出す

 Whereメソッドには、本来、次のように型パラメータTSourceが指定されているが、推論によってそれがint型であると確定されている。

public static IEnumerable<TSource> Where<TSource>(
  this IEnumerable<TSource> source,
  Func<TSource, bool> predicate
)
Whereメソッド(System名前空間のIEnumerableクラス)の型

 ちなみに、Whereメソッドは本来配列オブジェクトが持つメソッドではなく、拡張メソッドであり、型推論はやや複雑な経路をたどって確定される。しかし、そこまで踏み込んで理解せずとも十分であるため、ここでは詳しくは解説しない(拡張メソッドについては第14章で解説する)。

 さて、ここでは型推論の特に強力な部分を見ていくことにしよう。次のリスト7.22は、C# 3.0言語仕様書に掲載されているサンプルコードを若干修正したものである。

using System;

class Program
{
  static Z F<X, Y, Z>(X value, Func<X, Y> f1, Func<Y, Z> f2)
  {
    return f2(f1(value));
  }

  static void Main(string[] args)
  {
    double seconds = F("1:15:30",
                       s => TimeSpan.Parse(s),
                       t => t.TotalSeconds);
    Console.WriteLine(seconds);
  }
}
リスト7.22 型推論の連鎖によってすべての型パラメータが確定する例

 この例のFメソッドにおいて、型パラメータXは容易に推論できる。指定された「1:15:30」がstring型であるため、Xはstring型である。しかし、型パラメータYとZにはそのような明確なヒントが存在しない。それらを使った引数f1とf2には、引数の型をいっさい明示しないラムダ式しか書かれていないからだ。

 たとえば、引数f1に書かれたラムダ式「s => TimeSpan.Parse(s)」は、このラムダ式の戻り値がDateTime型であることは推論可能だが、引数sの型を推論で導き出すことができない。引数f2も同様である。

 それにもかかわらず、このコードはコンパイルして実行できる。

 その理由は、引数f1で確定できないラムダ式の引数sの型は、第1引数valueによって確定され、引数f2で確定できないラムダ式の引数tの型は、引数f1によって確定されるためである。このような推論の連鎖によって、型パラメータX、Y、Zはすべて確定させることができる。

 しかし、このようにエレガントな解決はつねにできるものではない。推論が十分にできないケースでは、型パラメータを明示して書いてしまうほうが楽だろう。


 INDEX
  [完全版]究極のC#プログラミング
  Chapter7 ラムダ式(後編)
    1.7.1 ラムダ式は何をもたらすか?
    2.7.2 ラムダ式と匿名メソッドの違い
    3.7.3 ステートメント型のラムダ
    4.7.4 式形式のラムダの可能性
    5.7.5 型指定を省略できる場合、できない場合
    6.7.6 何もしないラムダ式
    7.7.7 ラムダ式の使用例/【C#olumn】「=>」は不等号?
    8.7.8 ラムダ式のさまざまなバリエーション
  9.7.9 ジェネリックメソッドと型推論
    10.7.10 オーバーロードの解決/値型と参照型の相違は何か?/練習問題
 
インデックス・ページヘ  「[完全版]究極のC#プログラミング」


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 記事ランキング

本日 月間