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

Chapter13 自動実装と自動定義

川俣 晶
2010/02/17

13.5 “名無し”のクラス―匿名型

 同等な結果をより楽に手に入れるためなら、いかなる努力もいとわない、というのはプログラマーが持つ健全な資質の1つである。

 そのようなプログラマーであれば、自動実装プロパティを見て、こう思ったかもしれない。

「ならば自動実装クラスがあってもいいんじゃないか? クラスの宣言なしに、いきなりインスタンスを初期化式で記述してオブジェクトになるような機能が」

 実際、このような機能はほかの言語には存在する。そもそも、Ajaxの世界で使用されるJSONは、JavaScriptのそのような機能(オブジェクトリテラル)の書式そのものである。

 このような機能をC# 3.0は「匿名型」として実装している。

 たとえば、次のリスト13.10の変数aに格納されているのは、匿名型のオブジェクトである。

using System;

class Program
{
  static void Main(string[] args)
  {
    var a = new { 名前 = "網島研修生", 口座残高 = 39 };

    Console.WriteLine(
        "名前: {0} 口座残高: {1:C}", a.名前, a.口座残高);
    // 出力:名前: 網島研修生 口座残高: \39
  }
}
リスト13.10 匿名型の使用例

 C++、Java、2.0までのC#など、強く型付けされた伝統的なオブジェクト指向言語を見慣れた読者は、思わず「何だこれは!?」と思ったかもしれない。なにしろ、変数aが参照しているオブジェクトに関しては、型の宣言どころか、型名すら書かれていないのである。まるで、厳格に型付けされていない簡易スクリプト言語のように見えるかもしれない。

 しかし、そうではない。匿名型は正当なクラスを少ない文字数で書くための記法、一種のシンタックスシュガーなのである。

 そのような前提から容易に推測できるとおり、クラスの概念がないJavaScriptのオブジェクトリテラルとは似て非なるまったくの別物である。使い方も性質もまったく違う。

 では、C# 3.0の匿名型が実現する機能とは何だろうか? それは、具体的には次のようなものだ。

  • 読み出し専用プロパティとreadonlyのprivateフィールドを持つクラスとインスタンスを作り出す
  • プロパティ、フィールドの型は初期化式の型が使用される
  • クラス自身の名前は匿名(プログラマーには見えない)
  • プロパティの並び順は厳格に保存され、同じ名前、同じ型、同じ並び順の匿名型は同一のクラスによって実現される(相互に代入可能)

 C# 3.0の言語仕様から具体的な生成内容を引用する。匿名型を使ったコードは、コンパイラにより、次のリスト13.11のようなコードに展開される。

new { p1 = e1 , p2 = e2 , …… pn = en }
class __Anonymous1
{
  private readonly T1 f1 ;
  private readonly T2 f2 ;
   …
  private readonly Tn fn ;

  public __Anonymous1(T1 a1, T2 a2,…, Tn an) {
    f1 = a1 ;
    f2 = a2 ;
     …
    fn = an ;
  }

  public T1 p1 { get { return f1 ; } }
  public T2 p2 { get { return f2 ; } }
   …
  public T1 p1 { get { return f1 ; } }
  public override bool Equals(object o) { …… }
  public override int GetHashCode() { …… }
}
リスト13.11 匿名型の展開内容

 ここで、T1、T2、……Tnは初期化式から推測された型が使われる。

 リスト13.10から生成されたクラスは、Reflector for .NETを使えば見ることができる。重要ではない属性やメソッドなどを除去した内容は、リスト13.12のようになる。確かにreadonlyのフィールド、getのみのプロパティ、初期値を受け取るコンストラクタなどが生成されていることがわかる。

internal sealed class <>f__AnonymousType0<<名前>j__TPar, <口座残高>j__TPar>
{
  // Fields
  private readonly <口座残高>j__TPar <口座残高>i__Field;
  private readonly <名前>j__TPar <名前>i__Field;

  // Methods
  public <>f__AnonymousType0(<名前>j__TPar 名前, <口座残高>j__TPar 口座残高);

  // Properties
  public <口座残高>j__TPar 口座残高 { get; }
  public <名前>j__TPar 名前 { get; }
}
リスト13.12 リスト13.10の実行ファイルを逆コンパイルした結果(抜粋)

 以上のように、匿名型も厳格に型付けされた存在であり、型チェックは実行時まで遅延されることはなく、コンパイル時にチェックが行われる。

 しかし、匿名の型であることから、型を明示して宣言することはできず、varキーワードを使った「型を明示しない変数宣言」が必須のものとして要求されている。varは「なくてもよいオプション機能」ではなく、C# 3.0で必須の機能として要求された存在である一例がここにある。


 INDEX
  [完全版]究極のC#プログラミング
  Chapter13 自動実装と自動定義
    1.13.1 ラムダ式を使ったダーティテク―refの代役
    2.13.2 自動実装プロパティ
    3.13.3 自動実装プロパティのアクセス制御
    4.13.4 読み出し専用、書き込み専用はない
  5.13.5 “名無し”のクラス―匿名型
    6.13.6 匿名型の等価性
    7.13.7 匿名型の簡易記法
    8.13.8 匿名型の使用目的
    9.13.9 オブジェクト初期化子
    10.13.10 オブジェクト初期化子の本質とは?
    11.13.11 コレクションはreadonlyでも初期化できる
    12.13.12 オブジェクト初期化子の使用例/練習問題
 
インデックス・ページヘ  「[完全版]究極の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 記事ランキング

本日 月間