Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

C#/.NETがやっていること 第二版

36,951 views

Published on

2014/6/28 CLR/H in Tokyo 第3回 にて登壇

Published in: Technology
  • If you want to download or read this book, Copy link or url below in the New tab ......................................................................................................................... DOWNLOAD FULL PDF EBOOK here { http://bit.ly/2m6jJ5M } ......................................................................................................................... Download EPUB Ebook here { http://bit.ly/2m6jJ5M } ......................................................................................................................... Download Doc Ebook here { http://bit.ly/2m6jJ5M } ......................................................................................................................... .........................................................................................................................
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • If you want to download or read this book, Copy link or url below in the New tab ......................................................................................................................... DOWNLOAD FULL PDF EBOOK here { http://bit.ly/2m6jJ5M } ......................................................................................................................... Download EPUB Ebook here { http://bit.ly/2m6jJ5M } ......................................................................................................................... Download Doc Ebook here { http://bit.ly/2m6jJ5M } ......................................................................................................................... .........................................................................................................................
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • DOWNLOAD THIS BOOKS INTO AVAILABLE FORMAT (Unlimited) ......................................................................................................................... ......................................................................................................................... Download Full PDF EBOOK here { https://tinyurl.com/yyxo9sk7 } ......................................................................................................................... Download Full EPUB Ebook here { https://tinyurl.com/yyxo9sk7 } ......................................................................................................................... ACCESS WEBSITE for All Ebooks ......................................................................................................................... Download Full PDF EBOOK here { https://tinyurl.com/yyxo9sk7 } ......................................................................................................................... Download EPUB Ebook here { https://tinyurl.com/yyxo9sk7 } ......................................................................................................................... Download doc Ebook here { https://tinyurl.com/yyxo9sk7 } ......................................................................................................................... ......................................................................................................................... ......................................................................................................................... .............. Browse by Genre Available eBooks ......................................................................................................................... Art, Biography, Business, Chick Lit, Children's, Christian, Classics, Comics, Contemporary, Cookbooks, Crime, Ebooks, Fantasy, Fiction, Graphic Novels, Historical Fiction, History, Horror, Humor And Comedy, Manga, Memoir, Music, Mystery, Non Fiction, Paranormal, Philosophy, Poetry, Psychology, Religion, Romance, Science, Science Fiction, Self Help, Suspense, Spirituality, Sports, Thriller, Travel, Young Adult,
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • DOWNLOAD THIS BOOKS INTO AVAILABLE FORMAT (Unlimited) ......................................................................................................................... ......................................................................................................................... Download Full PDF EBOOK here { https://tinyurl.com/yyxo9sk7 } ......................................................................................................................... Download Full EPUB Ebook here { https://tinyurl.com/yyxo9sk7 } ......................................................................................................................... ACCESS WEBSITE for All Ebooks ......................................................................................................................... Download Full PDF EBOOK here { https://tinyurl.com/yyxo9sk7 } ......................................................................................................................... Download EPUB Ebook here { https://tinyurl.com/yyxo9sk7 } ......................................................................................................................... Download doc Ebook here { https://tinyurl.com/yyxo9sk7 } ......................................................................................................................... ......................................................................................................................... ......................................................................................................................... .............. Browse by Genre Available eBooks ......................................................................................................................... Art, Biography, Business, Chick Lit, Children's, Christian, Classics, Comics, Contemporary, Cookbooks, Crime, Ebooks, Fantasy, Fiction, Graphic Novels, Historical Fiction, History, Horror, Humor And Comedy, Manga, Memoir, Music, Mystery, Non Fiction, Paranormal, Philosophy, Poetry, Psychology, Religion, Romance, Science, Science Fiction, Self Help, Suspense, Spirituality, Sports, Thriller, Travel, Young Adult,
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

C#/.NETがやっていること 第二版

  1. 1. C#や.NET Framework がやっていること 第二版 岩永 信之
  2. 2. 今日の内容 • C#や.NET Frameworkがやっていること • どうしてそういう機能がある • (自前で実装するなら)どうやればできる • (自前でやる上で) 気を付ける必要がある点
  3. 3. 注意 • ベースは3月にBoost勉強会でやった内容 • 通常の3倍速で1時間で話した • 本来、1時間で話す内容じゃない • かなりの前提知識を仮定 • 3時間話します • いくつか追記 • 前提をだいぶ緩めたので背景説明など • .NET vNextがらみ追加 • 5月の非同期勉強会での内容も少しマージ 同じような内容でも回数重ねてこなれてきてたり
  4. 4. 目次 • 1時間目 • メモリ管理 • マルチ タスク管理 • 2時間目 • メタデータ • 3時間目 • C#言語機能 1 2 3
  5. 5. 1時間目 • メモリ管理 • ヒープ自動管理 • 境界チェック • ドメイン分離 • マルチ タスク管理 • スレッドと非同期処理 1 2 3
  6. 6. “Managed” • .NET FrameworkといえばManaged • .NETがいろいろ管理 • メモリ リーク防止 • セキュリティ ホール防止 • v1リリース後、一番進歩が激しいのは非同期 • 効率的なタスク管理 • I/O-boundな非同期処理
  7. 7. Garbage Collection メモリ リークをなくすために
  8. 8. メモリ管理(1) • スタック • 一番上に積む・一番上から取り出す • ローカル変数用 • 管理は楽 • スコープがはっきりしてないと使えない int x = 1; int y = 2; int z = 4; int sum = x + y + z; 1 1 2 load 1 load 2 3 add 3 4 load 4 7 add
  9. 9. メモリ管理(2) • ヒープ • 連結リスト構造で任意の位置にメモリ確保 • 動的なメモリ利用できる • 管理が大変 • 消し忘れ(メモリ リーク)があり得る 使用中かどうかのフラグ ☑□ □ ☑ 次のヒープの位置 前のヒープの位置 … …
  10. 10. 値型と参照型 • 値型 • intとかbyteとか • structとかenumとか スタック ヒープ 他の型 他の型 スタック上や、他の型の 中に直接埋め込まれる • 参照型 • stringとか • classとかdelegateとか スタック ヒープ スタック上や他の型の中 には参照情報だけある ヒープ上に置かれる
  11. 11. ヒープ管理 • 怠ると • 徐々にアプリが重くなる • 長時間稼働させると突然の死 • その割に大変 • (自動管理がない頃) プログラミングの苦労の半分くらい占める • 本当にやりたいことに注力できてない • 自動化したい 不要なオブジェクトの回収 =ゴミ を集め(garbage collection)
  12. 12. 代表的な手法 • 参照カウント • 生成・コピー代入のたびにカウントを +1 • 変数がスコープ外れるたびに -1 • カウントが0になったらメモリ削除 • Mark & Sweep • ルート†からたどれるオブジェクトに印を付ける • 印のついてないオブジェクトを削除 † スタックや静的フィールド中のオブジェクトなど 参照の起点となる場所 mark sweep
  13. 13. Mark & Sweep • ルートから参照をたどる Mark ヒープ ルート Sweep 使用中オブジェクト 未使用オブジェクト
  14. 14. Compaction • GC時にオブジェクトの位置を移動 使用中オブジェクト 未使用オブジェクト 隙間をなくすように移動 後ろが丸ごと空くので次のメモリ確保が楽
  15. 15. Generation • 移動したオブジェクトはしばらくノータッチ Gen1 Gen0 Gen0 しばらくここは ノータッチ この範囲でだけ メモリ確保・GC オブジェクトの寿命 統計的に • 短いものはとことん短く • 長いものはとことん長い 一度GCかかったオブジェクト はしばらく放置
  16. 16. .NET FrameworkのGC • .NET FrameworkのGCはMark & Sweep • Compactionあり • 世代別 (3世代、Gen0~2) • Backgroundスレッドで並列実行
  17. 17. Mark & Sweepと参照カウント • 比較 • 一長一短ある Mark & Sweep 参照カウント メモリ確保 ○ 末尾を見るだけ × 空いている場所を探す × カウント用の領域が追加 で必要 変数のコピー ○ ポインターのコピー × ポインターのコピーに加 え、参照数のカウント アップ メモリ解放 × MarkやCompactionに 時間がかかる × 負担が1か所に集中 ○ カウントが0になった時 にdeleteするだけ × 循環参照が苦手
  18. 18. Throughput • トータルの性能(throughput)はMark & Sweep の方がいい Mark & Sweep 参照カウント メモリ確保 ○ 末尾を見るだけ × 空いている場所を探す × カウント用の領域が追加 で必要 変数のコピー ○ ポインターのコピー × ポインターのコピーに加 え、参照数のカウント アップ メモリ解放 × MarkやCompactionに 時間がかかる × 負担が1か所に集中 ○ カウントが0になった時 にdeleteするだけ × 循環参照が苦手 まとめてやる方がバラバラに やるよりスループットはいい 頻度が高い操作なので ここの負担が大きいと 全体の性能落ちる 特に、スレッド安全を求めるときつい たかがインクリメントでも、atomic性 保証するとそこそこの負担 参照局所性も高くなって キャッシュが効きやすい
  19. 19. 短所の軽減 • 短所も、まったく打つ手がないわけじゃない Mark & Sweep 参照カウント メモリ確保 ○ 末尾を見るだけ × 空いている場所を探す × カウント用の領域が追加 で必要 変数のコピー ○ ポインターのコピー × ポインターのコピーに加 え、参照数のカウント アップ メモリ解放 × MarkやCompactionに 時間がかかる × 負担が1か所に集中 ○ カウントが0になった時 にdeleteするだけ × 循環参照が苦手 .NET 4以降、Background スレッド実行してる C++ 11的には 「move semantics活用 してね」
  20. 20. 自前メモリ管理との混在 • スコープが短いならスタックにとればいいのに • C#はclassは必ずヒープ、structはスタックになる • 使い分けれない • Mark & Sweep管理領域内のポインターを管理 外に渡すときには注意が必要 • Compactionでメモリ移動しないように 「ピン止め」が必要 • 結構ガベージ コレクションのパフォーマンス落とす
  21. 21. おまけ: 他の言語 • C++: 主に参照カウント方式のライブラリ利用 • 型がはっきりしない言語ではMark & Sweepしにくい • できなくはないけども「保守的」になる(効率落とす) • 数値を無差別にポインター扱いしてMarkする • Pythonとか: 参照カウントとMark & Sweepの併用 • ○ 循環参照問題避けつつ、負担を1か所に集中させない • × 性能的には悪いとこどり • Go: 割当先を自動判別 • スコープ内で完結してたらスタックに、さもなくばヒープに • これはこれでスタックの浪費激しそうなんだけども… • なので、可変長スタック持ってる
  22. 22. 注意: GCあってもリーク • 長時間生きているオブジェクト†が参照持った ままになると、GC対象になれない † ワーストケースはアプリ稼働中ずっと生きているオブジェクト class View : UserControl { public View(Model model) { model.PropertyChanged += (sender, e) => { // データの表示を更新 }; } } よく起こり得る例 ビューは画面遷移で消える モデルはずっと生きてる イベント購読で、 モデルがビューの参照持つ 解除 (-=) しないとメモリ リーク
  23. 23. ポイント • メモリ管理は大変なので自動化 • Mark & Sweep • いろいろ賢いアルゴリズム搭載してるし、ほとんど の場合、任せた方がいい • GCあってもメモリ リークは起こり得るので注 意
  24. 24. 境界チェック 意図しないメモリ領域にはアクセスさせない
  25. 25. 配列の境界チェック • .NETの配列は厳しい • 常に境界チェックしてる • 要はbuffer overrun†防止 • ちなみにJIT最適化で不要な境界チェック消してる (明らかに境界を侵さないものはチェック不要) • for (var i; i < a.Length; ++i) とか • foreach (var item in a) とか 配列 a OutOfRange OutOfRange a[-1] a[length+1] † クラッシュやセキュリティ ホールの温床
  26. 26. unsafe • とはいえ、C#にもポインターあるんだけども var x = new[] { 1, 2, 3, 4, 5 }; unsafe { fixed(int* px = &x[0]) { Console.WriteLine(px[100]); } } メモリ移動の防止 (ピン止め) 範囲チェックなし 安全でないコード この中でだけポイ ンター利用可能 buffer overrunやりたい放題
  27. 27. unsafeでも制限付き • class (参照型)はアドレス取れない • 仮想メソッド テーブル(vtable)とか入ってるし • 親クラス側のレイアウト変更の影響受けるし class Class { public int x; public int y; } var c = new Class(); fixed (void* p = &c) { } // error fixed (void* p = &c.x) { } // OK fixed (void* p = &c.y) { } // OK ピン止め必須 • 値型のメンバーのアドレスは取れる • オブジェクト自体のアドレスは取れ ない
  28. 28. unsafeでも制限付き • struct (値型)はアドレス取れる • ただし、メンバーが全部値型の時のみ† • ↑「unmanaged型」と呼ぶ struct UnmanagedStruct { public int x; public int y; } var u = new UnmanagedStruct(); void* p1 = &u; // OK void* p2 = &u.x; // OK void* p3 = &u.y; // OK メンバーが 全部値型 無制限にアドレス取れる † 正確には、再帰的に全子要素が値型、かつ、型パラメーターも値型
  29. 29. unsafeでも制限付き • struct (値型)であっても制限付き • メンバーに1つでも参照型を含むとダメ • ↑「managed型」と呼ぶ struct ManagedStruct { public int x; public string y; } var u = new ManagedStruct(); void* p1 = &u; // error void* p2 = &u.x; // OK void* p3 = &u.y; // error メンバーに 参照型が1つ 本体のアドレス取れない 値型のメンバーのところ だけはアドレス取れる
  30. 30. ポイント • unsafe • 危険なことは基本認めない • 面倒な追加の構文を要求 • コンパイル オプションでも/unsafeの明記必須 • fixed • ガベージ コレクションとの兼ね合い • Compaction阻害になるので注意 • 「Unmanaged型」に限る • 実装依存な部分(vtableとか)や、 型の定義側の変更が利用側に極力影響しないように
  31. 31. AppDomain 実行環境の分離 セキュリティ保証
  32. 32. コンポーネントの連携 • 他のアプリの機能を自分のアプリから呼びたい • WordとかExcelとかのドキュメントを出力 • サーバー上に自分のアプリを配置したい • IIS上に(ASP.NET) • SQL Server上に • (必ずしも)信用できない • セキュリティ保証 • 参照先のクラッシュにアプリ/サーバー が巻き込まれないように • コンポーネントのバージョン アップ
  33. 33. AppDomain • 単一プロセス内で、分離された複数の実行領域 (domain)を提供 • 「分離」 • plugin: 動的なロード/アンロード • security: AppDomainごとに異なる権限付与 • isolation: 別メモリ空間 AppDomain 1 AppDomain 2… プロセス 必ずしも信用できないコードを 安全に、動的に呼び出し
  34. 34. ドメイン間には壁がある • 別メモリ空間に分離されてる AppDomain 1 AppDomain 2 互いに独立
  35. 35. ドメイン間の通信 • マーシャリング(marshaling) • marshal (司令官、案内係)の命令通りにしか壁を超 えれない AppDomain 1 AppDomain 2 • 司令がダメといったら通れない • 司令の指示通りの形式にいったん シリアライズしないといけない
  36. 36. AppDomain 1 AppDomain 2 ドメイン間の通信 • ダメな例 これをBに渡し たいとして あるオブジェクト
  37. 37. AppDomain 1 AppDomain 2 ドメイン間の通信 • ダメな例 shallow copyなんてしようもんなら A側のメモリへの参照が残る
  38. 38. AppDomain 1 AppDomain 2 ドメイン間の通信 • よい例 serialize deserialize { {1, 2}, {3, 4}, {5, 6} } この辺りがマーシャリング 一度シリアライズ 必ずdeep copy
  39. 39. .NETのマーシャリング • 2種類 • marshal by ref • 参照を渡すんだけど、メモリ領域は直接触れない • メソッド越しにしか操作しちゃいけない • 「このメソッドを呼んでくれ」っていうメッセージだけ がドメインを超えてわたって、実際の実行は相手側ドメ インで(プロキシ実行) • marshal by value • 値をシリアライズして相手側に渡す • 規定では、BinarySeralizerを使用 privateフィールドまで含めてリフレクションで 内部状態を取得してシリアライズ
  40. 40. .NETのマーシャリング(文字列) • 文字列は特殊扱い (文字コード変換が不要な場合) • marshal by valueみたいにシリアライズを経ない • marshal by referenceみたいにプロキシ実行しない • 参照を渡して、メモリを直接読んでもらう • immutableかつrange-check付きに作ってある • 変更不可なので参照を渡しても安全 • COM (ネイティブ)に対して文字列を渡す時すらこの方式
  41. 41. AppDomain 1 AppDomain 2 その他のコスト • マーシャリング以外にもいくらかコストが • 例えばライブラリの読み込み Library X Library X 別ドメインに全く同じライブラリを読み込んでも、 それぞれ別イメージが作られる Global Assembly Cache (GAC)にあるDLLだけは別 GAC中のは同じメモリ イメージが共有される
  42. 42. おまけ: 他の言語 • COMなんかはマーシャリングやってる • というか「COMの頃から」やってる • .NETの前身なので • さもなくば、プロセス分離してプロセス間通信 • OS特権がないと他プロセスのメモリにはアクセス できない • 安全だけどかなり高負荷 • RPC†とか一時期流行ったけども廃れた † ただの関数呼び出しに見えるコードで、内部的にプロセス間通信する 知らないうちに性能落とすとか、リモート側でのエラーが処理しにくいとか
  43. 43. ポイント • プラグインとかやるなら「分離」が必須 • とはいえ、プロセス分けると負担が大きすぎる • プロセス内で分離を保証する仕組み • AppDomain • マーシャリング • それなりにコストがかかるけども、セキュリティに は変えられない
  44. 44. スレッド 応答性のよいプログラムを書くために OSやアプリ全体をフリーズさせないために
  45. 45. マルチタスク • コンピューター内で複数のタスクが同時に動作 • CPUコア数に制限されない タスク1 タスク2 タスク3 … タスクの動作期間 実際にCPUを使って 動いている期間 1つのCPUコアを複数の タスクがシェアしてる 問題は • どうやって他のタスクにCPUを譲るか • 誰がどうスケジューリングするか
  46. 46. 2種類のマルチタスク † preemptive: 専買権を持つ、横取りする ※cooperative • ハードウェア タイマーを使って強制割り込み • OSが特権的にスレッド切り替えを行う • ○利点: 公平 (どんなタスクも等しくOSに制御奪われる) • ×欠点: 高負荷 (切り替えコストと使用リソース量が多い) プリエンプティブ† • 各タスクが責任を持って終了する • 1つのタスクが終わるまで次のタスクは始まらない • ○利点: 低負荷 • ×欠点: 不公平 (1タスクの裏切りが、全体をフリーズさせる) 協調的※ なのでスレッドはこっち これが致命的 ただ、問題はこれ
  47. 47. 問題: スレッドは高コスト† • 細々としたタスクを大量にこなすには向かない for (int i = 0; i < 1000; i++) { var t = new Thread(Worker); t.Start(); } 大量の処理をスレッド実行 リソース消費大 切り替え頻発 … † スレッドごとに数MBのメモリを確保したり スレッド切り替えにOS特権が必要だったり 文脈が切り替わっちゃうのでキャッシュ ミスしたり
  48. 48. 解決策: スレッド プール • スレッドを可能な限り使いまわす仕組み • プリエンプティブなスレッド数本の上に • 協調的なタスク キューを用意 スレッド プール キュー タスク1 タスク2 … 数本※のスレッ ドだけ用意 空いているスレッドを探して実行 (長時間空かない時だけ新規スレッド作成) 新規タスク タスクは一度 キューに溜める ※ 理想的にはCPUのコア数分だけ
  49. 49. スレッド プールの性能向上 • Work Stealing Queue • lock-free実装†なローカル キュー • できる限りスレッド切り替えが起きない作り ローカル キュー1 ローカル キュー2 スレッド1 スレッド2 グローバル キュー ① スレッドごとに キューを持つ まず自分用の キューからタスク実行 ② ローカル キュー が空のとき、 他のスレッドから タスクを奪取 † 今回は詳細割愛
  50. 50. スレッド プールの性能向上 • Work Stealing Queue • lock-free実装†なローカル キュー • できる限りスレッド切り替えが起きない作り ローカル キュー1 ローカル キュー2 スレッド1 スレッド2 グローバル キュー ① スレッドごとに キューを持つ まず自分用の キューからタスク実行 ② ローカル キュー が空のとき、 他のスレッドから タスクを奪取 ポイント • スレッドは高コスト • Threadクラスはこっち • スレッド プールの利用推奨 • Taskクラスはこっち † 今回は詳細割愛
  51. 51. 問題: CPUの外の世界は遅い • 実行速度が全然違う ALU メイン・メモリ CPU 周辺機器 数千~ 下手すると数万、数億倍遅い >>>
  52. 52. 2種類の負荷 • CPU-bound (CPUが性能を縛る) • マルチコアCPUの性能を最大限引き出したい • UIスレッドを止めたくない • I/O-bound (I/O※が性能を縛る) • ハードウェア割り込み待つだけ • CPUは使わない • スレッドも必要ない ※ Input/Output: 外部ハードウェアとのやり取り(入出力)
  53. 53. I/O完了待ち • I/O-boundな処理にスレッドは不要 あるスレッド 要求 応答 この間何もしないのに スレッドを確保し続け るのはもったいない
  54. 54. 解決策: I/O完了ポート※ • スレッドを確保せずI/Oを待つ仕組み • コールバックを登録して、割り込みを待つ • コールバック処理はスレッド プールで スレッド プール タスク1 タスク2 … ※ I/O completion port あるスレッドアプリ I/O完了ポート ハードウェア I/O開始 I/O完了 コールバック 登録 コールバック登録後、 すぐにスレッド上での 処理を終了 割り込み信号
  55. 55. I/O完了ポート※ • スレッドを確保せずI/Oを待つ仕組み • コールバックを登録して、割り込みを待つ • コールバック処理はスレッド プールで スレッド プール タスク1 タスク2 … ※ I/O completion port あるスレッドアプリ I/O完了ポート ハードウェア I/O開始 I/O完了 コールバック 登録 コールバック登録後、 すぐにスレッド上での 処理を終了 割り込み信号 ポイント • I/O-boundな処理にスレッドを使っちゃダメ • I/O用の非同期メソッドが用意されてる (内部的にI/O完了ポートを利用)
  56. 56. 問題: スレッド安全保証 • スレッド安全なコードは高コスト • いっそ、単一スレッド動作を前提に • メッセージ ポンプ MSG msg; while (PeekMessage(&msg, (HWND)NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } こんな感じのループが単一スレッドで動作 他のスレッドからメッセージを キュー越しに受け取って処理
  57. 57. 典型例: UIスレッド • GUIはシングル スレッド動作(UIスレッド)† • ユーザーからの入力受け付け • 画面の更新 UIスレッド ユーザー からの入力 OK グラフィック 更新 他のスレッド 処理中は 応答不可 他のスレッドから は更新不可 † C#/.NETに限らず、だいたいの言語・環境でGUIはシングル スレッド動作
  58. 58. 矛盾 単一スレッドからしか UI更新できない そのスレッドを止める とUIフリーズ シングル スレッド推奨 マルチ スレッド推奨 OK
  59. 59. 解決策: メッセージ配送 1. スレッド プールで重たい処理 2. UIスレッドに処理を戻してからUI更新 UIスレッド OK 更新 他のスレッド 重たい処理 Dispatcher.Invoke Task.Run ※ dispatcher: 配送者 渡す役割を担うのが ディスパッチャー※ 他のGUIフレームワークだとevent queueとかhandlerとかいう 名前で提供されたりするものの、やってることは一緒 いったんUIスレッド にメッセージを渡す
  60. 60. おまけ: 他の言語 • スクリプト言語の類はだいたいスレッド機能 持ってない • GUIを前提にしていないか • GUIがフリーズするほどの処理を書くことを前提に していないか • C++とかは最近(11で)ようやく標準化 • それまでは、スレッドは結構環境依存
  61. 61. ポイント • 応答性と全体のパフォーマンスとの兼ね合い • スレッド プール • I/O待ちのためにスレッドは使っちゃダメ • I/O完了ポート • スレッド安全保証はそれなりに高コスト • シングル スレッドでメッセージ ポンプ • メッセージ配送 Task クラス
  62. 62. 2時間目 • メタデータ • 動的リンク • JIT • PCL 1 2 3
  63. 63. メタデータとは • 実行可能ファイル中には本来不要なデータ • プログラムを作るためのデータ • 実行に必要なデータよりもメタ(高次) var x = p.X; var y = p.Y; var z = p.Z; var pp = (byte*)&p; var x = *((int*)pp); var y = *((int*)(pp + 4)); var z = *((int*)(pp + 8)); プロパティ名とか 単にプログラムを動かすだけなら 相対アドレスだけわかればいい .NETでは、こういうメタデータを実行可能ファイルに残す
  64. 64. .NETのメタデータ • .NETのメタデータ(≒ 型情報) • DLLにどういう型が含まれるか • どういう型がどういうメンバーを持っているか • 外部のどういうDLLを参照しているか • バージョン情報 • DLLにメタデータを残すことで • プログラミング言語をまたげる • 動的リンクでのバージョン管理ができる
  65. 65. おまけ: 昔と今 • COMの頃 • メタデータを自分で書いてた (自動化するツールあるけど) • .tlb/.olbファイル • .NET Framework • .NET自体がメタデータの規格を持ってる • C#とかをコンパイルするだけで作られる
  66. 66. おまけ: C++/CX • C++ Component Extensions • マイクロソフトのC++拡張 C++/CX 素の標準 C++コード COMコード メタデータ (winmd) コンパイル C++内で完結して使う分 にはオーバーヘッドなし COMを呼べる言語なら 何からでも呼べる .NETのメタデータと互換 .NETから簡単に呼べる
  67. 67. 動的リンク 実行可能ファイルにメタデータが含まれていることで
  68. 68. ライブラリ共有 • 前提: ライブラリは共有する アプリA アプリB ライブラリX ライブラリY version 1 version 1
  69. 69. 動的リンク • ライブラリ単体での差し替え • セキュリティ ホールや、致命的なバグの修正 アプリA アプリB ライブラリX ライブラリY version 2 version 1 更新不要 差し替え アプリ実行時にライブラリをリンクしなおしてる = 動的リンク
  70. 70. いまどきの事情 • ライブラリはアプリのパッケージに同梱 • 動的なリンクいるの? アプリA ライブラリX ライブラリY アプリB ライブラリX ライブラリY アプリAパッケージ アプリBパッケージ 別バイナリ・別々に配布 別バージョンでも困らない とはいえ…
  71. 71. 差分ダウンロード ※ Windowsストア アプリはこういう仕組み持ってる アプリA ライブラリX ライブラリY アプリAパッケージ version 1 version 1 アプリA ライブラリX ライブラリY アプリAパッケージ version 2 version 2 version 1 version 1 version 1 version 1 ライブラリX 差分 version 2 ダウンロード アプリA ver.1 インストール機 バージョンアップ時 更新 ここだけ新しい
  72. 72. アプリB ライブラリX ライブラリY アプリ間でライブラリ共有 ※ Windowsストア アプリはこういう仕組み持って アプリA ライブラリX ライブラリY アプリAパッケージ ライブラリY アプリBパッケージ 差分 アプリA インストール機アプリB アプリBインストール時 X、Yは同じものを共有 (ハード リンク作るだけ) version 1 version 1 version 1 version 1 同じバージョン
  73. 73. パッケージ管理 ※ ASP.NET vNextはこういう仕組み持ってる 開発機 アプリ サーバー パッケージ リポジトリ 例 http://nuget.org http://myget.org project.json *.cs "dependencies": { "Library.A": "", "Library.B": "" } var c = new Configuration(); c.AddJsonFile("config.json"); a.UseServices(services => …); ソースコードだけを アップロード サーバー上で 編集可能 ライブラリの不足・更新 はクラウドから取得
  74. 74. ポイント • 動的にリンク • 部分更新・差分ダウンロード • ライブラリ共有 • パッケージ管理 • リンクに必要な情報(メタデータ)を実行可能 ファイルに残す • メタデータも規格化されてる • C#/.NETの場合はコンパイルするだけで作られる
  75. 75. JIT (Just-in-Time compile) 実際の動的リンクの挙動
  76. 76. 中間コードとJITコンパイル • .NET Frameworkの中間言語 • 高級言語のコンパイラーを作る人と、CPUごとの最 適化する人の分業化 • セキュリティ チェックしやすくなったり • 動的リンク時に、コード修正の影響をJITで吸収 高級言語 (C#など) 中間言語 (IL) ネイティブ コード ビルド時に コンパイル Just-in-Time コンパイル JITである必要ない LLVMとかでも中間言語介してコンパイルしてる ストア審査ではじくとか他にも手段はある
  77. 77. 例 • (C#で)こんな型があったとして • 整数のフィールドを3つ持つ public struct Point { public int X; public int Y; public int Z; }
  78. 78. 例 • こんなメソッドを書いたとする • フィールドの掛け算 static int GetVolume(Point p) { return p.X * p.Y * p.Z; }
  79. 79. IL • C#コンパイル結果のIL .method private hidebysig static int32 GetVolue(valuetype Point p) cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: ldfld int32 Point::X IL_0006: ldarg.0 IL_0007: ldfld int32 Point::Y IL_000c: mul IL_000d: ldarg.0 IL_000e: ldfld int32 Point::Z IL_0013: mul IL_0014: ret } 型とかフィールド の名前がそのまま 残ってる 型情報 メタデータ
  80. 80. ネイティブ コード • JIT結果 (x64の場合) push ebp mov ebp,esp cmp dword ptr ds:[5011058h],0 je 00FE2A01 call 74B7AEA8 mov eax,dword ptr [ebp+8] lea edx,[ebp+8] imul eax,dword ptr [edx+4] lea edx,[ebp+8] imul eax,dword ptr [edx+8] pop ebp ret 0Ch 4とか8とかの数値に 型情報は残らない
  81. 81. メモリ レイアウト • この4とか8の意味 public struct Point { public int X; public int Y; public int Z; } Point X Y Z 4バイト 8バイト ※レイアウトがどうなるかは環境依存
  82. 82. メモリ レイアウト • この4とか8の意味 public struct Point { public int X; public int Y; public int Z; } Point X Y Z 4バイト 8バイト ※レイアウトがどうなるかは環境依存 ILの時点までは名前 で参照してる ネイティブ コードは レイアウトを見て 数値で参照してる
  83. 83. 数値でのフィールド参照 • C#で擬似的に書くと static int GetVolume(Point p) { return p.X * p.Y * p.Z; } var pp = (byte*)&p; var x = *((int*)pp); var y = *((int*)(pp + 4)); var z = *((int*)(pp + 8)); return x * y * z; 4とか8とかの数値に ※これ、一応C#として有効なコード(unsafe)
  84. 84. 変更してみる • 大して影響しなさそうな ほんの些細な変更をしてみる public struct Point { public int X; public int Y; public int Z; } public struct Point { public int X; public int Z; public int Y; } フィールドの順序変更
  85. 85. その結果起きること • メモリ レイアウトが変わる※ Point X Y Z Point X Z Y ※ この例(フィールド変更)以外でも、仮想メソッド テーブル とかいろいろレイアウトが変わるものがある
  86. 86. ILレベルでの影響 • 影響なし IL_0000: ldarg.0 IL_0001: ldfld int32 Point::X IL_0006: ldarg.0 IL_0007: ldfld int32 Point::Y IL_000c: mul IL_000d: ldarg.0 IL_000e: ldfld int32 Point::Z IL_0013: mul IL_0014: ret 名前で参照してるん だから特に影響ない JITが吸収してくれる
  87. 87. ネイティブ レベルでの影響 • ここで影響が出る push ebp mov ebp,esp cmp dword ptr ds:[5011058h],0 je 00FE2A01 call 74B7AEA8 mov eax,dword ptr [ebp+8] lea edx,[ebp+8] imul eax,dword ptr [edx+4] lea edx,[ebp+8] imul eax,dword ptr [edx+8] pop ebp ret 0Ch 8 4 更新が必要 利用側の再コンパイルが必要 ライブラリ側だけの差し替えじゃダメ
  88. 88. ただし… • この役割に焦点を当てるなら… • 毎回毎回JITする必要ない • 全部が全部ILな必要ない
  89. 89. Ngen • Ngen.exe • Native Image Generator • ILを事前にネイティブ化するためのツール • 自前管理が必要 • アプリのインストーラー※とかを作って明示的に呼び出し • 参照しているライブラリが更新された時には呼びなおす 必要あり • かなり面倒なのでアプリを Ngenすることはめったにない • .NET自体が標準ライブラリの 高速化のために使ってる ※ 要するに、JITの負担を起動時じゃなくてインストール時に前倒しする
  90. 90. Auto-Ngen • .NET Framework 4.5以降なら • NgenがWindowsサービスとして常に動いてる • アイドル時に動作 • 利用頻度の高いものを自動的にNgen • デスクトップ アプリの場合はGACアセンブリのみ • Windowsストア アプリの場合はすべてのアセンブリ • よく使うアプリの起動はだいぶ早くなる • インストール直後の起動は相変わらず遅い
  91. 91. MDIL (ネイティブのおさらい) • おさらい: ネイティブ コードだと push ebp mov ebp,esp cmp dword ptr ds:[5011058h],0 je 00FE2A01 call 74B7AEA8 mov eax,dword ptr [ebp+8] lea edx,[ebp+8] imul eax,dword ptr [edx+4] lea edx,[ebp+8] imul eax,dword ptr [edx+8] pop ebp ret 0Ch 参照しているライブラリ のレイアウトが変わった 時に再コンパイルが必要
  92. 92. MDIL (部分的にネイティブ化) • じゃあ、こんな形式があればいいんじゃ? push ebp mov ebp,esp cmp dword ptr ds:[5011058h],0 je 00FE2A01 call 74B7AEA8 mov eax,dword ptr [ebp+8] lea edx,[ebp+8] imul eax,dword ptr [edx+4] lea edx,[ebp+8] imul eax,dword ptr [edx+8] pop ebp ret 0Ch int32 Point::X int32 Point::Y int32 Point::Z ほぼネイティブ レイアウトのところ だけ抽象的に型情報 を残しておく MDIL: Machine Dependent Intermediate Language
  93. 93. MDIL (Compile in the Cloud) • ストア サーバー上でMDIL化までやっておく C#コード IL MDIL ネイティブ コード C#コンパイラー MDILコンパイラー リンカー 開発環境でILにコンパイル Windowsストア サーバー 上でILをMDIL化 Windows Phone実機上では レイアウトの解決(リンク)だけ行う インストール直後の起動も高速 ※ Windows Phoneアプリはこういう仕組み持ってる
  94. 94. ポイント • JITの主な利点 • 動的リンクしやすい • (後述の、動的コード生成しやすいとかもある) • ただし、常にJust-In-Timeな必要ない • インストール時 → Ngen • サービスで定期的に → Auto-Ngen • ストア サーバー上で → Compile in the Cloud
  95. 95. vNextはJITだけじゃない • (詳細は後述) 選べる実行形態 • 事前に完全ネイティブ化 → .NET Native • ソースコード配置 → Cloud Mode
  96. 96. リフレクション 動的コード生成
  97. 97. メタデータ利用 • メタデータ(プログラム生成に必要な情報)を 持っているということは • 動的にプログラム コードを生成できる • 自己反映的動作(リフレクション)
  98. 98. コード生成API 高級言語 (C#) 構文木 IL ネイティブ コード parse emit JIT .NET Compiler Service† 式ツリー (System.Linq.Expressions) ILGenerator † 旧称(コードネーム)Roslyn 動的なコンパイルが可能
  99. 99. C#コードから • .NET Compiler Serviceの例 session.Execute<Func<Point, int>>("p => p.X * p.Y * p.Z") C#ソースコードをコンパイル C#→[parse]→構文木→[emit]→IL→[JIT]→Native
  100. 100. 構文木から • System.Linq.Expressions Expression.Lambda<Func<Point, int>>( Expression.Multiply( Expression.Multiply( Expression.Field(p, x), Expression.Field(p, y)), Expression.Field(p, z)),
  101. 101. ILを直接生成 • ILGenerator var gen = m.GetILGenerator(); gen.Emit(OpCodes.Ldarg_0); gen.Emit(OpCodes.Ldfld, x); gen.Emit(OpCodes.Ldarg_0); gen.Emit(OpCodes.Ldfld, y); gen.Emit(OpCodes.Mul); gen.Emit(OpCodes.Ldarg_0); gen.Emit(OpCodes.Ldfld, z); gen.Emit(OpCodes.Mul); gen.Emit(OpCodes.Ret);
  102. 102. コード生成にかかる時間 • 本当はあんまり動的にやりたくない • 当たり前だけどparseは遅い • なので、.NETはソースコード配布じゃなくてIL配布 • できる限りコンパイル時生成したい かかった時間[ミリ秒] ILGenerator (JIT) 39.89 Expressions (emit→JIT) 67.94 Compiler Service (parse→emit→JIT) 4314 倍遅い 2桁遅い ある環境で、あるコードの生成結果の例
  103. 103. ポイント • メタデータを持ってる = 動的コード生成でき る • C#ソースコードから: .NET Compiler Service • 式ツリーから: System.Linq.Expressions • IL直接生成: ILGenerator 遅い・楽 速い・面倒 ※ .NET Native(後述)だと動的コード実行が インタープリター方式になっちゃって かなり遅いので注意
  104. 104. vNext JIT以外の実行方法 .NET vNext • .NET Native • Cloude Mode
  105. 105. JITは便利なんだけど 利点 • 動的リンク・部分更新しやすい • セキュリティ検証しやすい • (ソースコード配布よりは)高速 欠点 • .NET Frameworkのランタイム インストール必須 • (ネイティブ配布よりは)低速 • (ソースコード配布よりは)デバッグ・修正が面倒 用途によっては…
  106. 106. 例えば携帯デバイス向け 利点 • 動的リンク・部分更新しやすい • セキュリティ検証しやすい • (ソースコード配布よりは)高速 欠点 • .NET Frameworkのランタイム インストール必須 • (ネイティブ配布よりは)低速 • (ソースコード配布よりは)デバッグ・修正が面倒 ストア サーバー上で やればいい ランタイム分のストレージ 容量使いたくない 性能的に結構困る ストア サーバー上で ネイティブ化 .NET Native
  107. 107. .NET Native: 事前ネイティブ化 • .NETアプリを事前にネイティブ化 • 実用上はストア サーバー上でネイティブ化 • セキュリティ保証 • アップロード時にはIL • 審査付き • クライアント デバイス的には低コスト • JITのコストなし • ランタイムのインストール不要 • Visual Studio上で直接ネイティブ化も可能 • 通常版の.NETとの差で問題が起きないかの確認 • パフォーマンスの確認
  108. 108. .NET Native: 最適化 • 「事前にJIT相当の処理」以上の最適化も • シリアライズなどのコードを事前に展開 • 型情報を見たいからリフレクションを使ってただけで、 実際にはビルド時に型が確定してることが多い • 必要な分だけ静的リンク • ライブラリをまたいだ最適化が可能 • 不要コードは残さないので実行可能ファイルのサイズは 膨らまない
  109. 109. .NET Native: リフレクション • リフレクションも使える ただし… • XAML {Binding}など、推論が効く部分は自動判定し てくれる • それ以外は、自分で「この型はリフレクションで 使ってる」という情報を書かないとダメ (Runtime Directiveファイル) • 動的コード生成は無理 • 式ツリーなどはインタープリター方式になるので遅い
  110. 110. 例えばサーバー向け 利点 • 動的リンク・部分更新しやすい • セキュリティ検証しやすい • (ソースコード配布よりは)高速 欠点 • .NET Frameworkのランタイム インストール必須 • (ネイティブ配布よりは)低速 • (ソースコード配布よりは)デバッグ・修正が面倒 アプリ稼働時間が長くて、 最初の1回だけのコストは 無視できる(低速でもいい) 開発機とサーバー機でバー ジョンが違って困ることが サーバー上で確認しつつ その場でソースコードを修正したい ソースコード配置 自動パッケージ管理 Cloud Mode アプリごとに別バー ジョンを使えない
  111. 111. Cloud Mode: • ソースコード配置 • .NET Compiler Service (Roslyn)を利用 • メモリ上で全部コンパイル(一時ファイルを残さない) • ソースコード書き替えて、ブラウザー更新するだけ • side-by-sideインストール • (サーバーにインストールするんじゃなく) アプリごとに別.NETランタイムを使える • アプリごとにバージョンを変えれる • 開発時と同じバージョンを使える
  112. 112. Cloud Mode: パッケージ管理 • 自動パッケージ管理 開発機 アプリ サーバー パッケージ リポジトリ 例 http://nuget.org http://myget.org project.json *.cs "dependencies": { "Library.A": "", "Library.B": "" } var c = new Configuration(); c.AddJsonFile("config.json"); a.UseServices(services => …); ソースコードとパッ ケージ利用情報だけ をアップロード サーバー上で 編集可能 .NETランタイムや、 ライブラリの不足・更新 はクラウドから取得 利用するパッケージ の情報ファイル
  113. 113. ポイント • 選べる実行方法 • IL配布(JIT) • デスクトップなら割とこれが便利 • ネイティブ配布(.NET Native) • 携帯デバイス向け • ソースコード配置(Cloud Mode) • サーバー向け • 特に共有ホスティングなクラウド サーバー
  114. 114. Portable Class Library 複数の「標準ライブラリ」 いろんな実行環境の共通部分
  115. 115. 問題: 複数の標準ライブラリ • マイクロソフト製品だけでも… デスクトップ クライアント アプリ Phone/タブレット クライアント アプリ サーバー アプリ 共通部分 この共通部分だけを 「標準」にすべき? .NET Native Cloud Mode
  116. 116. 問題: 複数の標準ライブラリ • まして今、Xamarin (Mono) • Xamarin.iOS、Xamarin.Android Windows デスクトップWindows タブレット/Phone Linux サーバー この共通部分だけを 「標準」にすべき? Windows サーバー iOS Android
  117. 117. .NET FrameworkとMono C# VB F# C# CLR Mono ランタイム .NET Full プロファイル .NET Core プロファイル .NET Full (サブセット) iOS向け プロファイル Android向け プロファイル .NET Framework Mono コンパイラー 実行環境 ライブラリ 実行環境にはかなり の互換性がある 標準で使える ライブラリが違う
  118. 118. どこでも使えそうに見えても… • 例えばファイル システム • 最近のWindowsはいろんなプロパティを持ってる • 画像ファイルなんかだと: サムネイル画像、撮影日時、画像サイズ、タグ • 検索インデックスも張ってる • ストア アプリだとファイル システムすら制限下 • ユーザーの許可がないファイルには触れない こういうものも標準に含めたいか? 一番制限がきつい環境に合わせて標準ライブラリを作るべき?
  119. 119. 汎用 VS 特定環境 どこでも動く • 最大公約数 • 動作保証に時間が かかる 特定環境で動く • 高機能 • 早く提供できる
  120. 120. Portable Class Library • 実行環境ごとに 別の「標準ライブラリ」 メタデータを用意 • ライブラリ側でどの 実行環境をターゲットに するか選ぶ 実行環境ごとに 別メタデータを提供 ターゲット選択
  121. 121. Portable Class Library • 例: 2環境 共通部分
  122. 122. Portable Class Library • 例: 3環境 共通部分
  123. 123. .NET vNext • ぶっちゃけ3系統別実装 (主用途も別) • JIT • .NET Native • Cloud Mode • 1つの“標準”ライブラリで保守するのは大変 • どうしても事前ネイティブ化しにくい… • サーバーにGUIコンポーネント要るの?… • フットプリント的に余計なもの載せれない… 既存品とはいえ、ちゃんと次世代でも更新あり • 64ビット対応強化 • SIMD演算対応 new! new! PCLみたいな仕組み必須
  124. 124. ポイント • 複数の“標準” • 用途によって • デスクトップ、携帯デバイス、クラウド • 実装によって • JIT、事前ネイティブ化、ソースコード配置 • 他OSへのポーティング ME SE EE v2.0 v3.0 v4.0 × 包含関係では不十分 ○ 複数の“標準”の共通部分管理
  125. 125. 3時間目 • C#言語機能 • generics • iterator • LINQ • dynamic • async/await • .NET Compiler Platform 1 2 3
  126. 126. C#の歴史 C# 1.0 • Managed C# 2.0 • Generics • Iterator C# 3.0 • LINQ C# 4.0 • Dynamic C# 5.0 • Async ※VB 7~11の歴史でもある
  127. 127. ジェネリック C# 2.0 C++で言うところのtemplate C++ templateと.NET Genericsの違い
  128. 128. 型違いの処理 • 型だけ違う処理、コピペで書いていませんか int Max(int x, int y) { return x > y ? x : y; } class IntStack { void Push(int item) { … } int Pop() { … } } double Max(double x, double y) { return x > y ? x : y; } class DoubleStack { void Push(double item) { … } double Pop() { … } }
  129. 129. ジェネリック† • 型をパラメーター化 † generics。MS翻訳ルール的に、語尾 s は取るんですって 複数形のs扱いで。単複の区別のない言語に訳すとき class Stack<T> { void Push(T item) { … } T Pop() { … } } T Max<T>(T x, T y) where T : IComparable<T> { return x.CompareTo(y) > 0 ? x : y; } int Max(int x, int y) { return x > y ? x : y; } class IntStack { void Push(int item) { … } int Pop() { … } }
  130. 130. 型の直行化 • 要素の型と、処理・要素管理は分けましょう 要素の型 処理 int byte double string … Max Min Average … 管理方式 List LinkedList Stack …
  131. 131. 型の直行化 • 要素の型と、処理・要素管理は分けましょう 要素の型 処理 int byte double string … Max Min Average … 管理方式 List LinkedList Stack … Lパターン Mパターン Nパターン 分けて作らないと L×M×N 通りのパターン 分けて作ると L+M+N 通り
  132. 132. 2.0からなので… • 1.0の頃の名残がちらほら… • 今となっては GroupCollection MatchCollection AttributeCollection StringCollection … IEnumerable<Group> IEnumerable<Match> IEnumerable<Attribute> IEnumerable<string> … IReadOnlyCollection<Group> IReadOnlyCollection<Match> IReadOnlyCollection<Attribute> IReadOnlyCollection<string> … もしくは 1つの型で済む † † System.dllに実在する型。ごくごく一部の抜粋。
  133. 133. .NETのジェネリック • ILレベル対応 • メタデータがちゃんと残る • リフレクションで厳密な型をとれる・区別できる • ジェネリック用命令持ってる • JIT時に展開 • キャストとかの不要コードが消える • 値型のボックス化も消える • 値型の展開はかなりパフォーマンスに寄与
  134. 134. おまけ: C++のtemplate • コンパイル時に展開 • 超高機能なマクロみたいなもの • ○ 実行時のパフォーマンスいい • ○ かなり自由が効く • × 実行可能ファイル サイズが肥大化しがち • × コンパイル エラーが出た時結構悲惨
  135. 135. おまけ: Javaのジェネリック • 型消去 • コンパイル時に実は型が消えてる • 全部object扱い • ○? Java 1.0の頃からbytecode命令増えてない • × 実行時にリフレクションで情報とれない • ×キャストが挟まる • × 全然違う型にキャストできちゃう(実行時例外)
  136. 136. いろいろ窮屈な面も • .NETのジェネリックでは、インターフェイス制 約かけないとメソッドすら呼べない static Type Max<Type>(Type a, Type b) { return a.CompareTo(b) > 0 ? a : b; } static Type Max<Type>(Type a, Type b) where Type : IComparable { return a.CompareTo(b) > 0 ? a : b; } コンパイル エラー そんなメソッド知らない 正しくは インターフェイス制約 IComparable.CompareTo
  137. 137. いろいろ窮屈な面も • 特に困るのが演算子使えないこと • 実体は静的メソッドなので static T Sum<T>(T[] array) { T sum = default(T); foreach (var x in array) sum += x; return sum; } コンパイル エラー 演算子定義されてない
  138. 138. いろいろ窮屈な面も • dynamicでごまかせなくはないけども… • パフォーマンス出ないので最後の手段 static T Sum<T>(T[] array) { dynamic sum = default(T); foreach (var x in array) sum += x; return sum; } 実行時コード生成で +演算子が呼ばれる
  139. 139. ポイント • 型のパラメーター化 • 似て非なるコピペ コードの解消 • 型の直行化 • {int, byte, string, …} × {List, Stack, Queue, …} • .NETのジェネリックはILレベル対応 • メタデータあり • 実行時にリフレクションで情報とれる • JIT時に展開 • キャストなどの不要なコードは挟まらない
  140. 140. イテレーター C# 2.0 イテレーター生成用の構文
  141. 141. データの列挙 • プログラム中、データの列挙は非常に多い • データを使う側はすごく楽 • 作る側、加工する側は? foreach (var x in data) { Console.WriteLine(x); }
  142. 142. データの列挙の例 • substringの列挙 • データを作る側と使う側を分けないなら static void WriteSubstrings(string s) { for (var len = s.Length; len >= 1; len--) for (var i = 0; i <= s.Length - len; i++) Console.WriteLine(s.Substring(i, len)); } いつもConsole.Writeしたいわけじゃない substringを使いたいたびに同じコード書くの?
  143. 143. データの列挙の例 • substringの列挙、分けたいなら • ↓こんな感じのクラスを書けばいいんだけど… class GetSubstringEnumerator { public string Current { get; private set; } string _s; int _len; int _i; public GetSubstringEnumerator(string s) { _s = s; _len = s.Length; _i = 0; } public bool MoveNext() { for (; _len >= 1; _len--, _i = 0) for (; _i <= _s.Length - _len; ) { Current = _s.Substring(_i, _len); _i++; return true; } return false; } } こういうクラスを イテレーター(iterator)とか 列挙子(enumerator)って言う 作るの結構面倒
  144. 144. イテレーターの例 • substringの列挙 static IEnumerable<string> GetSubstrings(string s) { for (var len = s.Length; len >= 1; len--) for (var i = 0; i <= s.Length - len; i++) yield return s.Substring(i, len); } foreach (var x in GetSubstrings("abcd")) Console.WriteLine(x); 実装側 使う側 イテレーター ブロック† (= yield returnを持つ関数ブロック) イテレーター クラスを自動生成
  145. 145. 内部実装(全体像) • クラス生成 class SubstringEnumerable : IEnumerator<string>, IEnumerable<string> { readonly string _s; int _len; int _i; int _state = 0; public SubstringEnumerable(string s) { _s = s; } public string Current { get; private set; } public bool MoveNext() { if (_state == 1) goto STATE1; if (_state == -1) goto END; _state = 1; _len = _s.Length; LOOP1BEGIN: ; if (!(_len >= 1)) goto LOOP1END; _i = 0; LOOP2BEGIN: ; if (!(_i <= _s.Length - _len)) goto LOOP2END; _state = 1; Current = _s.Substring(_i, _len); return true; STATE1: ; _i++; goto LOOP2BEGIN; LOOP2END: ; _len--; goto LOOP1BEGIN; LOOP1END: ; _state = -1; END: ; return false; } public void Reset() { throw new NotImplementedException(); } public void Dispose() { } object IEnumerator.Current { get { return Current; } } public IEnumerator<string> GetEnumerator() { if(_state == 0) return this; else return new SubstringEnumerable(_s).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } static IEnumerable<string> GetSubstrings(string s) { for (var len = s.Length; len >= 1; len--) for (var i = 0; i <= s.Length - len; i++) yield return s.Substring(i, len); }
  146. 146. 内部実装(ローカル変数) • ローカル変数 → フィールド class SubstringEnumerable : IEnumera { readonly string _s; int _len; int _i; int _state = 0; public SubstringEnumerable(strin public string Current { get; pri public bool MoveNext() { if (_state == 1) goto STATE1 if (_state == -1) goto END; static IEnumerable<string> GetSubstrings(string s) { for (var len = s.Length; len >= 1; len--) for (var i = 0; i <= s.Length - len; i++) yield return s.Substring(i, len); }
  147. 147. 内部実装(yield return) • yield return → 状態記録、return、caseラベル • 中断と再開 if (_state == 1) goto STATE1; if (_state == -1) goto END; _state = 1; _len = _s.Length; LOOP1BEGIN: ; if (!(_len >= 1)) goto LOOP1END; _i = 0; LOOP2BEGIN: ; if (!(_i <= _s.Length - _len)) go _state = 1; Current = _s.Substring(_i, _len); return true; STATE1: ; _i++; goto LOOP2BEGIN; static IEnumerable<string> GetSubstrings(string s) { for (var len = s.Length; len >= 1; len--) for (var i = 0; i <= s.Length - len; i++) yield return s.Substring(i, len); }
  148. 148. switch(_state) { case 0: for (_len = _s.Length; _len >= 1; _len--) for (_i = 0; _i <= _s.Length - _len; _i++) { _state = 1; Current = _s.Substring(_i, _len); return true; case 1:; } _state = -1; } 内部実装(yield return) • yield returnの部分、意味合いとしては† static IEnumerable<string> GetSubstrings(string s) { for (var len = s.Length; len >= 1; len--) for (var i = 0; i <= s.Length - len; i++) yield return s.Substring(i, len); } yield return以外の 場所はほぼ同じ yield return x; _state = 1; Current = x; return true; case 1:; † forループ内にラベル張れないからさっきみたいな複雑なコードになるけども switchで囲う
  149. 149. 内部実装(中断と再開) • yield returnの部分、意味合いとしては • 中断と再開 yield return x; _state = 1; Current = x; return true; case 1:; 状態の記録 現在の値の保持 復帰用のラベル
  150. 150. おまけ: 他の言語だと • 最近の言語は結構似た機能†持ってる • スタック丸ごとキャプチャしてしまうもの • 中断時に記録、再開時に復元 • 式を継続渡しスタイル(CPS)に変換してしまうもの • スレッド使う(さすがに性能的に論外だけども) • 割と機械的な置き換えなので、マクロでも † 他の言語だとジェネレーター(generator)って呼ばれることが多い #define BEGIN_ITERATOR switch(_state) { case 0: #define YIELD(STATE, VALUE) _state = STATE; _current = VALUE; return true; case STATE:; #define END_ITERATOR _state = -1; default:; } return false;
  151. 151. おまけ: C++ 1y • C++にもジェネレーターが載るかも • C++ 17に向けて標準化案出てる • C++/CX向けの実装を元にMSが提案 • async/awaitのついで sequence<int> range(int low, int high) resumable { for(int i = low; i <= high; ++i) { yield i; } }
  152. 152. ちなみに • コーディング面接で有名な某社の社員さん曰く、 • 例に使ったのsubstring列挙も割とよく出てくるパ ターン • 中断と再開って大事 アルゴリズムの問題はかなりの割合、 イテレーターを使うとあっさり書ける 「 」
  153. 153. ポイント • データ列挙は多い • データを作る側と使う側をきっちり分離しよう と思うと結構面倒 • 特に、作る側 • 作る側を簡単化する仕組みがイテレーター static IEnumerable<string> GetSubstrings(string s) { for (var len = s.Length; len >= 1; len--) for (var i = 0; i <= s.Length - len; i++) yield return s.Substring(i, len); }
  154. 154. LINQ (Language Integrated Query) C# 3.0 データ処理の直行化
  155. 155. LINQ • データ処理を言語統合 • ほとんどはライブラリで実現 • System.Linq名前空間 var source = new[] { 1, 2, 3, 4, 5 }; var filtered = source .Where(x => x <= 3) // 1, 2, 3 .Select(x => x * x); // 1, 4, 9 Enumerableクラスの WhereメソッドとSelectメソッド が呼ばれるだけ 条件を満たす要素だけ残す 要素ごとに処理をかける
  156. 156. データ処理の直行化 • データの入力、加工、出力は分けましょう ユーザー入力から Console.Read… 配列から { 1, 2, 3, … } データベースから SELECT x FROM t ファイルから File.Read… 1, 2, 3 変換 x => x * x 1, 4, 9 1, 2, 3 選択 x => x < 3 1, 2 1, 2, 3 グループ化 x => x % 2 {1, 3}, {2} コンソールに Console.Write… 配列に ToArray() データベースに INSERT INTO t ファイルに File. Write… 入力 加工 出力
  157. 157. データ処理の直行化 • 掛け算を足し算に ユーザー入力から Console.Read… 配列から { 1, 2, 3, … } データベースから SELECT x FROM t ファイルから File.Read… 1, 2, 3 変換 x => x * x 1, 4, 9 1, 2, 3 選択 x => x < 3 1, 2 1, 2, 3 グループ化 x => x % 2 {1, 3}, {2} コンソールに Console.Write… 配列に ToArray() データベースに INSERT INTO t ファイルに File. Write… 入力 加工 出力 Lパターン Mパターン Nパターン 分けて作らないと L×M×N 通りのパターン 分けて作ると L+M+N 通り
  158. 158. yield returnで実装できる • 列挙子から別の列挙子を作る処理 • ここまではC# 2.0 • これに加えて、C# 3.0では… IEnumerable<R> Select<S, R>( IEnumerable<S> source, Func<S, R> selector) { foreach (var x in source) yield return selector(x); } IEnumerable<T> Where<T( IEnumerable<T> source, Func<T, bool> predicate) { foreach (var x in source) if (predicate(x)) yield return x; } 列挙子を引数にとって列挙子を返す yieldがあれば実装簡単
  159. 159. C# 3.0 • ラムダ式 • 匿名型 • 拡張メソッド
  160. 160. ラムダ式 • 匿名関数を簡単に書ける • =>演算子 • goes to (~になる)演算子 • 左が引数で、右が関数本体 • 型推論も効いてる source.Select(x => x * x); (シーケンスの全要素を二乗)
  161. 161. そもそも: 匿名関数 • メソッドの自動生成 IEnumerable<int> Triple(IEnumerable<int> input) { return input.Select(x => 3 * x); } IEnumerable<int> Triple(IEnumerable<int> input) { return input.Select(X); } int X(int x) { return 3 * x; }
  162. 162. ローカル変数のキャプチャ • 実はクラス生成 IEnumerable<int> Multiply(IEnumerable<int> input) { var a = int.Parse(Console.ReadLine()); return input.Select(x => a * x); } IEnumerable<int> Multiply(IEnumerable<int> input) { var _ = new Anonymous(); _.a = int.Parse(Console.ReadLine()); return input.Select(_.X); } class Anonymous { public int a; public int X(int x) { return a * x; } } ローカル変数が フィールドに昇格
  163. 163. 匿名型 • 1か所でしか使わないような型は作らなくてい い • immutableなクラスを自動生成 • GetHashCode、等値比較、ToStringを完備 source.GroupBy(p => new { p.X, p.Y }); (XとYでグループ化) class Anonymous { public int X { get; private set; } public int Y { get; private set; } public Anonymous(int x, int y) { X = x; Y = y; } }
  164. 164. 拡張メソッド • 静的メソッドを後置き記法で書ける • 単に語順を変えるだけ source.Select(x => x * x); System.Linq.Enumerable.Select( source, x => x * x); 同じ意味
  165. 165. 語順を変えるだけで • 語順のインパクト意外と大きい var result = data .Where(x => x < 3) .Select(x => x * x) .GroupBy(x => x % 2) .Select(g => g.Sum()); var result1 = Select( GroupBy( Select( Where( data, x => x < 3), x => x * x), x => x % 2), g => Sum(g)); 逆順 処理順 ( ) が遠い ( ) が近い 普通の静的メソッド 拡張メソッド
  166. 166. 静的メソッド • “static”と言ってもいろいろ static bool globalFlag; いつどこで誰が書き換えるか わからないのがダメ 静的フィールド(書き換え可能) const int Max = 100; static readonly TimeSpan interval = TimeSpan.FromSeconds(1); 定数 or 読み取り専用静的フィールド 書き換え禁止すれば無害 static int Clip(int x) { return Math.Max(x, Max); } 静的メソッド 無害なものにしか触れない限り、無害
  167. 167. 静的メソッド • “static”と言ってもいろいろ static bool globalFlag; いつどこで誰が書き換えるか わからないのがダメ 静的フィールド(書き換え可能) const int Max = 100; static readonly TimeSpan interval = TimeSpan.FromSeconds(1); 定数 or 読み取り専用静的フィールド 書き換え禁止すれば無害 static int Clip(int x) { return Math.Max(x, Max); } 静的メソッド 無害なものにしか触れない限り、無害 純粋関数(pure function) 書き換えが起こらないメソッド = 同じ引数を与えたら常に同じ結果しか返らない = テストしやすくていい
  168. 168. ポイント • C# 3.0 • ラムダ式 • 匿名型 • 拡張メソッド • 小さくて、汎用的な機能の集まり • LINQ以外でも有用 LINQ (データ処理用構文・ライ ブラリ)に関連して入った機能
  169. 169. dynamic型 C# 4.0 ダック タイピング用の型
  170. 170. おさらい: メタデータ • プロパティ名とかは、本来、実行時に要らない IL_0000: ldarg.0 IL_0001: ldfld int32 T::X IL_0006: ldarg.0 IL_0007: ldfld int32 T::Y IL_000c: mul IL_000d: ldarg.0 IL_000e: ldfld int32 T::Z IL_0013: mul IL_0014: ret push ebp mov ebp,esp cmp dword ptr ds:[5011058h],0 je 00FE2A01 call 74B7AEA8 mov eax,dword ptr [ebp+8] lea edx,[ebp+8] imul eax,dword ptr [edx+4] lea edx,[ebp+8] imul eax,dword ptr [edx+8] pop ebp ret 0Ch ILの状態 実行時(ネイティブ化した状態) 名前が残ってる 名前が消えて、 レイアウト情報 だけになってる
  171. 171. ダック タイピング† • 同じ名前のメンバーを持っていれば 同じ型扱いできないか † アヒルのように歩き、アヒルのように鳴くなら、それはアヒルだ」 という論法からきた比喩表現 class Point { public int X { get; set; } public int Y { get; set; } public void Add(Point p) { X += p.X; Y += p.Y; } }
  172. 172. ダック タイピング† • 同じ名前のメンバーを持っていれば 同じ型扱いできないか † アヒルのように歩き、アヒルのように鳴くなら、それはアヒルだ」 という論法からきた比喩表現 class Point { public int X { get; set; } public int Y { get; set; } public void Add(Point p) { X += p.X; Y += p.Y; } } ? XとYというプロパティ (またはフィールド)を 持っている任意の型を 使いたい こういう処理にはリフレクション (メタデータの実行時利用)が必要
  173. 173. dynamic型 • ダック タイピング用の型 class Point { public int X { get; set; } public int Y { get; set; } public void Add(dynamic p) { X += p.X; Y += p.Y; } } XとYというプロパティ (またはフィールド)を 持っている任意の型を 使える もちろん、内部的には リフレクション使ってる
  174. 174. dynamic型 • 結構用途が限られる • そもそもC#でダック タイピングしたい状況が稀 • ほぼ、外部との連携用 • COMとの連携 • 動的言語との連携 • JSONみたいなスキーマレスなデータ読み書き • その他、例えばできること • 多重ディスパッチ • できないこと • メタプログラミング • C#のスクリプト的実行
  175. 175. 例: 動的言語との連携 • DLR (Dynamic Language Runtime) • 例: IronPython var py = IronPython.Hosting.Python.CreateEngine(); dynamic p = py.Execute("['a', 'b', 1, 2]"); for (var i = 0; i < 4; i++) Console.WriteLine(p[i]); Pythonコード
  176. 176. 例: 多重ディスパッチ • 静的な型に対して class Base { } class A : Base { } class B : Base { }
  177. 177. 例:多重ディスパッチ • x, yの両方の型で動的に分岐 • 仮想メソッドでは(素直には)できない static class Extensions { public static string Dispatch(this Base x, Base y) { return (string)X((dynamic)x, (dynamic)y); } static string X(A x, A y) { return "A - A"; } static string X(A x, B y) { return "A - B"; } static string X(Base x, Base y) { return "others"; } } 動的な呼び出し
  178. 178. 例:多重ディスパッチ • 呼び出し例 static void Main() { Dispatch(new A(), new A()); // A - A Dispatch(new A(), new B()); // A - B Dispatch(new B(), new B()); // others Dispatch(new B(), new A()); // others } static void Dispatch(Base x, Base y) { Console.WriteLine(x.Dispatch(y)); }
  179. 179. dynamic型の内部実装 • コード生成結果 dynamic X(dynamic x) { return x.X; } object X(object x) { if (_site1 == null) { _site1 = CallSite<Func<CallSite, o Binder.GetMember(CSharpBinderF new CSharpArgumentInfo[] { CSharpArgumentInfo.Create( })); } return _site1.Target(_site1, x); } 実際にはobject 動的コード生成 用の情報 これが本体
  180. 180. CallSite.Targetの中身 • メソッドを動的コード生成してる • 生成したメソッドはキャッシュして持っておく (inline method cache) static object _anonymous(CallSite site, object x) { return site.Targetを更新する処理 } _site1.Targetの初期状態
  181. 181. CallSite.Targetの中身 • メソッドを動的コード生成してる • 生成したメソッドはキャッシュして持っておく (inline method cache) • 同じ型に対して何度も呼ぶ分には高性能 static object _anonymous(CallSite site, object x) { if (x is Point) return ((Point)x).X; else return site.Targetを更新する処理 } メソッドXにPoint型のインスタンスを渡した後 1行追加 単なる型判定+キャスト
  182. 182. CallSite.Targetの中身 • メソッドを動的コード生成してる • 生成したメソッドはキャッシュして持っておく (inline method cache) static object _anonymous(CallSite site, object x) { if (x is Point) return ((Point)x).X; if (x is Vector3D) return ((Vector3D)x).X; else return site.Targetを更新する処理 } さらに、メソッドXにVector3d型のインスタンスを渡した後 もう1行追加 ちなみに、最近はスクリプト言語でも、 内部的に型を作って、inline method cache で高速化してる
  183. 183. “heavy” dynamic • 用途によっては高性能なものの… • 多くの場合、過剰スペック • できるけど過剰スペックな例 • JSONみたいなスキーマレスなデータ読み書き • もっと”light-weight”なdynamicが必要 p.X; p.M(); p.Get("X"); p.Invoke("M"); 例: 規約ベースの置き換え 一時期、「p.$x を p["x"] と解釈しよう」 という提案はあったけども、結局立ち消え中
  184. 184. できないこと • メンバー名がコンパイル時に既知でないとダメ • なのでメタプログラミングには使えない • そのためにはもっと低レイヤーなAPI使う • IL Emit • Expression Tree • Roslyn dynamic x = p; dynamic y = q; y.X = x.X; y.Y = x.Y; コンパイル時に既知
  185. 185. ポイント • dynamic型 • C#でダック タイピング • 主に外部との連携用 • inline method cache • 用途があえば結構高性能 • 用途によっては過剰スペック • メタプログラミング用ではない
  186. 186. async/await C# 5.0 非同期処理
  187. 187. スレッド関連おさらい • 非同期処理 • マルチタスク • UIスレッドを止めない • I/O (外の世界との入出力待ち) • Taskクラスを使いましょう • スレッド プール • I/O完了ポート 割と避けれないこと なんだけども…
  188. 188. 非同期処理しんどい • 普通に非同期処理やったらコールバック地獄 • begin/end地獄 • イベント地獄 • then地獄(ContinueWith地獄) x.BeginX(args, state, ar => { var result = x.EndX(); … }); x.XCompleted += (s, arg) => { var result = arg.Result; … }); x.XAsync(); x.XAsync() .ContinueWith(t => { var result = t.Result; … };非同期呼び出しが1回だから この程度で済んでる
  189. 189. • 複数の確認ダイアログ表示 面倒な非同期処理の例 確認 1 チェック 確認 2 チェック 確認 3 チェック No No Yes Yes Yes 確認フロー 結果表示 No ゲームで アイテムを 合成します レア アイテムですよ? 合成強化済みですよ? もう強化限界ですよ? ユーザーから の入力待ちも 非同期処理
  190. 190. 同期処理 if (Check1.IsChecked) { var result = Dialog.ShowDialog("確認 1", "1つ目の確認作業"); if (!result) return false; } if (Check2.IsChecked) { var result = Dialog.ShowDialog("確認 2", "2つ目の確認作業"); if (!result) return false; } if (Check3.IsChecked) { var result = Dialog.ShowDialog("確認 3", "3つ目の確認作業"); if (!result) return false; } return true;
  191. 191. 非同期処理 (旧) • 画面に収まるように フォント サイズ調整 • 4pt • ほんの84行ほど • ちなみに • 部分部分を関数化して多 少は整理できる • ダイアログ3つだからまだ この程度で済む if (Check1.IsChecked) { Dialog.BeginShowDialog("確認 1", "1つ目の確認作業", result => { if (!result) { onComplete(false); return; } if (.Check2.IsChecked) { Dialog.BeginShowDialog("確認 2", "2つ目の確認作業", result2 => { if (!result2) { onComplete(false); return; } if (Check3.IsChecked) { Dialog.BeginShowDialog("確認 3", "3つ目の確認作業", result3 => { onComplete(result3); }); } else onComplete(true); }); } else if (Check3.IsChecked) { Dialog.BeginShowDialog("確認 3", "3つ目の確認作業", result3 => { onComplete(result3); }); } else onComplete(true); }); } else if (Check2.IsChecked) { Dialog.BeginShowDialog("確認 2", "2つ目の確認作業", result => { if (!result) { onComplete(false); return; } if (Check3.IsChecked) { Dialog.BeginShowDialog("確認 3", "3つ目の確認作業", result3 => { onComplete(result); }); } else onComplete(true); }); } else if (Check3.IsChecked) { Dialog.BeginShowDialog("確認 3", "3つ目の確認作業", result3 => { onComplete(result3); }); } else onComplete(true);
  192. 192. 非同期処理(C# 5.0) if (this.Check1.IsChecked ?? false) { var result = await Dialog.ShowDialogAsync("確認 1", "1つ目の確認作業"); if (!result) return false; } if (this.Check2.IsChecked ?? false) { var result = await Dialog.ShowDialogAsync("確認 2", "2つ目の確認作業"); if (!result) return false; } if (this.Check3.IsChecked ?? false) { var result = await Dialog.ShowDialogAsync("確認 3", "3つ目の確認作業"); if (!result) return false; } return true; • 同期処理と比べてawait演算子が増えただけ • ダイアログの数が増えても平気
  193. 193. イテレーターと似た仕組み • イテレーター(ジェネレーター)があれば、割と 単純なラッパーで非同期処理も可能 • 要は、「中断と再開」 • 例えば、TypeScriptでは • 現状でもawaitのMS社内実装は持ってる • いまだと、生成されるJavaScriptが悲惨すぎて大変 • EcmaScriptにジェネレーターが実装されてから、 TypeScriptにawaitを追加する予定
  194. 194. 参考: C#のイテレーター(再) • 中断と再開 class MethodEnumerator : IEnumerator<int> { public int Current { get; private set; } private int _state = 0; public bool MoveNext() { switch (_state) { case 0: Current = 1; _state = 1; return true; case 1: Current = 2; _state = 2; return true; case 2: default: return false; } } } IEnumerable<int> Method() { yield return 1; yield return 2; } Current = 1; _state = 1; return true; case 1: 状態の記録 中断 再開用のラベル
  195. 195. 基本的な考え方 • 概念としては イテレーター+継続呼び出し async Task<int> Method() { var x = await task1; var y = await task2; } _state = 1; if (!task1.IsCompleted) { task1.ContinueWith(a); return; } case 1: var x = task1.Result; 中断 状態の記録 結果の受け取り 再開用のラベル 非同期処理が終 わったら続きから 呼び出してもらう
  196. 196. 実際の展開結果 • 実際はもう少し複雑 • Awaiterというものを介する(Awaitableパターン) _state = 1; var awaiter1 = task1.GetAwaiter(); if (!awaiter1.IsCompleted) { awaiter1.OnCompleted(a); return; } case 1: var x = awaiter1.GetResult(); • Awaiterを自作することで awaitの挙動を変更可能 • Task以外もawait可能 • GetAwaiterは拡張メソッ ドでもOK
  197. 197. Awaitable • Awaitableなクラスは自作可能 • 実装次第でいろいろ • Windowd Runtime • IAsyncOperationインターフェイスとかをawaitできる • TaskのAwaitable実装 • UIスレッドへのディスパッチを内部的にやってくれる • そうしたくない場合の実装もある • ConfigureAwait
  198. 198. おまけ: C++ 1y • C++にもawaitが載るかも • C++ 17に向けて標準化案出てる • C++/CX向けの実装を元にMSが提案 • 前述の通り、ついでにジェネレーターも future<void> f(stream str) async { shared_ptr<vector> buf = ...; int count = await str.read(512, buf); return count + 11; }
  199. 199. おまけ: immutable • 並列処理といえばimmutableだけども • 書き換えが起こらないなら複数のスレッドで共有し ても安全 • この用途だと、C++のconstは不十分 • あれは参照渡しを安全に行う用であって • 呼び出し元の側ではconstとは限らず、書き換わる 可能性あり • あと、mutable修飾子を付ければconstなオブジェク トすら書き換えれる
  200. 200. ポイント • 非同期処理は、避けれないんだけども面倒 • async/awaitで多少マシに • 実装的にはイテレーターと同様、中断と再開
  201. 201. .NET Compiler Platform Compiler as a Service コンパイラーの内部データを活用
  202. 202. .NET Compiler Platform† • C#/VBコンパイラーを再設計・再実装 • .NET実装 • C#実装のC#コンパイラー • VB実装のVBコンパイラー • コンパイラーの内部データを誰でも自由に使える † コードネーム“Roslyn”って呼ばれてたやつの正式名称 コンパイラーのサービス化、 プラットフォーム化
  203. 203. 用途 • C#スクリプティング • C#で設定ファイル書きたい(むしろXMLがいや) • アプリの再起動なしでロジック更新 • ソースコード配置 • .NET vNext, Cloud Mode • コード解析 • コードのインデックス化† • サーバー ビルド、テスト • IDE連携 現状の最優先事項 † .NETの参照ソースコードが実際やってる http://referencesource.microsoft.com/
  204. 204. 構文ハイライト • C#は文脈キーワードだらけ static IEnumerable<async> async() { var var = "var"; var yield = new async(); yield return yield; Func<string, Task<int>> async = async x => { await Task.Delay(100); return int.Parse(x); }; var await = async(var).GetAwaiter().GetResult(); } メソッド名 クラス名 変数 キーワード
  205. 205. リアルタイム エラー検出 • エラー検出タイミングがビルド時とか遅い • Visual Studioは常時やってる
  206. 206. コード補完 • タイピングめんどくさい • 文法やライブラリ、いちいち覚えたくない
  207. 207. リファクタリング • 最初からきれいなコード書くのめんどくさい • でもほっときたくない
  208. 208. ということで • 今、コンパイラーに求められる要件 • ソースコードのどこからどこまで(何行何列目)が 何かという情報がとれる • 文脈に応じてソースコードを生成したり、書き替え たりできる • リアルタイム処理を必要とするので、パフォーマン スも求められる
  209. 209. 実はこれまで • コンパイラー(パーサー)を2重開発してた • コンパイル用 • IDE用 • まして、サード パーティ製コード解析プラグイン も含めると、3重開発 • Java (EclipseとかIntelliJとか)でも • (去年ぐらいによく言われてたけども) 「Eclipseが対応するまでJava 8プレビュー試せない や」
  210. 210. おまけ: 他の環境(Clang) • Clangはその辺りをゴールの1つに掲げてる • IDEで使う前提 • リファクタリングに使いやすいデータ構造の構文木 • インクリメンタル コンパイルとかもしやすい
  211. 211. IDE連携まで考えると • 簡単そうに見える文法ですら、実装コストかな り高い • 2重開発はそれだけ大変 • だからIDEを信用しない/できない人も多い • IDE対応待ってられない/待ちたくない • .NET Compiler Platformはそれを解消 • C#に新機能を足しやすくなる • C# 6.0
  212. 212. C# 6.0 (予定)の例 public class Point(int x, int y) { public int X { get } = x; public int Y { get } = y; } Primary Constructor / Property Expressions while ((var line = stream.ReadLine()) != null) line ... if ((var x = obj as Point) != null) x ... Declaration Expressions immutableな型を作りやすく 「式」で書けることの幅が広がる
  213. 213. ポイント • 実行可能ファイルを作るだけがコンパイラーの 仕事じゃない • スクリプト実行 • コード解析 • 特に、IDE連携 • しかも、リアルタイム処理 • C#/VBコンパイラーの再設計 = .NET Compiler Platform このあたりまで考えると ちょっとした言語機能を 足すのも大変

×