特集
PDC05レポート

動的プログラミング言語へと発展するC# 3.0とVB 9.0

吉松 史彰
2005/10/13

Page1 Page2 Page3 Page4

型推論とラムダ式

 型が推測できる場所はほかにもある。

Module Module1
  Dim data() As Integer = { 11, 3, 5, 13, 7, 17 }

  Sub Main()
    For Each a In data ' aの型は?
      Console.WriteLine(a.GetType().Name)
    Next
  End Sub
End Module

 このVBコードは、Visual Basic .NET 2003版とVisual Basic 2005版でOption Explicit Onにすると以下のようにコンパイル・エラーになる。

C:\Temp>vbc a.vb /optionexplicit+
Microsoft(R) Visual Basic .NET Compiler version 7.10.6001.4
for Microsoft(R) .NET Framework version 1.1.4322.2032
Copyright (C) Microsoft Corporation 1987-2002. All rights reserved.

C:\Temp\a.vb(4) : error BC30451: 名前 'a' は宣言されていません。

    For Each a In data
         ~
C:\Temp>"c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\vbc.exe" /optionexplicit+ /out:c:\temp\a.exe a.vb
Microsoft (R) Visual Basic Compiler version 8.0.50727.26
for Microsoft (R) .NET Framework version 2.0.50727.26
Copyright (c) Microsoft Corporation.  All rights reserved.

C:\Temp\a.vb(4) : error BC30451: Name 'a' is not declared.

    For Each a In data
         ~
コンパイル時に表示されるコンパイル・エラー
(上:Visual Basic .NET 2003、下:Visual Basic 2005)

 だが、VB9のコンパイラではコンパイルできる。

C:\Temp>"c:\Program Files\VB LINQ Preview\Bin\vbc.exe" /optionexplicit+ /out:c:\temp\a.exe a.vb
Microsoft (R) Visual Basic Compiler version 8.0.50801
for Microsoft (R) .NET Framework version 2.0.50727.26
Copyright (c) Microsoft Corporation.  All rights reserved.
VB9のコンパイラでのコンパイル結果

 これは次のように書いたものと見なされる。

For Each a As Integer In data
  Console.WriteLine(a.GetType().Name)
Next

 もちろんC#3でもこう書けるようになる。

// C#3
static int[] data = { 11, 3, 5, 13, 7, 17 };

static void Main() {
  foreach (var a in data)
    Console.WriteLine(a.GetType().Name);
}

■ラムダ式の導入

 2005版のC#(以下C#2)には、匿名デリゲート(=匿名メソッドを利用したデリゲート)の機能が導入されている。これによって、例えばメソッドのパラメータとして、その場でデリゲートを作って渡せるようになった。

// C#2
static int[] data = { 11, 3, 5, 13, 7, 17 };

static void Main() {
  Array.ForEach<int>(data,
      delegate(int x) { Console.WriteLine(x); });

  Array.Sort<int>(data,
      delegate(int x, int y) { return x - y; });

  Array.ForEach<int>(data,
      delegate(int x) { Console.WriteLine(x); });
}

 ところで、デリゲートを渡すこれらの構文は、分かり切ったことを明示しているもう1つの例と考えられる。ある型のデリゲートを要求されているということはメソッドのプロトタイプから分かっているのだから、わざわざ「delegate」と書かなくてもよさそうなものだ。

 型推論ができるようになったことによる恩恵の1つが「ラムダ式」の導入だ。ラムダ式を利用すると、C#3では次のように書けるようになる。

// C#2での匿名デリゲートの記述例
// Array.Sort<int>(data, delegate(int x, int y){ return x - y; });

// C#3でのラムダ式を用いた匿名デリゲートの記述例
Array.Sort<int>(data, (x, y) => x - y);

 「(x, y) => x - y」というラムダ式の構文は、コンパイラによって上の匿名デリゲートと同じコードに展開される。Sortメソッドのプロトタイプから、第2パラメータがComparison<T>というデリゲートであることが分かる。また、Comparison<T>の定義からこのデリゲートは2つのint型のパラメータを取り、int型の値を返すことが分かる。

 そこで、プログラマは「=>」という構文を示すだけで、コンパイラに意図を伝えることができるようになる。「int」「Customer」などと型を文字どおり記述することは、オブジェクトに静的に型を付けるために必須ではない。型を推論する機能によって、このことが可能になる。静的な型付きによるメリット(コンパイラによるエラー検出やIntelliSense機能など)は捨てず、コンパイラにヒントを示すだけでそれを可能にしているのである()。

ちなみにVB9でもラムダ式の構文は検討されているようだが、まだ実装の公開には至っていない。また、C#3ではステートメント・ブロックをラムダ式に指定する構文もサポート予定だが、PDC05版のコンパイラには実装されていないため、上のForEachコードのラムダ式版は書けなかった。

Extension Methodsの導入

 静的型付言語において、メソッドは型(クラス)で静的に定義されるもので、型を定義した後に付け加えるようなものではない。だが次のC#3とVB9では、定義されている型のオブジェクトにメソッドを追加できる(ように見える)機能が導入される。これが「Extension Methods」である。

 Extension Methodsは、例えば次のように定義できる。

// C#3(Extension Methodの記述例)
static class MyExt {
  internal static string ToXML(this Person source) {
    return string.Format(
      "<Person><Name>{0}</Name><Age>{1}</Age></Person>",
      source.Name, source.Age);
  }
}

 すると、コンパイラによって次のようなコードがコンパイル可能になる。変数pでToXMLメソッドを呼び出している点に注意してほしい。

// C#3(Extension Methodの記述例)
using System;

class Person {
  internal string Name;
  internal int Age;
}

static class App {
  static void Main() {
    Person p = new Person();
    p.Name = "Yoshimatsu";
    p.Age = 22;
    Console.WriteLine(p.ToXML());
    // Console.WriteLine(MyExt.ToXML(p));
  }
}

 Extension Methodsは、静的クラス(Static Class、C#2で導入される)に定義された静的メソッドにすぎない。だが、メソッドに付けられた特殊な属性によって、コンパイラがそれを特殊なメソッドとして理解し、静的メソッドではなくあたかもインスタンス・メソッドであるかのように呼び出す構文をコンパイル可能にする。

 JavaScriptでは、クラスではなくそのインスタンスに対して機能を追加することができる。Extension Methodsはこれと厳密に同じことを実現するわけではないが、クラスの提供者の意図とは別に、クラスの利用者が独自機能を追加できる仕組みである。

 このため、上のコード例で分かるとおり、ソース・コードの見た目はクラスにインスタンス・メソッドが定義されているように見えるが、クラスの定義を調べてもそのメソッドは定義されていないということになる。

 C#のアーキテクトであるAnders Hejlsberg氏は、この機能を紹介したセッションの中で、この機能の乱用はやめるように聴衆に忠告していたし、公開されているC#3の言語仕様にも警告文が載っている。ちなみにこの機能はVB9でも提供される予定だ。


 INDEX
  [特集] PDC05レポート
  巻き返しが始まるMicrosoftのRSS/Ajax戦略
    1.PDC05の3つのテーマ
    2.RSSへの取り組み
    3.Ajaxと新世代Webアプリケーション
  動的プログラミング言語へと発展するC# 3.0とVB 9.0
    1.動的型付言語への憧憬
  2.型推論とラムダ式/Extension Methodsの導入
    3.クエリ構文の統合「LINQ」/DLinqとXLinq
    4.Visual Basicを再び動的プログラミング言語にする試み
 


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

本日 月間