メイン

.NET アーカイブ

2004年07月10日

INETA Japan リ ーダーズミーティング

INETA Japan リ ーダーズミーティング に出席。

小田急サザンタワーのマイクロソフト 本社 (新宿オフィス) へ。

内容は,

  • 活動報告
  • Tech・Ed 2004』のイベントについて
  • .NET 本について

等。

初参加で初対面の人だらけ.取り敢えず名刺交換。

2004年08月19日

Tech・Ed 2004 Yokohama

Tech・Ed 2004 Yokohama に参加予定 (9/7~10).

目的は,

  • .NET 2.0 の知識
  • Visual Studio 2005 のアジャイル関連の新機能の知識
  • エンタープライズ アプリケーション パターンの知識
  • INETA Japan 主宰のコミュニティ イベントへの参加

2004年09月07日

Tech Ed 2004 Yokohama 一日目

  • INETA Japan Events!「アジャイル開発ライブ」 打ち合わせ
  • MVP/INETA Japan Party + Hard Rock Cafe で二次会

2004年09月08日

Tech Ed 2004 Yokohama 二日目

  • セッション
  • INETA Japan Events!「アジャイル開発ライブ」 打ち合わせ
  • Tech・Ed & MSDN 10th Anniversary Party

2004年09月09日

Tech Ed 2004 Yokohama 三日目

  • セッション
  • INETA Japan Events!「アジャイル開発ライブ」
  • INETA Japan リーダーズ ミーティング
  • INETA Japan 打ち上げ+二次会

2004年09月10日

Tech Ed 2004 Yokohama 四日目

四日間に渡った "Tech Ed 2004 Yokohama" から、新幹線と北陸本線で夜中に帰って来た。ひんやりとした夜風に吹かれながら帰り道を歩いた。虫の声しか聞こえない。こっちはなんて静かなんだろうと思った。

今回は、なんと言っても「アジャイル開発ライブ!」に参加したのが、一番の体験だった。

INETA Japan の人達やその他多くの技術者の人と毎晩のように飲んだり、昔の同僚とばったり会って久々に昼食を共にしたり、NIFTY のフォーラムで以前よくお話をした ひどり さん と初対面したり、同じくNIFTY のフォーラムや XP祭り2002 でお話した επιστημη さんに STL.NET のお話を伺ったり、コミュニケーションが沢山とれた。

# いずれレポートを書きたい。

2004年09月29日

Tech・Ed 2004 参加レポート

2004-09-29-1.jpg

今更ながら、Tech・Ed 2004 Yokohama の参加レポートを書いてみた。

Tech・Ed 2004 Yokohama 参加レポート

参加直後は、なんだか慌ただしくてレポートを書く気にならなかったが、昨日漸く社内報告会をおこなったので、そのついでに書いた。

# ちなみに現在社内では C# + ASP.NET の研修を実施中である。
# 今日は、研修用に以下の本を買った。

ASP.NET+SQL Server ゼロからはじめるWebアプリケーション

2004年10月01日

こんな.NETはいやだ

【.NET】こんな.NETはいやだ

「『こんな .NET はいやだ』と思うものを自由にあげていくテスト。」

2004年10月15日

VB.NET JAPAN TOUR & INETA Japan リーダーズミーティング

VB.NET JAPAN TOUR と INETA Japan リーダーズ ミーティングに参加。
http://www.ineta.jp/activity/calender/

会場は、マイクロソフト株式会社 笹塚 NA オフィス。
vbtr1.jpg

VB.NET JAPAN TOUR では、実際に VB の開発をしているジョーさんとアマンダさんから最新の VB の情報を二時間半に渡ってお話頂いた。

○ 内容

・VB 2003
・Visual Basic Power Pack
・Visual Basic .NET Resource Kit
・Tablet PC
・Vusial Studio Tools for Office
・.NET Compact Framework
・VB 2005
ClickOnce、My クラス、Edit&Continue 等
・Q&A

○ お土産

・Net Compact Framework Guide (Pocket Reference (O'Reilly))
・Visual Basic .NET Resource Kit CD
・Microsoft Mobile DevCon 2004 Conference DVD
・VB.NET World Tour Tシャツ

ジョーさんもアマンダさんも笑顔が魅力的な方だった。
通常の講演と異なり、気軽に随時質問も O.K. とのこと。講演者との距離が近い感じで、リラックスして聴けた。

私は VB でなく C# を主に使っているが、今の仕事で役に立つ知識が結構得られた。来週にでも調査してみたいことが幾つか。
vbtr2.jpg

※ 通訳付き、軽食と飲み物付きだった。

その後、INETA Japan リーダーズ ミーティングは一時間程度。
主に、INETAJ の今後について話し合われた。

終了後は、新宿で飲み会 (一次会・二次会)。
INETA 参加者の方達と五名で。
楽しいお話が沢山聞けて、とてもおいしいお酒だった。
少し焼酎を飲みすぎ。

2004年11月03日

Microsoft Tech・Ed 2004 Yokohama 「Post Conference DVD」

Microsoft Tech・Ed 2004 Yokohama の 「Post Conference DVD」が送られてきた。

内容は、

  • 「Post Conference DVD」 4枚
    Learning Day や 基調講演、テクニカル セッション等の 映像 (Windows Media) や資料 (Power Point)
  • 「Visual Studio 2005 Beta 1 Refresh with Visual Studio 2005 Team System (英語版)」
  • 「SQL Server 2005 Beta 2」と「SQL Server 2005 Beta 2 ハンズ オン ラボ DVD」

Visual Studio 2005 Team System は楽しみにしていたもの。
早速個人的にも仕事上も評価して行きたい。

また、参加できなかったセッションについてもこれで内容を把握することができる。

続きを読む "Microsoft Tech・Ed 2004 Yokohama 「Post Conference DVD」" »

2004年11月04日

.NET/C# 研修

今日から、.NET/C# の社内研修を始めた。

ASP.NET 研修は、ずっと前から続行中だが、今度のは、Windows フォーム中心。
C++/C# での Windows 上での CAD の作り方の研修も数回実施。

自分の学習の方が全然進まない。
読みたい本もあるが、それより、もっとプログラミングを試したい気分。

今度、.NET Windows フォーム用のフレームワークと、その上にのる CAD のフレームワークのモデリング&実装をしてみたい。これはいずれにしても一度試す必要がある。
他にも .NET 上で実装を試したいものが幾つかある。開発用のツールとある種の ASP.NET アプリケーション。

2004年11月17日

Microsoft patterns & practices

mvc.gif

近年マイクロソフトが力を入れているらしいパターン関連の頁は、今後益々要チェックのようだ。

日本語の書籍も今月頭に発売になっている。

最近 ASP.NET の社内研修をやっているのだが、見ていると、或る程度 ASP.NET が判ってきて実用的なプログラムを書こうとしたときに、よく戸惑うのが、次のような点らしい。度々質問を受けた。

  • 「ページのクラスとデータに関連の強い部分のクラスが入り乱れてしまうのだけど、それぞれどのように作っていったらいいの?」
  • 「ページが沢山になったときに共通の処理をどこかに纏めたいのだけど、どうするのが普通?」
  • 「ページ遷移が複雑になったときに、ソースが汚くなるのだけれど、何か良い方法はないかしら?」

ASP.NET でアプリケーションを書き進めていくと、上のようなことが解決できず、直ぐにソースがぐちゃぐちゃになってしまう、と言うのだ。

そういう人には、先の Microsoft patterns & practices から、先ず以下を試してみるように言ってみる。

かなり手取り足取り説明してあるので、パターンに不案内な人にも分かりやすい。
また、テストのしやすさについても言及されているのが良いと思う (*1)。

後は追加で「『3 層分散』周辺は読んどいてね」とか、Singleton パターンくらいなら知っているという人には「『C# でのシングルトンの実装』が面白いよ」とか。
とにかく、効果が実感しやすそうなところから紹介していく。

この辺に少しずつ慣れていって、それから少しずつ、ドメイン モデルを作るべきかどうかや、ユーザー インタフェイスのテスト方法などについても考えていったら良いと思う。
そして、色々とパターンを試していく中で、プログラムの複雑さをどこにどんな風に吸収させていけば良いのか、そのコツを帰納的に体得していくのが良い。
パターンは教育ツールとしても強力なのだ。


(*1) これからは、デザイン パターンやアーキテクチャ パターンの記述のテンプレートに「テストのしやすさ」という項も追加するのが良いのではないだろうか。
パターンを適用することでどう設計が良くなるのかを言うのに、「テストがやりやすくなる」というのは判りやすい指標になると思う。

2004年12月05日

2004 Japan Community Open Day

2004 Japan Community Open Day に参加予定。

2004 Japan Community Open Day
日時 2004/12/18(土) 14:30-21:30
場所 マイクロソフト 新宿オフィス17階会議室
概要 コミュニティ活動や Visual Studio 2005 Team System などに関するワークショップ

沢山のワークショップが行われる。
Visual Studio 2005 Team System 関連のワークショップへの参加を希望した。

# 翌日は、INETA Japan のリーダーズ ミーティングに出席予定。

2004年12月20日

『2004 Japan Community Open Day』 & 『INETA Japan リーダーズ ミーティング』

INETA Japan の関連で上記二つのイベントに参加してきた。

sjkttntw.jpg
・両方の会場となった小田急サザンタワー

■ 12/18(土)

この日は、『2004 Japan Community Open Day』に参加。

2004 Japan Community Open Day
時間 14:30-21:30
場所 マイクロソフト 新宿オフィス17階会議室
概要 コミュニティ活動や Visual Studio 2005 Team System などに関するワークショップ

cmntyod.jpg
・Community Open Day の様子

  1. Visual Studio 2005 Team System を用いたアジャイル開発と手法
  2. Visual Studio 2005 Team System Genera

の二つのワークショップに参加した。
Team System について、無責任に言いたいことを沢山言ってしまった。やれ導入しづらいだの、やれ高すぎるだの。
MS の人は熱心に聴いてくださったが、果たしてフィードバックになっただろうか。

その後、中華レストランで懇親会。
INETA の皆さんや MVP の方々、MS の方々など、色々な人とお話をさせて頂いた。
# 途中「○×ゲーム」があったが、商品ゲットならず。

そして二次会。
小井土 さん、福井 さん、中西 さんとご一緒させていただいた。
レゴの話などで盛り上がった。

コミュニティ関連の飲み会でいつも感じることだが、こういう素敵な方達と自分が一緒にいる状況というのは、五年前の自分には想像することもできなかった。
コミュニティならではの体験だと思う。


その後、12時過ぎにホテルにチェックイン。
ジャグリングの練習をした (謎)。


■ 12/19(日)

翌日は、『INETA Japan リーダーズ ミーティング』に出席。

第8回 INETA Japan リーダーズミーティング
時間 10:30-14:00
場所 マイクロソフト 新宿オフィス17階会議室

帰りはPAPA'n VB の杉下 さんとたまたま飛行機が同じだったため、新宿~福井県丸岡町まで、話をしながら帰ってきた。
バスで帰る予定のところ、車で送っていただいた。
杉下 さんに感謝。

今回やっと羽田空港第二ターミナルを初利用。
haneda2t.jpg
・羽田空港第二ターミナル

続きを読む "『2004 Japan Community Open Day』 & 『INETA Japan リーダーズ ミーティング』" »

2005年01月13日

.NETで始めるデザインパターン

stateptn.jpg
楽しみにしていた 中西 さん の記事が @IT に載った。

私の偏見かも知れないが、.NET 開発者は、Java の人たちと比較して、オブジェクト指向だのデザインパターンだのに慣れていないような気がする。

だから、今回のような .NET 開発者向けの記事は、これからとても重要だ。

これから .NET 開発者の前には、テスティング フレームワークだとかリファクタリングだとかエンタープライズ パターンだとか DI (DependencyInjection) コンテナだとかアスペクト指向だとか、Java の人たちが取り組んできた様々な技術の可能性が待っている。
そしてこれらは、オブジェクト指向やパターン技術に関する知識を前提としているのだ。


ところで、中西さんは、ドットネッターでアジャイラーで TDD Player (謎) だ。
なので、デザインパターンの有用性を .NET 開発者に教えるのにもなんだかとてもアジャイルな感じだ。

これまでのデザインパターンの解説に、次のような言葉が使ってあるものがあっただろうか。

  • 「モチベーション指数」
  • 「ホワイト・ボードにクラス図」
  • 「NUnit用テスト・コードを記述してみよう」

例題も、HowMuchGreenbarLover メソッドだとか、HowHappy メソッドだとか。
福井 さん も書いてたが、とても中西さんらしい。

※ この記事は、Ichikawa さん のところでも紹介されている。


さて、今回の記事では、次のような問いに答えている。

問い: 「デザインパターン? 何それ? 何が良いの? .NET な我々にメリットあるの?」

でこれに関する有り勝ちな答え:

  1. 「デザインパターンというのは、こうこうこういうものだ」
  2. 「例えば、State パターンというのがあって、それはこういうもので・・・」
  3. 「使い方は、こんな感じ」
  4. 「VB.NET や C# でサンプル コードを書いてみると、こんな感じだよ」
  5. 「ねぇ、わかった? 良さそうでしょ。使ってみてよ」

これは、演繹的な説明だが、なんだかウォーターフォールな感じだ。
「うーん、どういうものかはなんとなく判ったけど… まあいいや、また今度改めて勉強するよ」
とか言われてしまいそうだ。
# 私経験有りますから!! 残念!!! 切腹!

で、中西 さん の答えは一味違う。少し真似してみるとこんな感じだろうか。

中西さん風の答え:

  1. 「例えば、VB.NET や C# でこんなコードがあるとするやん」
  2. 「ありがちなコードやけど、これってどうなんかなー 自分どう思う?」
  3. 「こんな風にしてみたらどうかなー」
  4. 「な? 自分知ってるやろ? ポリモーフィズム。どう、これ」
  5. 「これが State パターンや。で、今やったのがリファクタリング」
  6. 「な? 今度から State パターンやらいうたら、こういうのを指すんやで」
※ フィクションです。実在の中西さんとは関係ありません。


「デザインパターン23個覚えたぞ。さあ何かに役立てよう」でなくて、リファクタリングの結果として、使うべきところに使う分だけのデザインパターン。
こういきたいものだ。

さあ、今後の連載にも期待だ。

続きを読む ".NETで始めるデザインパターン" »

2005年01月19日

Developers Summit 2005 (デブサミ2005) ― VB.NET vs C# 『.NET 言語合戦』


デブサミ2005

デブサミで INETA Japan として以下をやります。
# 私は C# 側。
是非ふるってご参加ください。

Developers Summit 2005 (デブサミ2005 ― VB.NET vs C# 『.NET 言語合戦』)
日時 2005/02/03(木) 17:30~19:00
会場 青山ダイヤモンドホール B1F 会場 C
主催 株式会社 翔泳社
詳細 イベント告知サイト (Developers Summit 2005) にて


関連リンク:

2005年02月09日

デベロッパーズ サミット2005

上記に参加してきたので、レポートしてみたい。

■ 詳細

Developers Summit 2005
日時 2005/02/03(木)~04(金)
会場 青山ダイヤモンドホール
主催 株式会社 翔泳社
詳細 Developers Summit 2005


■ はじめに

デベロッパーズ サミットの開催期間には丁度、寒気団が日本上空に来ていた。
北陸地方は、今年一番の大雪で、出発前日の午前中には羽田行きが飛ばなかったこともあり、無事会場に行けるかどうかが先ず心配だった。

当日の朝は、五時半に自宅を出発した。道には新雪が積もり、ちょっと油断するとすぐスタックしてしまう。
高速道路もすっかり凍っている。凸凹の路をゆっくりと走った。

それでも小松空港には随分早くついた。INETAJ のイベントに一緒に出る杉下さんも、たまたま飛行機が同じで、同じ頃に空港にいらした。
心配した飛行機は定刻通りに出発し、定刻通りに羽田に到着。予定通りに会場につくことができた。ほっと一安心。


dvsm0502.jpg dvsm0508.jpg dvsm0503.jpg dvsm0501.jpg

さて会場に入ると、すごい人の行列。
二日間会場の青山ダイヤモンドホールは大変な人で、歩くのに苦労した。

デベロッパーズ サミットには、最初の年から毎年参加しているが、参加するたびに良くなっている。
特に今回は、楽しみなセッションが盛り沢山で、選ぶのに迷うほどだった。


ただ、昨年も感じたことだが、あの Web からの申し込みのユーザー インタフェイスはちょっと「いけてない」気がする。
あれは本当にユーザーの方を向いた UI と言えるのだろうか。
ユーザー側の関心事に対してではなく、主催者側の関心事に対して UI が設計されているように感じた。

  • 主催者側の関心事 : 各セッションに何人の人が参加しようとしているのか。どういう人が参加しようとしているのか。
  • ユーザー側の関心事: 自分がどのセッションを何時受けるか。申し込み/変更/キャンセルの方法。

今回の参加者、即ちデベロッパーにとっては、ユーザーの視点・ユーザーの関心事に合わせるのがデフォルトだから、多分違和感有りまくり。


でもまあ、その他の面では大満足。とっても良いイベントだった。
デベロッパーが中心というのが実に良い感じ。

このイベントのサブタイトルに「デベロッパーの復権」というのがある。
この趣旨には大賛成。
日本のデベロッパーも、もっと楽しく自信に満ちて物作りをして良い。


■ 参加内容

○ XP事例カタログ
大熊 知栄 氏、猪狩 錦光 氏、関 将俊 氏、小倉 唯克 氏

四年の歴史を誇る XPJUG (日本 XP ユーザグループ) で紹介されてきた XP プロジェクトの中から三つの例が紹介された。

すごい人で立ち見がでている。

大熊 さんの司会に始まり、三人の発表者が順に XP 事例の紹介を行った。

関 さん の発表は、XP 祭り 2004 で 関 さん が発表された内容「三年目の報告」(サブタイトル「XPが良いか悪いかなんて話は もうしないよ。」) だが、マイナー バージョンアップしていて「その後」が少し語られた。題して「3.5年目の報告」。「忍者式テスト」など、独自に XP をカスタマイズされている。
二人目の方はゲーム業界での XP。単純作業などのときに使う「ペアプロ解除」というプラクティスが興味深かった。

お二人とも開発者として、XP をやっていく中で現実的な解をいくつも見つけている。
また、回顧をよくやっていて、次にフィードバックしている。

今回特に良かったのは、顧客側からの XP プロジェクトの発表があったことだ。
以前見たことのあるバーンダウン チャートが顧客側視点で語られた。
これは結構目から鱗で、視点が変わると随分違って見えるものだと思った。


○ 失敗から学ぶプロジェクトマネジメント
伊藤 健太郎 氏

dvsm0504.jpg
XP だけでなく、プロジェクト マネジメントについても学ぶ必要があるだろう、ということでこのセッションを聴きにいった。

  • プロジェクトの失敗原因は? → プロジェクト マネジメントの進化のさせ方

という内容のセッション。
プロジェクトの失敗原因で、

  • プロジェクトを KKD (勘と経験と度胸) で実施

というのが印象的だった。

ソフトウェア開発の場合、QCD (Quality: 品質・Cost: コスト・Delivery: 納期) のマネジメントだけでは、中々プロジェクトの成功に結び付かない、とよく言われているようだが、プロジェクト マネジメントも進化しているようだ。

講演中、プロジェクト マネジメントによるプロジェクト成功のための様々なキーワードが使われたが、「いけてる」と思ったものを私の独断であげてみる。

  • いけてるキーワード:
    • コミュニケーション
    • モチベーション
    • キャリアパス
    • ナレッジ マネジメント
    • メンタリング

ところで、はじめの方で「是非このセッションが思い出に残るように」とのことで、「プロジェクトの失敗について隣の人と話してみよう」という時間があった。
アイス ブレーキングなんだろうが、これはちょっといけてない。余りにも唐突で中途半端な感じ。


○ .NETでアジャイル ペアプロ ライブ! ~VB.NETはテスト ファーストで行こう!
中西 庸文 氏、福井 厚 氏

dvsm0505.jpg

「ドットネッターでアジャイラーで TDDer (謎)」になるための方法について。

最近は、Java の開発者に比べてアジャイルに馴染みが薄い .NET の開発者の人たちにもアジャイルな開発を知ってもらいたいという趣旨の講演や記事が(ようや)く増えてきたようだ。
嬉しい限りだ。

さてこのセッションだが、なんと終始関西弁。
ペア プレゼンになっていて、会話形式で説明が進められていく。

圧巻は途中二回の「ペアプロ笑劇場」と題したペアプロ ライブ。
若手アジャイラーとアジャイル未経験なベテラン先輩開発者という設定で、テスト ファーストなペアプロを行う。
で、このペアプロが TDD で且つ TDD (謎) なのだ。

前者の TDD は、勿論テスト駆動開発 (Test Driven Development) のこと。アジャイルではお馴染みの手法だ。
後者の TDD (謎) は、ツッコミ駆動開発 (Tsukkomi Driven Development)。
テンポの良いボケとツッコミによって、開発が進められていった。このテンポは関西弁ならではかも。

※ ちなみにツッコミ駆動開発にもペアプロは必須。
※ 一人でぶつぶつとボケとツッコミをしながらプログラミングされるのはかなり嫌だ。

面白いのは、ペアプロなので、ドライバー役 (キーボードでプログラムを書く方: リアルタイム コード レビューア) とパートナー役 (ドライバーのコーディングを見ていてリアルタイムにフィードバックを行う) があるのだが、パートナーがボケてドライバーがツッコんでいたりする。


二回目の「ペアプロ笑劇場」では、Mock (擬似オブジェクト) によるテストも紹介され、技術的にも興味深かった。


このセッションを聴き終わって考えたこと:
これは私の経験則だが、アジャイルな人というのはアジャイルな講演をする。
見ていて感動が有る。
プレゼンテーションが濃いのだ。

薄いセッション → 濃いセッション → もっと濃いセッション
パワーポイントに文章を書いておいて、それを読みながら解説 → 実際にやってみせる → 参加者に体験してもらう
言葉で説明する → 図や写真で説明する → 寸劇で表現・動かして説明
抽象的な新しいアイディアを述べる → 具体的な例を交えて新しいアイディアを述べる → 新しいアイディアを試してもらう

コミュニケーションの帯域が違うのだ。時間当たりに伝わる情報量が違う。
これは、沢山話して言葉数を増やす、ということではない。沢山のパワーポイントを用意する、ということでもない。

発信側でなく受信側の情報量を増やすのだ。
新しいアイディアというものは、いくら言葉数を増やしたって伝わらないものは伝わらない。
見せる工夫、伝える工夫をしなければ。

私はこれを Broadband Communication と呼びたい。

brdbndcm.gif

仕事でも多分同じだ。

例えば、

  • 「プロジェクトの失敗の主な要因はコミュニケーション エラー」

という問題提起があるとする。先のプロジェクト マネジメントのセッションでもこれはあげられていた。

でそれに対する解。プロジェクト マネジメントのセッションでは、

  • 「コミュニケーションは『お仕事』なんだからしっかりやらなくちゃ」

ということだった。

でも「プロジェクトの失敗の主な要因はコミュニケーション エラー」という問題提起に対して、しっかりやるべきなのは自明であって、それだけでは不十分なのだ。
そこには「見える化」など実践するための工夫がなければならない。アジャイルな人たちはそこが上手だと思う。


○ INETA Japan Presents VB.NET vs C# 『.NET 言語合戦』
河端 善博 氏、東海林 秀晃 氏、小野 修司 氏、石野 光仁 氏、菊池 和彦 氏、小島 富治雄 氏、福王寺 聡明 氏、杉下 朋年 氏、中西 庸文 氏、片岡 真二 氏、樋口 忠洋 氏、Hollytown 氏

Visual Studio .NET 2003 になってからは、VB.NET と C# はそれほど使い勝手に違いがなくなりつつある。2005 では更に優劣がなくなる。
その中で、VB.NET と C# のそれぞれの長所をあげて、双方の使いどころについて考えてみよう、という趣旨のイベント。
別にどっちを使っている方が偉いかを競う訳ではない。

INETA Japanは、.NET のコミュニティのコミュニティだ。
私は、パネラーの一人として参加。C# 側。

ライブで VB.NET と C# のそれぞれで七並べの戦略部分をプログラミングし、その場で対決した。
お客さんは勝つと思う方に投票し、勝負の結果により、アマゾン ギフト券や PSP などが当たる。全員に参加賞もあたる。

パネル ディスカッションは、どうやら C# 側が劣勢な(まま)終わってしまった。折角 C# 側のパネル リーダーの小野 さん が頑張ってくれてたのに。残念。
C# を応援してくれてた参加者の方はさぞやきもきしたことだろう。
というか、VB.NET 側のパネル リーダーの杉下 さん のプレゼンが良過ぎた。

反対にプログラミング ライブの結果の七並べの対戦では、C# 側が終始優勢だった。

商品の効果かも知れないが、全体としては結構ほんわかと盛り上がっていたようで、コミュニティ色が出ていて良かったのではないだろうか。


○ My Framework作成の勧め:アプリケーションを30個作る時に何を用意するか
arton 氏

或る業務に関して、沢山アプリケーションを書くのであれば、その業務に特化した良いマイ フレームワークを自分で書いた方が良い、というお話とその作り方のお話。

arton さん のお話は、理論的で且つとても判り易い。

  • フレームワークって何のことだったのか。
  • 作ると何がうれしいのか。
  • 良いフレームワークってどういうものか。
  • そして .NET 上で作る方法は。

arton さん の独特の語り口は、とても説得力がある。

フレームワークの作り方の話は特に技術的に興味深かった。

パラメータ、プッシュ モデル (Tell) /プル モデル (Ask)、依存性注入 (Dependency Injection) などの実際の実装方法を、C# のソースをデバッグ実行しながらデモで見せてくれた。

リフレクションで仮引数名で検索してみせたり、実行時にコンパイラを呼び出してパラメータを評価させたり、自作の DI コンテナを使って依存性注入を実際にやってみせたり、ととても楽しめた。

※ ちなみに、このソース コードは公開されている。


○ フレームワークの効能と、.NET導入事例紹介
三部 雅法 氏

.NET Framework 上でのフレームワークの紹介とその導入事例の紹介。
自社製フレームワークの説明という感じ。


○ セッション参加者のパーティー (懇親会)

dvsm0507.jpg dvsm0506.jpg

一日目の夜は会場でパーティーがあった。
デベロッパーズ サミットの場合は、他のイベントと比較して広い分野から参加者が集まっている。
で、層としては技術者が中心。
なんか縦割りでなく横割な感じで新鮮だった。

例えば、ドットネッターとアジャイラーは日頃それ程イベントでかぶらない。
今回はあちこちでドットネッターとアジャイラーが名刺交換する風景が見られた。

私も、多彩な人とお話ができて実に楽しかった。


○ 二次会

懇親会を途中で抜け出して、ドットネットな方々 (INETAJ・MVP) と原宿辺りで二次会。


○ その他

二日目の午前中は、INETAJ のリーダーズ ミーティングに参加した。
マイクロソフト 新宿オフィス。
その後 INETAJ の方々と食事会。


■ 人とのつながり

今回も多くの方々と話すことができた。
イベント参加の一番の収穫。

  • 今回デブサミで初めて直接ご挨拶できた方々 (50音順):
    小野 さん、菊池 さん、国広 さん、関 さん、田中 さん、原 さん、樋口 さん…
  • デブサミで再会できた方々 (50音順):
    arton さん、天野 さん、石野 さん、市川 さん、岩切さん、牛尾 さん、大熊 さん、太田 さん、小野 さん、沖田 さん、角谷 さん、懸田 さん、片岡 さん、河端 さん、倉貫 さん、小井土 さん、児玉 さん、佐藤 さん、渋木 さん、東海林 さん、杉下 さん、中西 さん、平澤 さん、平鍋 さん、福井 さん、福王寺 さん、松本 さん、水越 さん、堀田 さん、安井 さん、吉原 さん、和田 さん…
  • 同じデブサミ会場にいらしたのに再会できなかった方々 (50音順):
    北野 さん、本間 さん、森屋 さん、和田 さん…

※ お会いしたのにお名前のもれてる方、すみません。ご指摘頂けると幸いです。


■ 関連リンク:

2005年02月10日

C# vs. VB.NET

2/3 に行われた Developers Summit 2005 ― INETA Japan Presents VB.NET vs C# 『.NET 言語合戦』 のために集めたネタ (とイベント中で使われたネタ)。

2005年02月15日

C# Tips: interface を 抽象クラス (abstract class) とどう使い分けるか

# 久々に技術ネタを書いてみる。 # と言っても、某掲示板で使ったネタの使い回し。

csharp.gif

C++ にはなかった新しいキーワードとして、C# では interface というものが出てくる。 interface は、Java ではおなじみのキーワードだ。
例.

interface ICloneable
{
    object Clone();
}
interface では公開されているメソッドとプロパティの外見 (名前、パラメータ、戻り値) だけが宣言されていて、実装部分が定義されていない。実装部分は、その interface を実装するクラスによって定義される。
例.

class Employee : ICloneable
{
    private string name;

    public string Name
    {

        get { return name; }
        set { name = value; }
    }

    public Employee(string name)
    { Name = name; }

    public object Clone()
    {
        return new Employee(Name);
    }
}
interface は 抽象クラス (abstract class) と機能的には似ている。 抽象クラスも、中身のないメソッド (abstract method) の宣言を持つことができ、そこから派生したクラスで、そのメソッドの実装を定義する。
例.

abstract class Person
{
    private string name;

    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    public Person(string name)
    { Name = name; }

	public abstract void DoSomething();
}

class Employee : Person
{
    public Employee(string name) : base(name)
    {}

	public override void DoSomething()
    { /* Do good job. */ }
}
機能上の大きな違いとしては、
    • 抽象クラスは、実装を持つこともできる。上記の例でいうと、name や Name や Person(string name)。
    • interface は、実装が持てない。
    • 抽象クラスは、二つ以上継承できない。つまり、多重継承できない。
    • interface は、二つ以上でも継承できる。
というものがある。 しかし、機能上の違いでなく、使い分けるときの一般的な指針はないだろうか。 デザインパターンの一つである、State/Strategy パターンの場合で考えてみよう。 先ずは、抽象クラスを使った場合と、interface を使った場合の両方について書いてみる。
・抽象クラスを使った場合の例: statebyac.png

class Context
{
    State state = null;

    public State State
    {
        set { state = value; }
    }

    public void DoSomething()
    {
        if (state != null)
            state.DoSomething();
    }
}

abstract class State
{
    public abstract void DoSomething();
}

class ConcreteState1 : State
{
    public override void DoSomething()
    { /* 省略 */ }
}

class ConcreteState2 : State
{
    public override void DoSomething()
    { /* 省略 */ }
}
・interface を使った場合の例: statebyit.png

class Context
{
    State state = null;

    public State State
    {
        set { state = value; }
    }

    public void DoSomething()
    {
        if (state != null)
            state.DoSomething();
    }
}

interface State
{
    void DoSomething();
}

class ConcreteState1 : State
{
    public void DoSomething()
    { /* 省略 */ }
}

class ConcreteState2 : State
{
    public void DoSomething()
    { /* 省略 */ }
}
さて、State/Strategy パターンを使う場合、はたしてどちらが標準的なのだろうか。 「State/Strategy パターンを構成している部分だけ」を見ると、或る振る舞いに関する制約が付けられれば十分なので、interface が適しているような気がする。 但し、実際の設計では、
「State/Strategy パターン」を使おう → じゃ interface を使おう
とはならない。 それは、通常 State/Strategy パターンが適用される場合というのは、先ず (リファクタリングなりの結果としての) クラス設計があって、そこにおける抽象化すべきものがクラスなのか、振る舞いに関する制約なのかの判断があり、結果として「State/Strategy パターン」の形になるだけであるからだ。 つまり、抽象化したいのがクラスであれば抽象クラス、単なる振る舞いに関する制約であれば interface、という感じで使い分けることになる。 それから、もう一つ。 実装上の問題として、State/Strategy パターンなどが対象としている関心事が、既存のクラス階層と直交する関心事であった場合は、抽象クラスでなく interface を使う。多重継承ができないからだ。 例えば、始めの方の例でいうと、
umlpe.png
等の継承によって形成される継承ツリー或るいはその他の継承ツリーが対象としている関心事と、ICloneable が対象としている関心事は直交している。 従って、ICloneable の方の関心事の実装には interface を使う。 これは、「アスペクトの実装を便宜上 (言語の都合上) interface で行う」というイディオムといえるかと思う。 尚、余談になるが、Strategy パターンの場合は、抽象クラスも interface も使わずに delegate を使う、というイディオムも有ると思う。
例.

class Context
{
    public delegate void DoSomethingMethod();

    DoSomethingMethod doSomething = null;

    public DoSomethingMethod OnDoSomething
    {
        set { doSomething = value; }
    }

    public void DoSomething()
    {
        if (doSomething != null)
            doSomething();
    }
}

続きは「[C#] Tips: interface と partial class で横断的関心事を分離」。

用語解説:
  • インタフェイス (interface):
    広義では、境界。或る纏(まと)まりと別の或る纏まりが接する点のこと。ソフトウェア開発では、或るモジュールが別のモジュールに公開している機能の利用方法を記述したもののセット。オブジェクト指向においては、特に、それを実装するクラスに必要なメソッドやプロパティ、イベントのセットを定義したもの。
  • ソフトウェア パターン (software patterns)
    ソフトウェア開発のためのパターン。ソフトウェアを設計する際に繰り返し現れる経験的な要素を抽出したもので、効率の良いプログラミングをするためのテンプレート。「デザイン パターン」、「アーキテクチャパターン」、「アナリシス パターン」、「アンチ パターン」、「イディオム」等の種類がある。
  • デザイン パターン (design patterns)
    ソフトウェア パターンの一種で、OOD (オブジェクト指向設計) において、過去のソフトウェア設計者が発見し、編み出した設計ノウハウを蓄積し、名前をつけ、再利用しやすいように特定の規約に従ってカタログ化したもの。
  • イディオム (idiom)
    プログラミング言語やプラットフォームなど、実装レベルの問題に対するソフトウェア パターン。
  • 関心事 (concerns)
    ソフトウェアを構成する様々な要素のうち個別に着目することができひとまとめに扱うことのできる何か。
  • 横断的関心事 (Crosscutting Concerns)
    分割されたモジュールをまたがる関心事。例えば、「オブジェクト指向のパラダイムによって関心事の分離がなされたモジュール」間にまたがる関心事。
  • 関心事の分離 (SOC:Separation of Concerns)
    関心事を分離すること。関心事によってプログラムをモジュール分割すること。
  • アスペクト (aspect)
    様々な視点からみた際に、関心事 (特に横断的関心事) として分離されるべきソフトウェアの持つ側面。

C# Tips: 継承

inheri.gif

今まで新人向けのオブジェクト指向の研修で、「継承」というのは、


    class Sub : Super
    {
    }

こういうのだ、とか説明してきたんだけど…

ごめん。この説明はちょっとごまかしなんだ。
説明が面倒だったから…

本当は、これは C# 言語の「派生」なんだ。オブジェクト指向でいう継承と同義じゃない。
これは、「『派生』という機能を使って継承を実装する」っていう C# のイディオムに過ぎないんだ。


他にも、オブジェクト指向の用語を C# にマッピングして説明するときに結構ごまかしてるんだ。


    Super  s = new Super();

オブジェクト指向っぽくこれを表現してみると、
「Super というクラスに new というメッセージを送ると s というインスタンスが作られた」
という感じかな。


    s.Foo();

これも、「s という名前のインスタンスに Foo というメッセージを送ると s はそれに応じた振る舞いを行う」という感じに表現できる。


でも、実は C# 的には、s はインスタンスじゃない。参照変数に過ぎない。インスタンスを参照してるだけ。インスタンスは別に在る。

これも、「クラスのインスタンスを参照変数で表現」っていう C# でオブジェクト指向をやる場合のイディオムを前提に説明しちゃってる。

用語の曖昧さを使って誤魔化していたんだ。説明すると長くなっちゃうからね。
本当は、どのレイヤの話をしているのか、明示すべきなんだ。

ごめんよ。

2005年03月08日

.NET Tips: VC++ から来た人向け

tips.gif
■ VC++ から来た人向けの資料

VC++ でウィンドウズ アプリケーションを書いていた人が、.NET 開発を始めるときに重宝するドキュメント。
※ 特に C + Win32API で書いてた人向け

2005年03月09日

.NET Tips: C#で XML コメントからヘルプを作成

tips.gif
■ Visual Studio の HTML ヘルプ作成機能

C# では XML コメントを書くことで、HTML ヘルプをコードから自動生成することが出来る。
※ 尚、VB.NET では次期バージョン (2005) から、この機能に対応する。

Visual Studio では、メニューから「Web ページのビルド コメント」を選ぶことで、コードの XML コメントからHTML ヘルプ作られる。

但し、この機能で作られた HTML ヘルプは、Windows XP SP2では見ることができない。

参考:


■ NDoc の紹介

"NDoc" というツールを使うことで、MSDN のような HTMLヘルプ形式 (.chm ファイル) を含む様々なヘルプ形式を出力できる。
※ プロジェクトのプロパティで「XML ドキュメント ファイル」を指定しておく必要がある。

NDoc の使い方はこちらを参考に。

2005年03月10日

.NET Tips: 名前付けのガイドライン

tips.gif
■ .NET 開発における名前付けのガイドライン

.NET 開発でクラス名や変数名などのシンボル名を付けるときのガイドライン。

参考:

  • C#コーディング標準 (by 河端 善博 氏)
  • VB.NET コーディング標準 (by 中西 庸文 氏)
オブジェクト倶楽部 ― コーディング規約の会 にあります。

2005年03月14日

.NET Tips: .NET 開発用のツール

tips.gif
■ .NET 開発用ツール

.NET 開発用の様々なツールが存在している。
※ 森屋 英治 氏 により N* (エヌアスタ) と命名されている。

  • NWiki
    中西 庸文 氏 により、.NET 開発のためのツールが紹介されている
  • SharpToolbox (英語)
    .NET 開発のためのツールがとても沢山紹介されている
  • Open Source Software in C# (英語)
    こちらは開発用ツールも含む、C# で書かれたオープン ソースのソフトウェアが沢山紹介されている

2005年03月15日

.NET Tips: ASP.NET Web アプリケーションの設計パターン

tips.gif
■ ASP.NET Web アプリケーションの設計パターン

ASP.NET でアプリケーションを作っていく場合、よく入門書などにあるように、Visual Studio 上、でコントロールやデータ クラスをドラッグ&ドロップで作っていくと、

  • 「ページ = クラス」
  • データに関する処理は、各クラスに分散
  • 頁遷移に関する処理も各クラス (= 各ページ) に分散
  • UI に関する処理とデータに関する処理とロジックが混在

となり、或る程度以上の規模のアプリケーションでは、複雑なコードとなることが多い。

そのような場合にどのように設計を行うのが良いか。その代表的な設計のパターンを幾つか紹介する。


参考:

参考書籍:

2005年03月16日

.NET Tips: セキュリティ ― ASP.NET での 危険な HTML の入力を防ぐ

tips.gif
■ Web アプリケーションにおける 危険な HTML の入力

先ず Web アプリケーション一般における、危険な HTML の入力とはどのようなものか、以下を参照のこと。

■ ASP.NET での対策

今の (.NET Framework 1.1 以降の) ASP.NET では、デフォルトでは、タグ入りの入力などは、自動でチェックされ、危険なリクエストを受けると例外がスローされる。
これにより、アプリケーションがサニタイズを怠った場合でも、最低限のセキュリティ対策はとられることになる。

これをオフにするにし、アプリケーション側でサニタイズを行う場合には、

Web.config で、

のようにする。

※ 参照:

アプリケーション側で HTML サニタイズを行うには、以下のメソッドが便利。

データバインド時に使う場合はこんな感じ:

<%# HttpUtility.HtmlEncode(DataBinder.Eval(Container, "XXXXX").ToString()) %>

※ 関連サイト:

2005年03月17日

.NET Tips: セキュリティ ― Web アプリケーションにおける SQL インジェクション対策

tips.gif
■ Web アプリケーション一般における、 SQL インジェクションの脅威

先ず Web アプリケーション一般における、 SQL インジェクションの脅威とはどのようなものか、以下を参照のこと。

■ .NET の Web アプリケーションにおける対策

対策としては以下を参照:

2005年03月22日

C#がJISに制定

csharp.gif
C#言語がJIS (Japanese Industrial Standard: 日本工業規格) に制定された。JIS X 3015。

C# 好きの私としては喜ばしい限り。

2005年04月15日

Visual Studio 2005 Beta2 リリース間近?

昨年の夏前に Beta を見てその強力な機能強化に魅入られて以来、待ち遠しい Visual Studio 2005 だが、果たして何時頃出るのだろうか。

先ずは近々出る予定の Beta2 に期待が掛かる。
下の記事を見る限り、4/25 (3/56) 辺りに Beta2 が出るようだ。
ゴールデン ウィーク前後には試せそうか。

続きを読む "Visual Studio 2005 Beta2 リリース間近?" »

2005年04月18日

INETA Japan 総会

05-04-17-17.jpg
4/17(日)は、新宿で INETA Japan の総会に出席。

小井土 さん と私がジャグリングのボールを持ってきていて、休憩時間にジャグリングの啓蒙活動を行った。
# N* (エヌアスタ) の一つに NJuggler というのも追加されることになった (嘘)。

終了後は、飲み会へ。
沖縄そばの『ヤンバル』など、三軒ばかり梯子。
# 日曜だというのに、新宿には居酒屋の客引きが沢山居て ぺがらぼ さん と一緒に吃驚。

INETA の方々と .NET 系のお話ばかり延々とできて、嬉しかった。

いやしかし。皆さん、本当によく勉強されてる。
セミナーに参加したり、本を読んだり、英語勉強したり。
もうお会いするたびにどんどん知識が増えていってるし。
素晴らしいことだ。

改めてすごい人たちとご一緒させて頂いてるんだな、ということを実感した。

2005年04月27日

NAgileで行こう!

NAgile を推進されている afukui さん が新しいサイトをオープンされた。

NAgile (エヌ・アジャイル) というのは、.NET でアジャイル開発を行うこと。
NUnit をはじめとした .NET 用のアジャイル開発ツール (N*: エヌ・アスタと呼ばれている) を使う。

.NET 開発者にも、是非アジャイル手法が広まっていってほしいと願っている。
※ ちなみに、.NET でアジャイル開発を行う人のことは、NAgiler と呼んでいる。


その他の NAgile のサイト:

導入記事はこちら:

Visual Studio 2005 Team Suite Beta 2 日本語版

待ちに待った Visual Studio 2005 のBeta 2 日本語版が出た。

先程インストール完了。

これから色々試してみるつもり。
わくわくする。

2005年04月28日

C#がJISに制定

csharp.gif
以前書いた内容への補足。

C# の JIS 規格は、以下で閲覧できるようだ。

上記ページの「データベース検索」―「JIS検索」―「JIS規格番号からJISを検索」で「X3015」を検索すると、PDF形式で見られる。

# 但し閲覧のみ。

ちなみに C++ の規格は「X3014」で閲覧できる。

2005年05月23日

『MD3 (エムディーキューブ)』

以下のイベントに参加予定。
大変に濃いイベントとのこと。去年は仕事の都合で参加出来なかった。
興味の有る講演が目白押し。楽しみだ。

『MD3 (エムディーキューブ)』
マイクロソフト社のエバンジェリストやアーキテクトが技術者を対象に広範囲で奥の深い技術情報をお届けするイベント
昨年「Microsoft Developer Deep Dive(MD3)」として開催されたもの
参考: aspxの日記(ASP++ブログ) - MD3(エムディーキューブ)が気になる。
主催 マイクロソフト社
開催日時 Day1 2005年6月16日(木) 13:00-17:45 (受付開始 12:30-)
※終了後にパーティを開催予定
Day2 2005年6月17日(金) 11:00-17:45 (受付開始 10:30-)
開催場所 野村コンファレンスプラザ
参加費 無料
お申込方法 クローズドなイベントで一般向けには参加者を募集していないのですけれど、INETA Japan 参加コミュニティのメンバー向けに参加募集しても良いそうなので、ここを見ている方でご希望の方は、5月30日までにメールをください

2005年05月30日

『NAgileのご紹介』のご紹介

NAgile ネタ。

日本XPユーザグループ (XPJUG)第13回ユーザー会で、NAgiler の福井さんが『NAgileのご紹介』というネタ (『ネタ』という表現でいいのだろうか) を発表されたそうだ。

資料 (PDF) が、ここで公開されている。とても楽しい内容だ。
http://plone.xpjug.org/event/20050526meeting/NAgile_intro.pdf/file_view

2005年06月08日

Visual Studio 2005 の正式リリース日

・MSDN ― Launch 2005 での発表 (英語)
<http://msdn.microsoft.com/launch2005/> より

Welcome to the Visual Studio 2005, SQL Server 2005, and BizTalk Server 2006 launch portal. On this site you'll find links to technical content designed to help you learn about the new products. In addition, this site serves as the home for our online launch day activities. Launch is the week of November 7, 2005...and on launch day we’ll have:

・米MSがSQL Server、Visual Studio、BizTalk Serverの出荷日を正式発表
MYCOM PC WEB <http://pcweb.mycom.co.jp/news/2005/06/08/100.html> より

米Microsoftは6月7日(現地時間)、現在米フロリダ州オーランドで開催されている同社主催の開発者会議「Tech・Ed 2005」において、データベースの「SQL Server 2005」、開発ツールスイートの「Visual Studio 2005」、Webサービス構築や異なるアプリケーション間の連携を行う「BizTalk Server 2006」の3製品の出荷日が、11月7日になると正式に発表した。また同日、SQL Server 2005向けの開発者プレビュー版(June CTP)が提供されたことも発表した。同CTP版はMicrosoftの SQL Serverサイト <http://www.microsoft.com/sql/downloads/>からダウンロード可能だ。

・米Microsoft,「SQL Server 2005」「Visual Studio 2005」「BizTalk Server 2006」は11月第2週リリース
IT Pro <http://itpro.nikkeibp.co.jp/free/ITPro/USNEWS/20050608/162309/> より

米Microsoftは,データベース・ソフトウエア「SQL Server 2005」,アプリケーション開発環境「Visual Studio 2005」,電子商取引向けサーバー・ソフトウエア「BizTalk Server 2006」の最終版を11月の第2週にリリースする。Microsoft社が米国時間6月7日に明らかにしたもの。

・SQL Server新版、11月に正式リリース
IT Media エンタープライズ <http://www.itmedia.co.jp/enterprise/articles/0506/08/news008.html> より

米Microsoftはフロリダ州で開催のTech・Ed 2005で6月7日、「SQL Server 2005」「Visual Studio 2005」「BizTalk Server 2006」を11月7日の週に正式リリースすると発表した。


英語版の話です。
日本語版は未定です。
※ 英語版より早いことはないでしょう。多分。

2005年06月11日

Microsoft Tech・Ed 2005 Yokohama

以下に参加予定。

Microsoft Tech・Ed 2005 Yokohama
日時 2005年8月2日(火)~5日(金)
場所 パシフィコ横浜

INETA Japan の方で以下の参加費用が無料になる招待枠があるそうです。

参加費用: 一般通常参加費用 4日間の場合で ¥125,000円 (7月8日までの早期申込ので4日間の場合 ¥87,000円)

この無料招待を希望される方は、6月13日(月)までにメールください。

2005年06月20日

MD3

MD3 (エムディーキューブ: Microsoft Developers Deep Dive) に参加してきた。

『MD3 (エムディーキューブ)』
マイクロソフト社のエバンジェリストやアーキテクトが技術者を対象に Deep な技術情報を話すクローズド イベント
主催 マイクロソフト社
開催日時 2005/06/16・17 ※一日目終了後にパーティ
開催場所 野村コンファレンスプラザ
参加費 無料

会場は新宿野村ビルの上から三つ目の階。






md3-02.jpg md3-05.jpg

会場の雰囲気は下のような感じ。







md3-03.jpg md3-04.jpg md3-06.jpg

流石に深い内容のセッションが多かった。
これを聴けただけでも来た甲斐があった、と思えるセッションも一つあった。
また、Tech・Ed などでは聴けないようなセッションもあって楽しめた。

一日目終了後のパーティでは、福井さん、萩原さん、一色さん、市川さんや、他にも多くの方たちと色々な話をさせて頂いた。
二次会では、小井土さん、福井さん、市川さん、小野さん、森屋さんと。
そして、三次会では、小井戸さんと MD3 のスピーカーの MS のエバンジェリストの方たちと。

なんとも感動的なメンバーだ。
こういう素晴らしい方たちとお酒を飲みながら、技術の話を延々と、というのは、技術者として本当に幸せを感じるひと時である。
本当になんて楽しそうに技術について語る人たちなんだろう!

md3-07.jpg

二日目終了後、小井土さんがジャグリングのリング (!) を持っていらしてたので、トイレ脇の目立たないところでちょっと触らせてもらった。
私は偶然 (謎) ジャグリングのボールを持っていたので、小井土さんと福井さんと私とでジャグリングとアジャグラーという言葉を MS のエバンジェリストの方々に少しだけ披露。萩原さんはじめ MS の方々にも結構面白がってもらえたようだ (多分)。

# その後は INETA の方々と河豚会へ。

続きを読む "MD3" »

INETA リーダーズミーティング

6/18 (土) は、INETA リーダーズミーティングに参加。
inetamt.jpg


# これにて三日連続の新宿滞在も終了。

2005年07月05日

『C#で学ぶオブジェクト指向プログラミング 』ブックレビュー

C#で学ぶオブジェクト指向プログラミング』という本のブックレビューを書きました。

Tech・Ed 2005 Yokohama ― Welcome to INETA Japan Event 2005!

Tech・Ed Yokohama において、INETA Japan は、昨年 (*1) に引き続き今年も セッションをやります。

(*1) 昨年のイベント:

Micosoft MVP

Micosoft MVP (Most Valuable Professional) Visual Developer - Visual C# のアワードを受けることになりました。
C# 大好きな私としては、大変光栄でありがたいことだと感じています。

関係者の皆様ありがとうございます。そして、今後とも宜しくお願い致します。


※ 同時に受賞した INETA Japan のリーダーの方々。

2005年07月25日

Tech・Ed 2005 Yokohama INETAイベント『デスマーチからの脱出 ~徹夜からの帰還~』

Tech・Ed 2005 Yokohama ― Welcome to INETA Japan Event 2005! の続き

Tech・Ed 2005 Yokohama で、INETA Japanイベント『デスマーチからの脱出 ~徹夜からの帰還~』 (8/4 18:30~20:00) をやります。

INETA Japan ― Tech・Ed 2005 Yokohama スペシャルプログラム「Welcome to INETA Japan Event 2005!」の特設頁

豪華賞品が当たる抽選会 (*) がありますので、是非奮ってご参加ください。
(*) INETA関係者 (リーダー、代理者、INETAJ枠参加者) とTechEd 運営側の方は対象外

  • 抽選会の抽選券はINETAコミュニティメンバーの名刺と引き換え
  • 名刺は会場内でINETAウィンドブレーカーを来たメンバーと名刺交換するかスタディホールで担当している人と名刺交換で入手
  • メンバーは20人近くいるので、全員分集めれば当選確率もアップ
  • 賞品は一人一つまで
    賞品リスト
    • マイクロソフト賞
      • NETエンタープライズWebアプリケーション開発技術大全 全巻
      • Microsoft Optical Desktop Elite×2
    • cbook24賞
      • 書籍「コンピュータの名著・古典 100冊」
      • 書籍「C言語による最新アルゴリズム事典」
      • 書籍「プログラミングの心理学 25周年記念版」
      • ソフト「大人のための右脳キッズ 記憶力編」
      • ソフト「声に出して覚える音読英会話 入門編」
    • INETA Japan賞
      • Amazoneギフト券3万円分
私も、このイベントでウィンドブレーカーを着て会場をうろついたり、スタディホールやセッションに出たりしておりますので、宜しくお願いします。

2005年08月09日

Tech・Ed 2005 Yokohama で考えたこと ― ソフトウェア開発の問題点の解決とエンジニアの幸せについて

Tech・Ed 2005 Yokohama で考えたこと ― ソフトウェア開発の問題点の解決とエンジニアの幸せについて。

.NET や Visual Studio、Team System など、ツールの進化は素晴らしい。かなり良くなって来ている。
効率的に開発が行えるようになってきた。
また、.NET の世界でも AOP や DI コンテナ、Generics などが新しいパラダイムとともに、使われていくようになりつつあるようだ。
一方で、.NET にアジャイル開発を積極的に取り入れようという動きもある。

そんなふうに技術が変化していく中、エンジニアとしてどのように幸せになっていったら良いのか、考えた。

※ 下記マインドマップは、Jude Professional 2.3 で作成。クリックすると大きいマインドマップがポップアップ。


工学的に解決を図ろうとするのとアジャイルに解決しようとするのは、態度の違い。
どちらの態度も必要。

「或る現場で工学的手法がうまくいかなかった → 工学の所為」とは言えない。単にその現場のやり方が「実は工学的にまずかった」だけかも。
また、たとえ或る問題が現状の工学で解決できないからといって、工学的解決をあきらめるべきではない。
但し、工学の守備範囲外の人間系の問題が現場でボトルネックとなっている場合も多い筈。そうした現場では、工学的に解決は図れない。
反面、再利用性など工学的手法が必要な面もある。


どうも、一人で考えても拙い結論にしか到らないようだ。
ツッコミ募集中。

2005年08月22日

Tech・Ed 2005 Yokohama 参加レポート

※ 「Tech・Ed 2005 Yokohama で考えたこと ― ソフトウェア開発の問題点の解決とエンジニアの幸せについて」の続き。

忘れないうちに、Tech・Ed 2005 Yokohama に関することをメモ。


往路

2005/08/02(火) 出発
6:30 自宅発
えちぜん鉄道 → JR北陸本線 → JR新幹線 → JR横浜線 → みなとみらい線


宿泊場所

横浜平和プラザホテル


参加内容

Microsoft Tech・Ed 2005 Yokohama
期間 2005/08/02(火) - 05(金)
場所 パシフィコ横浜
(横浜市西区みなとみらい)

8月2日(火)
時刻 Room Session ID Title Speaker
13:20 - 14:35
MVPラウンジ
  MVP Roundtable 「MVPプログラムについて」  
14:55 - 16:10
D
L10-1
Customer Voice : Enterprise Library の解説 Part I
マイクロソフト株式会社 荒井 省三、INETA Japan 市川 龍太
 
16:30 - 17:45
D
L10-2
Customer Voice : Enterprise Library の解説 Part II
マイクロソフト株式会社 荒井 省三、INETA Japan 市川 龍太
18:30 - 20:00
 横浜 グランド インターコンチネンタル ホテル 3 階 インターコンチネンタルボールルーム
 
コミュニティパーティ 2005
 
串焼・焼鳥 たる平
 
NAgile 打ち合わせ
 
 
 
二次会
 


8月3日(水)
時刻 Room Session ID Title Speaker
10:50 - 12:05
A
T1-302
.NET におけるアプリケーション アーキテクチャ構築
マイクロソフト株式会社 近藤 和彦
13:20 - 14:35
B
T3-326
Microsoft Visual C# 2005 詳細
NRI ラーニング ネットワーク株式会社 矢嶋 聡
14:35 - 14:55
スタディーホール
 
INETA スタディーホール
 
14:55 - 16:10
B
T3-335
スマート クライアント アプリケーションの開発
マイクロソフト株式会社 中原 幹雄
16:10 - 16:30
スタディーホール
 
INETA ビッグセッション ― リーダーを探せ ― ウィンドブレーカー着用
 
16:30 - 17:45
A
T3-337
Visual Studio 2005 で改善された Windows フォーム
マイクロソフト株式会社 高橋 忍
18:30 - 20:30
展示ホール B
 
Attendee Party
 
21:00 - 
手作り居酒屋 甘太郎 みなとみらい店
 
NAgile Open Source Project Group 宴会
 
 
 
二次会
 


8月4日(木)
時刻 Room Session ID Title Speaker
09:00 - 10:15
E
T3-325
Microsoft Visual C++ 2005 の新機能と既存コードの改善/活用
NRI ラーニング ネットワーク 株式会社 矢嶋 聡
10:35 - 11:50
D
T1-402
マイクロソフトのモデル駆動開発における DSL ツール
マイクロソフト株式会社 荒井 省三
12:00 - 13:00
MVPラウンジ
 
INETA ビッグセッション 人間系 打ち合わせ
 
13:20 - 14:35
MVPラウンジ
 
MVP Roundtable 「C# 2.0 & .NET Framework 2.0 Insider」
マイクロソフト コーポレーション 波村 大悟
14:35 - 14:55
スタディーホール
 
INETA スタディーホール
 
14:55 - 16:10
B
T3-406
Visual Studio 2005 Team System によるプロセス統合 (仮)
株式会社 豆蔵 岡村 敦彦
16:00 - 18:25
会議センター3F @317
 
INETA ビッグセッション準備
 
18:30 - 20:00
会議センター5F Room-F 小ホール
 
Welcome to INETA Japan Event 2005! 『デスマーチからの脱出 ~徹夜からの帰還~』
INETA Japan
20:30 - 21:30
Tech・Ed会場周辺のお店
 
INETA Japan ミーティング ― Tech・Edイベント反省会,その他
 
 
 
二次会
 


8月5日(金)
時刻 Room Session ID Title Speaker
09:00 - 10:15
E
T1-403
Software Factories によるシステム構築
マイクロソフト株式会社 萩原 正義
10:35 - 11:50
A
T2-315
Visual Studio 2005 と ASP.NET 2.0 を利用した Web サービスの開発
マイクロソフト株式会社 中原 幹雄
11:50 - 13:20
ヨコハマ グランド インターコンチネンタル ホテル 3F
 
Peer Talk Lunch
 
14:55 - 16:10
B
T3-407
Visual Studio 2005 Team Foundation Server 詳細
マイクロソフト株式会社 近藤 和彦
16:30 - 17:45
B
T1-303
システム要求開発からアーキテクチャ設計、実装までのプロセスを効率化するアプローチ
マイクロソフト株式会社 安藤 浩二、有限会社アークウェイ 森屋 英治

写真


・会場となったパシフィコ横浜
・セッションの様子
・セッションからセッションへ移動する人々
・Study Hall のマイクロソフト コミュニティ ブース ― INETA Japan の展示の様子
 
・Attendee Party の様子
・Sponsers Area と Microsoft Tech・Ed Store の辺り
・Sponsers Area にて ― Intel と Microsoft のロゴが入った「インテル センス」。
・MVPラウンジ
・MVPラウンジにて ― マイクロソフトの社員証を付けたキティちゃん。話し掛けると相手になってくれる。

復路

2005/08/05(金) 帰宅
みなとみらい線 → JR横浜線 → JR新幹線 → JR北陸本線 → えちぜん鉄道

23:30 自宅着


所感

今年もアツイ四日間だった。沢山の刺激を受けた。
朝から晩まで、のんびり息をついている間もない程だった。セッションの合間も夜も含めて技術漬けの毎日だ。

  • セッション
    • 初日は、Enterprise Library のセッションがメイン。Enterprise Library ハンズオン ラボの実例を元に、判り易く解説。もっともっと Enterprise Library を使ってみたいと思わせる内容だった。
    • Visual Studio 2005 のセッションでは、如何にツールが進化したかをデモンストレーションするセッションが多く、どれも楽しめた。確かに素晴らしく良くなっている部分が多いことを再確認。
    • その他で今回楽しみだったのは、DSL (Domain Specific Language: ドメイン特化言語) や Software Factories の話。幾つものセッションで聞くことができた。特に、8/5 の萩原さんのセッションは良かった。Software Factories の今までにない判りやすい説明を聴くことができた。実コードによる説明には納得。
  • INETA Japan コミュニティ リーダーとしての参加
    • 8/3 と 8/4 にスタディーホールに INETA Japan コミュニティ リーダーとして参加。「INETA Japan Event 2005!」の抽選券と交換できる名刺を配ったりした。
    • また、8/4 の「Welcome to INETA Japan Event 2005! 『デスマーチからの脱出 ~徹夜からの帰還~』」に「人間系」チームで参加。350名位の方々に聴きにきて頂き、満員御礼だった。このセッションの準備では、色々な問題があったが、セッション自体は各人の能力によって成功だったと思う。反省点は多いが。
  • その他
    • 今年も、毎晩飲みに行ったわけだが、毎晩のように素晴らしい技術者の方とご一緒させて頂いた。様々なことを考えさせられた。余りに、色々なことを考えすぎて熱が出たくらいだ。

エンジニアとしての自らの現状を見つめ直す機会になった。
ツールの進化は素晴らしい。
でもツールだけ進化しても幸せにはなれない。
エンジニアとして、次のパラダイムに自らをシフトさせていかねば。
日々学んでいくのがエンジニアだ。
さあ変化を受け入れよう。

ここで一句詠んでみる。

「行く末の道の途中に吾亦紅(ワレモコウ)
― Fujiwo

吾亦紅(ワレモコウ)の花言葉は「変化」。
wrmk.jpg

2005年09月28日

Microsoft MVP Global Summit 2005

今日からシアトルへ。

Microsoft MVP Global Summit 2005
期間 2005/09/28~10/01 (10/04 帰宅)
場所 ワシントン州レドモンド マイクロソフト コーポレーション

荷造り完了。
sbggg.jpg

今回、英会話の本やジャケット等、色々と散財。

電子辞書と無線LANアダプタも新たに購入。
ediclan.jpg
電子辞書は、SHARP の PW-M800。
¥10,800 (メーカー希望小売価格:¥35,000) だった。
電子辞書の中でも特に小さく軽く (電池を入れて 113g と平均的なものの半分位) 値段も手頃で、収録辞書は22。

2005年09月30日

Microsoft MVP Global Summit 2005 出発~成田

シアトルに向け朝6:40に自宅を出発。
車で小松空港へ。

乗るのは、一日一便しかない 8:00 発 ANA 成田行き。

普通の 767 だとばかり思っていたら、なんとも小さい飛行機で吃驚。
mvpsmt01.jpg

成田へ移動中。
mvpsmt02.jpg

成田空港到着。まだまだ時間があるので、タイのグリーン カレーを食べたり、ぼんやりと飛行機を眺めたりした。
mvpsmt03.jpg

MVP が集合して、いざ出発の乾杯。
mvpsmt04.jpg

Microsoft MVP Global Summit 2005 一日目

ノースウェスト航空の直行便でシアトル空港に到着。
時差ぼけ防止のため、前夜は一時間の睡眠に抑え、飛行機の中で睡眠をとった。
mvpsmt05.jpg mvpsmt06.jpg mvpsmt07.jpg

"COMP USA" というコンピュータ専門店へ。ここではプレゼン用のツールを購入。
mvpsmt08.jpg mvpsmt09.jpg

ボーイング社の敷地内にある航空博物館へ。SR-71偵察機の本物が展示してあって感激。
mvpsmt10.jpg mvpsmt11.jpg

コンコルドの中も見た。
mvpsmt12.jpg mvpsmt13.jpg

昼食はシアトルの東の町べレビューで中華。
mvpsmt14.jpg mvpsmt15.jpg

その後、"Microsoft MVP Global Summit" にレジストレーション。
mvpsmt16.jpg

Microsoft のカンパニー ショップへ。最新のマウスだとか Windows Vista のシャツとか色々と購入。
mvpsmt17.jpg

そしてコロンビア ワイナリーへ。5種類くらいのワインを試飲させてもらった。とても美味しい。一本購入。
mvpsmt18.jpg mvpsmt19.jpg mvpsmt20.jpg

"Doubletree Hotel Bellevue" にチェックイン。部屋がとても広い。
mvpsmt21.jpg

日本の MVP でディナーへ。
mvpsmt22.jpg

その後ホテルのバーで暫く話をした。途中はあまりの眠さに、しょっちゅう意識を失う。
そして就寝。長い一日だった。

2005年10月04日

Microsoft MVP Global Summit 2005 二~四日目

セミナーの内容は NDA (Non-Disclosure Agreement: 秘密保持契約) の関係で詳しく書けないので、差し障りのない範囲で。

マイクロソフト キャンパスは三万人もの社員がいるそうで、とても広い。木々が茂る中に沢山の建物が建っている。そしてバスが巡回している。
とても良い環境だ。
一人一部屋ずつあるという仕事場のある建物にも入った。

三日間は、英語漬けの日々。
英語でセミナーが続く。

途中で、Don Box 氏と写真を撮った。それから Gregor Hohpe さん を紹介していただいた。

途中の夕食会では、マイクロソフト コーポレーションの社員や外国の MVP に積極的に英語で話し掛けるようにしてみた。
十~二十人位の人と話したと思う。矢張り余り通じない。
以降、一度話した人と再会したときには、目を合わせて "Hi." と笑いかけるようにしてみた。これは必ず答えてもらえるので良い。

MVP Party というのもあった。場所は、シアトル EMP (Experience Music Project)
mvpsmt51.jpg

ここでも、なるべく外国の人と話すようにしてみた。

こんなに英語を使ったのは初めてだ。
特にヒアリングが大切だと思った。

MVP Summit 中はとにかく「時差ぼけ」がひどかった。
セミナー中に何の前触れもなく突然意識がなくなることがしょっちゅうだった。

Microsoft MVP Global Summit 2005 後

Microsoft MVP Global Summit 2005 が終わって再び少しだけのシアトル観光へ。

先ずは、"Bellevue Square" へ。
mvpsmt53.jpg mvpsmt54.jpg
mvpsmt55.jpg

次は、シアトルのダウンタウンにも少しだけ。
mvpsmt56.jpg mvpsmt57.jpg mvpsmt58.jpg mvpsmt59.jpg mvpsmt60.jpg

最後は、シーフードのレストランで打ち上げ。
mvpsmt62.jpg mvpsmt61.jpg mvpsmt63.jpg

寝不足が続いていたので、この晩は大人しく就寝。

Microsoft MVP Global Summit 2005 より帰国

シアトル最終日。

まずは、"Doubletree Hotel Bellevue" をチェックアウト。

飛行機まで若干時間があるので、アメリカのテレビ ドラマ "Twin Peaks" で有名だというシアトル近くのスノコルミーの滝 (Snoqualmie Falls) へ。

スノコルミーは、とても美しい滝だ。
mvpsmt102.jpg mvpsmt103.jpg

シアトルとその近郊は、都会と自然が調和した美しいところだ。
美しい山と木々と湖がある。

もうすっかり秋で、紅葉も綺麗だ。
東京辺りと比べても随分気温が低い。

mvpsmt105.jpg mvpsmt106.jpg mvpsmt107.jpg mvpsmt108.jpg

そしてシアトル空港へ。
mvpsmt109.jpg

シアトル最後の食事は Burger King のステーキ バーガーだった。
mvpsmt110.jpg

ノースウェスト航空で成田へ。
入国手続きを済ませて、成田エキスプレスに乗った。

今は品川プリンスホテル。

明日は福井へ帰る。そのまま仕事の予定。
早く妻子の顔を見たい。

2005年10月16日

VSUG ミーティング

10/15(土) は、VSUG (Visual Studio Users Group) のミーティングに参加すべく、新宿小田急サザンタワーへ。
scghndsy.jpg scghndsy.jpg scghndsy.jpg

一次会、二次会と多くの素晴らしい方達と技術やコミュニティの話ができて楽しかった。
調子に乗って焼酎を飲みすぎた。
大崎に一泊して翌朝帰宅。

VSUG の関連記事:

2005年10月28日

11月17日

event.gif
今年の 11月17日 に、あのソフトウェアが発表になる。

そう。

…いや違った。まあ、『ポケモン不思議のダンジョン 青の救助隊』も 11月17日 に発表になるんだけれども。

待ちに待った 『Visual Studio 2005』や『SQL Server 2005』が発表になるのだ。

そして、この日はイベントが開催される。

そう!

…でなくて。まあ、『産官学連携フェスティバル2005』も 11月17日 に開催されるけども。

この日は、

が開催される。
マイクロソフト株式会社社長のダレン ヒューストン氏やマイクロソフト コーポレーション CEO のスティーブ バルマー氏が話す。

私も参加予定。

the Microsoft Conference 2005』 (東京会場)
開催日時 11月17日(木) 9:30~18:30 (受付開始 8:30)
※ スペシャルプログラムは 11:00~18:30
開催会場 名称 東京プリンスホテルパークタワー
住所 東京都港区芝公園 4-8-1

そして、この日は記念すべき日でもあるのだ。
そう。それは、

でなくて。 確かに、この日 TOKIO の城島は 35歳になるけども。
この日は実は、

  • 「蓮根の日」

でもなくて。 確かに、この日は第十二回「蓮根の日」には違いないけど。

なんと、この日 Visual Studio User Group (VSUG) も発足予定なのだ。
まあ、めでたい日だ。

# またこの二日後に、 『NAgile合宿2005秋』というのも予定されている。
# というわけで、11/17~18は東京に滞在予定。

2005年11月02日

マイクロソフト株式会社「スタート・マイ・ドメイン」キャンペーン

http.jpg
マイクロソフト株式会社「スタート・マイ・ドメイン」キャンペーンを開始
~新規ドメイン取得無料、月額換算利用料1,000円以下などの特典を提供~

例えば、KAGOYA の DOSABA は 1GB で一年目6,300円(月額525円)、二年目以降10,080円(月額840円)。
うーん、中々魅力的な価格。Windows のホスティング サービスは今まで様子見してたけど、そろそろ手を出しても良いかな。

今はロリポップで 200MB で月あたり300円だけど、Apache で Perl と PHP で、MySQL、MovableTypeXOOPS という環境。
DOSABA なら IIS で ASP.NET で、SQL Server、DotNetNukeCommunityServer という環境。

取り敢えずロリポップの方とは別に、DOSABA か cervi.jp 辺りを利用してみようかな。

ニュース:

Enterprise Watch の記事に比較表が載っている。

2005年11月17日

Visual Studio ユーザーグループ (VSUG) オープン

event.gif
# 11月17日の続き。

Visual Studio ユーザーグループ (VSUG) が、本日オープンしました。

私は、小井土さんと一緒に、この中の「開発プロセス」フォーラムのボードリーダーを務めます。

いよいよ本日、待望の Visual Studio 2005 が発表になり、

も開催されます。
# 私もこれから東京に向かいます。

Visual Studio 2005 は、素晴らしく開発効率をあげるツールに仕上がってきています。
無料の Visual Studio 2005 Express Edition も出ますし、Team System にも期待が高まります。

Visual Studio に関心の有る方は、是非お気軽に Visual Studio ユーザーグループ (VSUG) にご参加ください。

2005年11月18日

the Microsoft Conference 2005

昨日は、the Microsoft Conference 2005 へ。

the Microsoft Conference 2005 (東京会場)
開催日時 11月17日(木) 9:30~18:30
開催会場 名称 東京プリンスホテルパークタワー
住所 東京都港区芝公園 4-8-1

msc1.jpg
msc2.jpg
msc3.jpg
msc4.jpg

Visual Studio 2005 と SQL Server 2005、 BizTalk Server 2006 のローンチ記念イベント。

夜は、「Visual Studio / SQL Server 2005 ローンチ記念コミュニティーパーティー」へ。
場所は、キハチ・銀座店。

マイクロソフトや MVP、INETA Japan、VSUG (Visual Studio User Group) の方々と再会して色々な話をした。
ジム グレイ (Jim Gray) 氏とインスタンス カメラで写真を撮って、それにサインをもらったりして、楽しかった。
その後軽く二次会へ。

# 今日も東京泊。

2005年11月30日

寄せ書き

を書きました。
どちらの製品もとてもお薦めです。

2005年12月03日

VSUG設立記念セミナー

seminar.gif
これからVSUG (Visual Studio User Group) 設立記念セミナーに出発。

イベント名 VSUG設立記念セミナー
日時 2005年12月3日(土) 13:00開始 18:30終了
主催 VSUG (Visual Studio User Group)

2005年12月05日

VSUG設立記念セミナー

以下のイベントに参加した。

イベント名 VSUG設立記念セミナー
日時 2005年12月3日(土) 13:00開始 18:30終了
主催 VSUG (Visual Studio User Group)

■ 内容


  • 挨拶
    VSUG発起人
    vsugsmn1.jpg

  • VSUG設立についてお祝いの言葉
    マイクロソフト株式会社 北川 裕康氏

  • 70分でズバリ!!Visual Studio 2005ココがお勧め!! ~"使える"新機能、差分機能をTips満載でご紹介~
    VSUGフォーラムリーダー 荻原 裕之氏
    vsugsmn2.jpg

  • ソフトウェア開発における諸問題を最新技術はどう捉えているか
    マイクロソフト株式会社 萩原 正義氏

  • VSラウンチイベント in US
    VSUG発起人 奥津 和真氏

  • Lightning Talk
    VSUGフォーラムリーダー

  • 懇親会
    vsugsmn3.jpg

  • 二次会

  • 三次会

■ 感想など

○ 講演について

荻原 さん と 萩原 さん の講演があった。

荻原 さん の講演は、Visual Studio 2005 の新機能部分に関する Tips。
楽しい Tips が多かった。
「Ctrl キーで DataTip が透ける!」など、知らなかった Tips が結構あった。
早速今日会社で披露。

萩原 さん の講演は、ソフトウェア工学の視点からソフトウェア開発の諸問題をみる、というもの。
とても良かった。
マインドマップでメモをとりながら聴いていたのだが、ものすごい勢いで、マインドマップの枝が伸びていった。
自分の発想がどんどん広がっていくのが分かる。

問題をどう捉えればよいのか、問題に対してどういう見方ができるのか。
それについて、萩原 さん の講演ではいつも、「かくあるべき」という視点を突きつけられる。
それと対比することで、自分のものの見方について再考させられたり、新たな視点が芽生えたりするのだ。

それは、とても貴重な体験だ。

○ Lightning Talk について

私は、VSUGフォーラムリーダーとして Lightning Talk をやった。
内容は、アンケート。参加者の皆さんに、手で大きな○か×を作って答えていただいた。100人程度の参加者だったので、1/100 アンケートもやってみた。

Lightning Talk はコミュニティ イベントらしさを出すのに最適なツールの一つだと思った。一般参加者の方にもやってもらえれば更に良いと思う。

○ 懇親会などについて

コミュニティ のオフライン イベントは一方通行で終わってはつまらない。
それでは、本当につまらない。
コミュニケーションは双方向でないと。そして、フェイス トゥ フェイスが重要だ。

今回、多くの人とコミュニケーションがとれた。
イベント後の飲み会では、大きな感動を味わいさえした。
こういう体験が、私にとってとても大切だ、ということを再認識した次第だ。

続きを読む "VSUG設立記念セミナー" »

2006年01月12日

Visual Studio 2005 Team System 学習用 Web サイト

● MSDN ― Visual Studio Team System ホーム ● MSDN ― Visual Studio Team System ホワイト ペーパー ● MSDN ― Visual Studio Team System 技術情報 ● MSDN ― Visual Studio 2005 Team Foundation Server スタートアップガイド ~ソフトウェア構築の未来へ~ ● MSDN ― Visual Studio 2005 Team System 導入事例 ● Microsoft.com Japan ― Visual Studio 2005 Team Foundation Server Beta 3 インストール ガイド (2005/12/16) ● @IT ― Visual Studio 2005 Team System 概要 ● @IT ― Visual Studio Team Systemで開発プロジェクトはこう変わる ● @IT ― VSTSの単体テスト機能は本当に使えるのか? ● Enterprise Watch ― 「Visual Studio 2005 Team System」、その本質に迫るThink IT (無料会員制) ― 『Visual Studio 2005を活用した、テスト駆動開発とソフトウェア品質向上アプローチ』Think IT (無料会員制) ― 『開発ライフサイクルとVisual Studio 2005という選択肢』Think IT (無料会員制) ― 『チーム開発ここまできた、個人からチームの生産性向上へ』CodeZine ― 『Visual Studio 2005 Team Systemの全体像を知る』

■ トレーニング

● MSDN ― Visual Studio Team System トレーニング ● MSDN ― イベント & セミナー 特別企画「Tech・Ed 2005 セレクション」ストリーミング公開 (無料登録制) ● MSDN ― MSDN eyeiStudy Quick Learning ― 『SQL Server 2005 Quick Learning』『Visual Studio 2005 Quick Learning』 (無料登録制)
  • Visual Studio 2005 Team System 品質向上の為の役割とスキルを学ぶ編
  • Visual Studio 2005 Team System アーキテクトの役割とスキルを学ぶ編
● MSDN ― Visual Studio 2005 Team System: Demo Videos (英語) ● MSDN ― ステップ 7 シリーズ ―Visual Studio 2005 Team System によるアプリケーション品質の向上
  • 第1回 Visual Studio Team System によるチーム開発 (2007/10/03)
  • 第2回 品質の高いコード作成 (1) (2007/10/10)
  • 第3回 品質の高いコード作成 (2) (2007/10/17)
  • 第4回 さらなる品質の向上 (2007/10/24)
  • 第5回 チーム開発を支える機能 (2007/10/31)

続きを読む "Visual Studio 2005 Team System 学習用 Web サイト" »

2006年02月03日

Microsoft Developers Conference 2006

下記に参加。

名称 Microsoft Developers Conference 2006
会期 2006年2月2日(木) - 3日(金)
会場 パシフィコ横浜 会議センター 1F-3F
主催 マイクロソフト株式会社
参加費 一般価格: ¥49,800、特別価格: ¥40,000 (事前登録制)

参加内容は以下の通り。

2/2 9:00-10:40   基調講演 マイクロソフト コーポレーション デベロッパー & プラットフォーム エバンジェリズム グループ コーポレート バイス プレジデント サンジェイ パラササラシー、マイクロソフト コーポレーション Windows クライアント プラットフォーム & ドキュメント ゼネラル マネージャ マイケルウォーレント、マイクロソフ トコーポレーション インフォーメーション ワーカー ビジネス グループ ゼネラル マネージャ 沼本 健
2/2 10:55-11:55   ゼネラル セッション マイクロソフト コーポレーション Developer Division のテクニカル フェロー アンダース ヘルスバーグ
2/2 13:15-14:25 T3-206 Windows Communication Foundation ("Indigo") 概要 マイクロソフト株式会社 伊藤 英豪
2/2 14:40-15:50 T2-324 次世代開発基盤技術 "Software Factories" Part 1 ~ その概念の中心 マイクロソフト株式会社 成本 正史
2/2 16:05-17:30 MVP Private Roundtable Sharing info & Discussion about Windows Presentation Foundation (Avalon) マイクロソフト コーポレーション Windows クライアント プラットフォーム & ドキュメント ゼネラル マネージャ マイケルウォーレント
2/2 17:30-18:40 T1-306 Windows Presentation Foundation ("Avalon") マイクロソフト株式会社 高橋 忍
2/2 19:00-21:30   コミュニティ スペシャル セッション with Anders Hejlsberg in Microsoft Developers Conference 2006 マイクロソフト コーポレーション Developer Division のテクニカル フェロー アンダース ヘルスバーグ
2/3 9:00-10:30   基調講演 マイクロソフト コーポレーション デベロッパー & プラットフォーム エバンジェリズム グループ コーポレート バイス プレジデント サンジェイ パラササラシー
2/3 10:50~11:55 T1-301 Windows Presentation Foundation ("Avalon") による新しいアプリケーション ユーザー インターフェースの実現 マイクロソフト株式会社 高橋 忍
2/3 13:15-14:25 T2-501 次世代開発基盤技術 "Software Factories" Part 2 ~ 開発手順の実践 マイクロソフト株式会社 萩原 正義
2/3 14:40~15:50 T2-314 リレーショナル データのための LINQ マイクロソフト株式会社 荒井 省三
2/3 16:05~17:15 T1-307 Windows Presentation Foundation ("Avalon") : 先進的なドキュメント ワークフロー コンテンツのセキュリティ、ビュー、印刷 マイクロソフト プロダクト ディベロップメント リミテッド 佐藤 大樹
2/3 17:20-18:00 Hands-On Lab Windows Vista: Windows Presentation Foundation Hands-On Lab  

2006年02月05日

Special Session with Anders Hejlsberg in Microsoft Developers Conference 2006

2/2 夜は、Turbo Pascal と Delphi、そして C# の父である Anders Hejlsberg を囲んだスペシャル セッションに参加。

anders.jpg
参加者からの沢山の C# 関連の質問に答えていただいた。
とてもフレンドリーな方で、どんな質問にも笑顔で真剣に答えてくださった。

サイン入りの C# 本まで頂いてしまった。
Turbo Pascal と C# のファンである私としては、かなり嬉しくて舞い上がってしまった。
84103886_102.jpg 84103886_153.jpg

※ この様子はいずれレポートしたい。

2006年02月11日

Visual Studio 2005 ACE

vs2005ace1.jpg vs2005ace2.jpg vs2005ace3.jpg

マイクロソフトから、Visual Studio 2005 ACE の置物 (謎) が届いた。

2006年03月02日

レポート:コミュニティスペシャルセッション with Anders Hejlsberg in Microsoft Developers Conference 2006

csah01.jpg csah14.jpg
※ 「Special Session with Anders Hejlsberg in Microsoft Developers Conference 2006」の続き。

MSDN に、

レポート:コミュニティスペシャルセッション with Anders Hejlsberg in Microsoft Developers Conference 2006

を書いた。


一色さんの記事
と合わせてお読みいただけると幸いだ。

2006年03月07日

.NET Tips: NDoc の紹介

document.gif
■ NDoc の紹介

Visual Studio 2005 と "NDoc" というツールを使うことで、MSDN のような HTMLヘルプ形式 (.chm ファイル) を含む様々なヘルプ形式を出力できる。

NDoc は以下で入手できる。

■ NDoc 日本語版の導入方法

NDoc 日本語版を使うには、以下のような設定を行うと良い。

  • Visual Studio 2005 側の設定
    • プロジェクトのプロパティの「ビルド」で「XML ドキュメント ファイル」を指定しておく。
  • NDoc 側の設定
    • NDoc の日本語版の最新版をインストール (現時点 2006/03/07 では v1.3.1-rev2-beta4 または v1.3.1-rev1)。
    • NDoc は現時点 (2006/03/07) では、そのままでは .NET Framework 1.0 と 1.1 にしか対応していないため、下の内容で "NDocGui exe.config" というテキスト ファイルを作成し、NDoc をインストールしたフォルダの下の bin というフォルダに入れておく。
    • 日本語のヘルプ出力のためには、NDoc を起動して、設定の "SdkDocLanguage" を「日本語」に変更。

※ "NDocGui exe.config" の内容
<?xml version="1.0" ?>
<configuration>
<startup>
<supportedRuntime version="v2.0.50727" />
<supportedRuntime version="v1.1.4322" />
<requiredRuntime version="v1.1.4322" />
</startup>
</configuration>

参考:

2006年03月27日

Visual System Team System Tips: チーム プロジェクトの削除方法

team.gif

Visual System Team System Tips: チーム プロジェクトの削除方法

■ Q.

Visual Studio Team System を試している。チーム プロジェクトを試しに作ってみたのだが、消し方が分からない。不要なチーム プロジェクトが増えて困っている。

Visual Studio 2005 Team System の「チーム エクスプローラ」から「削除」を選ぶと、「チーム エクスプローラ」の一覧から消えるだけで、本当に消えてはいないようだ。 再度「Team Foundation Server に接続」しようとすると、一覧に残っている。

不要なチーム プロジェクトを本当に 消してしまうことはできないのか?

■ A.

もちろん、できる。

Team Foundation Server をインストールした PC に、"TFSDeleteTeamProject.exe" というコマンド ラインから使用するツールがある。これを使って不要なチーム プロジェクトを削除することができる。

このツールは、Team Foundation Server をインストールしたフォルダを、例えば、

C:\Program Files\Microsoft Visual Studio 8\

とすると、

C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\PrivateAssemblies

にあるはずだ。



この "TFSDeleteTeamProject.exe" を試しにコマンドラインから実行してみると、以下のように、このツールの使い方が表示される:

C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\PrivateAssemblies>TFSDeleteTeamProject
使い方: DeleteTeamProject [/q] [/force] </TeamFoundationServer:<サーバー名>> <チーム プロジェクト名>

Team Foundation からチーム プロジェクトを削除します。このコマンドを使用するには、Team Foundation Server 管理者グループのメンバであるか、または削除するプロジェクトのプロジェクト管理者グループのメンバでなければなりません。チーム プロジェクトを削除した後、復元することはできないため、注意してこのコマンドを使用してください。

   [/q] - 確認用のメッセージを表示しません。
   </TeamFoundationServer:<サーバー名>> - Team Foundation Server の名前です。
   [/force] - 削除できないデータがある場合でも続行します。
   <チーム プロジェクト名> - プロジェクトの名前です。名前に空白が含まれる場合は引用符を使用します。

C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\PrivateAssemblies>

次に、実際に  "tfsserver" というサーバーの "HelloProject" というチーム プロジェクトを削除してみた例を以下に示す:

C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\PrivateAssemblies>TFSDeleteTeamProject /TeamFoundationServer:tfsserver /force HelloProject
警告: チーム プロジェクトの削除操作は元に戻すことができません。チーム プロジェクトを削除しますか (Y/N)?y

Build から削除しています
完了
作業項目トラッキング から削除しています
完了
バージョン管理 から削除しています
完了
レポート サーバー ファイルを削除しています
完了
SharePoint サイトを削除しています
完了
Team Foundation コア から削除しています
完了

C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\PrivateAssemblies>

これで、チーム プロジェクトを削除することができた。

ちなみに、同じことをもう一度やってみると以下のようになる:

C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\PrivateAssemblies>TFSDeleteTeamProject /TeamFoundationServer:tfsserver /force HelloProject
警告: チーム プロジェクトの削除操作は元に戻すことができません。チーム プロジェクトを削除しますか (Y/N)?y

プロジェクト 'HelloProject' は、TF Server で見つかりませんでした。

C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\PrivateAssemblies>

このツールを使用する場合の注意点は以下の通り:

  • チーム プロジェクトを削除するときのユーザーが、Team Foundation Server 管理者グループのメンバであるか、または、削除するプロジェクトのプロジェクト管理者グループのメンバでなければならない。
  • バックアップがない場合に、チーム プロジェクトを削除すると、復活はできない。
  • 不完全に削除すると、削除した同じ名前でチーム プロジェクトを作成しようとした場合に、失敗することがあるので注意。
  • "/force" オプションを使わずに、削除が途中で失敗に終わった場合は、不完全な削除となっていることがある。この場合は、再度 "/force" オプション付きで再度削除を行うことで、削除工程を最後まで行わせることができる。

続きを読む "Visual System Team System Tips: チーム プロジェクトの削除方法" »

2006年04月24日

Japan MVP Summit 2006

jmvps01.jpg jmvps02.jpg
以下に参加した。

Japan MVP Summit 2006
日時 2006/04/22(土) 10:00~18:30
場所 マイクロソフト新宿本社

始発の飛行機に乗っていったのだが、会場へは10分遅れで到着。

参加内容は、

  • プログラム アップデート
  • ランチ セッション
    • 新MVPを囲む会
  • MVP によるプレゼンテーション
    • Web テスト
    • Virtual Server と愉快なインスタンス達
    • Community Server はもう古い? Windows SharePoint Sercive V3 を使った「コミュニティーサイト」の構築と運営
    • Windows Mobile 5 搭載 WILLCOM W-ZERO3 は何故流行っているのか?
  • ブレイクアウト セッション
    • 新たなユーザー エクスペリエンスへの挑戦 Microsoft Expression
    • 次期 ASP.NET とその実行環境

とても沢山の MVP の方々が参加されていた。聞いたところでは、8割を超える参加率だとか。

その後、MVP 主催の懇親会にも参加。こちらも、80名近い人が参加されていた。場所は、新宿ワシントンホテル2F「三十三間堂」。
その後、二次会まで参加。

知人の方々と久しぶりに直にお話ができたり、何人かの新たな知人ができた。
また、オンラインでお話したことがある何人かの方と、初めて対面できたこともとても嬉しかった。

しかし、この週は月曜日からずっと体調が悪かったので、この後また体調不良に。
もう復活したつもりで参加していたのだが、まだ体調が戻っていなかったらしく、自宅へ着いてから今朝まで寝込む羽目に。反省するとともに色々と凹んでいる。

# 今日は朝から病院へ行った。

2006年05月09日

VSUG DAY 2006 Summer 開催決定!

vsugday_120x60.gif VSUG DAY 2006 Summer
主催 VSUG (Visual Studio® User Group)
日時 2006年6月13日(火)10:00~17:00 (受付 9:30)
場所 秋葉原コンベンションホール (東京 秋葉原ダイビル 2F)
定員 400名
参加料 無料
申し込み方法 Visual Studio User Group > イベント > VSUG Day 2006 Summer より
(VSUGへの会員登録が必要)

2006年06月12日

マイクロソフトのカンファレンス

下記に参加予定。

Microsoft Office Developers Conference & MD3 2006
会期 2006年6月26日(月) ~ 27日(火)
会場 名称 住友スカイルーム (新宿住友ビル 47F)
住所 東京都 新宿区 西新宿 2-6-1 新宿住友ビル


Tech・Ed Yokohama 2006
会期 2006年8月29日(火) ~ 9月1日(金)
会場 パシフィコ横浜

.NET 3.0

IT Media News より ― 『Microsoftの「Web2.0」と「.NET 3.0」

上記より引用:

Microsoftは、これまでWinFXと呼んでいたデベロッパーブランドを.NET Frameworkに一本化させ、.NET Framework 3.0と改名した。また、開発者向けコミュニティーとして「MSDN Wiki」のβを開始した。

Visual Studio 2005 Team Foundation Server 評価版

『Visual Studio 2005 Team Foundation Server 評価版』をインストールしてみた。
vststry1.jpg
vststry2.jpg
vststry3.jpg

詳細はこれから確認だが、"β3 Refresh" では、エラーになって表示されなかったチーム ポータルのレポートがきちんと表示されているようだ。また、英語だった MSF のドキュメントが日本語化されている。その他、「セキュリティ」の権限の種類も変更されていたり、色々と変わったようだ。

関連情報:

2006年07月04日

Micosoft MVP

この七月に、Micosoft MVP (Most Valuable Professional) Visual Developer - Visual C# のアワードを再受賞することができました。

ありがたいことです。

思えばこの数年、色々な方に多くの機会を与えていただきました。技術者を中心として多くの人に出会い、様々なお話を伺うことが出来ました。講演を聴かせて頂いたり、飲み会でご一緒させて頂いたり。

素晴らしい技術者の方々に接し、「この人達には、とても適わない」と感じながらも、何とか生き方・考え方を見習おう、少しでも近づこうと考えつつ送ってきた日々。そうしてきた過程こそが、私にとってかけがえのない Engineering Life であったような気がします。

皆様に感謝です。そして、今後とも宜しくお願い致します。

2006年07月21日

MVP バッグ

mvpbag.JPG
MVP バッグが届いた。

ベースは、"Victorinox Louvre Computer Briefcase" っぽい。

2006年08月04日

Microsoft On - 出張ワークショップ

Microsoft On - 出張ワークショップ

8/2 Microsoft On - 出張ワークショップ を会社で開催。

  • .NET Framework 2.0 アーキテクチャ
  • Visual Studio 2005 によるスマートクライアント開発

栄えある第一回とのこと。
社内からは、予定を大幅に超える 50 人の技術者が参加して、社内の研修室は超満員。大成功だった。

わざわざ福井まで来ていただいて、きっちりとセミナーを二コマやっていただいた。ありがたいことだ。

関連記事:

2006年09月14日

第2回 VSUG アカデミー

『第2回 VSUG アカデミー』が開催されます。

日時: 2006年9月27日(水) 19:00~21:10 (※18:30受付)
場所: ニューオータニガーデンコート 17階 セミナールーム1
定員: 80名
参加料: 500円

申し込み: Visual Studio ユーザーグループ (VSUG) ― 第2回 VSUG アカデミー

2006年09月21日

Microsoft On - 出張ワークショップ - 最新技術を学ぶ in 福井

bannar_fitea_110x25.gif福井情報技術者協会(FITEA) からのお知らせ。

10月20日(金) に福井で「Microsoft On」が開催される。
これはマイクロソフトが福井に来て出張ワークショップをやってくれるというイベントだ。

マイクロソフトの最新の開発環境や次世代の Windows である Vista における開発のセミナーで参加は無料。

詳細情報と申込み先:

関連情報:

2006年10月22日

Microsoft On - 出張ワークショップ - 最新技術を学ぶ in 福井

Microsoft On - 出張ワークショップ - 最新技術を学ぶ in 福井
日時 2006年10月20日(金) 13:00~17:15
場所 ふくい産業支援センター 会議室A・B
主催 マイクロソフト株式会社 (共催: 福井情報技術者協会bannar_fitea_110x25.gif 後援: (株)福井コンピュータ、(株)トゥーアーティ、アル・デザインワークス)
内容 福井で「Microsoft On」による無料セミナーを開催。
  • 第一部「Visutal Studio によるアプリケーション開発実際」
  • 第二部「Windows Vista とWindows開発環境の進化」
講師 マイクロソフト株式会社 デベロッパー エバンジェリスト 松崎 剛 氏

microsoftonfitea1.jpg
microsoftonfitea2.jpg

無事終了。

  • 参加者数: 35名
  • 懇親会参加数: 11名

当初の予定以上に人が集まり、会場はいっぱいになった。
講師の松崎さんの話がとてもよかった。

開始前に「単なる Visual Studio 2005 の使い方の話より、.NET Framework の話を聴きたくて来られてる方が多いでしょう」とのご提案があり、前半のメニューでは、CLR や IL、アセンブリなどについての「中々 Web サイトや書籍では目に出来ないテクニカルな情報」について話していただいた。

後半は、Vista に関してデモを中心に話していただいた。

単に技術を紹介するにとどまらず、そのテクノロジーがどうして出てきて、今後どのような展望があるのかについて、松崎さん自身の考え方を聴けたのが良かった。「Web サイトや書籍で目に出来ない」生の声を聴くことができた。

コマンドライン上でのツールを使って基本的なことを説明した後で、Visual Studio 上で同じことをやってみせるやり方は分かりやすくて良かった。C# や、ASP.NET、XAML の説明のときにもメモ帳でソースを書くところから説明をしていた。共感できる。単なるドラッグ&ドロップの連続での説明だと見た目は派手だが内容に欠けるのだ。

聴衆を飽きさせずに、常に興味を引き付ける話ぶりは流石だ。参加者の方はその名調子に引き込まれていたようだ。

chikura さんと一緒に松崎さんを福井駅に送る車中で、三人でこれからの新人技術者についてなどの様々な話をした。

その後も懇親会でみっちりと話し合った。久しぶりに少し飲みすぎたが、とても楽しい一日だった。

2006年10月24日

VSUG DAY 2006 Winter

VSUG DAY 2006 Winter
日時 2006年11月25日(土)
11:00~20:00 (懇親会: 18:20~)
会場 ベルサール神田
主催 VSUG (Visual Studio User Group)
受講料 無料 (懇親会のみ 1,000円)
詳細/申し込み http://vsug.jp/tabid/135/Default.aspx

2006年10月31日

第1回 VSUG アカデミー for Students

第1回 VSUG アカデミー for Students
日時 2006年11月10日(金)
19:00~21:10 (18:30受付)
会場 株式会社翔泳社1Fスペース
主催 VSUG (Visual Studio User Group)
受講料 無料
詳細/申し込み http://vsug.jp/tabid/134/Default.aspx

2006年11月09日

今月のイベント

11月は以下の二つのイベントに参加予定。

the Microsoft Conference 2006 東京会場
日時 2006年11月15日(水) 10:15~17:20、16日(木) 10:00~18:10
会場 東京国際フォーラム
主催 マイクロソフト株式会社
参加費 無料
詳細/申し込み http://www.event-registration.jp/events/msc06/
VSUG DAY 2006 Winter
日時 2006年11月25日(土)
11:00~20:00 (懇親会: 18:20~)
会場 ベルサール神田
主催 VSUG (Visual Studio User Group)
受講料 無料 (懇親会のみ 1,000円)
詳細/申し込み http://vsug.jp/tabid/135/Default.aspx

2006年11月21日

the Microsoft Conference 2006

先週、以下に参加した。

the Microsoft Conference 2006』 東京会場
日時 2006年11月15日(水) 10:15~17:20、16日(木) 10:00~18:10
会場 東京国際フォーラム
主催 マイクロソフト株式会社
参加費 無料
詳細 http://www.event-registration.jp/events/msc06/

以下のセッションと「コミュニティ オープン ステージ」や「Windows Vista & 2007 Office system タッチ & トライ」、「パートナー ソリューション ショーケース」などに参加。

■ 所感
会場は「東京国際フォーラム」で、広過ぎてセッションからセッションへの移動が大変だった。

今回は特に SharePointServer 2007、 周りの情報収集を行った。SharePointServer と Exchange Server、InfoPath、と Office のクライアント製品の連携の部分だ。マイクロソフト松崎さんの Visual Studio を使った SharePointServer 2007 のカスタマイズの話は面白かった。

2007 への移行にあたって当面重要な鍵となりそうな、旧バージョンとの連携の話、特に SharePointService2.0 を使ったシステム (Team Foundation Server) などとの連携の話は余り聞けなかった。

一番楽しめたのは「コミュニティ オープン ステージ」だった。

以下は、会場の様子。
dc111731.jpg
dc111704.jpg
dc111726.jpg
dc111757.jpg
dc111756.jpg

続きを読む "the Microsoft Conference 2006" »

2006年11月22日

Windows Vista

昨日 MSDN サブスクリプション ダウンロード サイトで、Windows Vista (x86用日本語版) の提供が開始された。

早速昨日から今日にかけて、ノートPCにインストールしてみた。
※ PC のスペックは以下の通り。






















ノートPC DELL XPS M1210
CPU インテル Core Duo 2GHz
Memory DDR2-SDRAM 2GB
HDD SATA HDD 100GB
グラフィック NVIDIA GeForce Go 7400 256MB DDR TurboCache (最大192MBをメインメモリより使用)
ディスプレイ 液晶 12.1インチ 1280x800
先ずはアップグレード インストールを試してみたのだが、ダウンロードから Vista のインストーラの最後までで、四時間くらい掛かった。

ざっと触ってみた感じでは、アプリケーションはほぼ全部問題なく動くようだ。互換性に問題がありそうなアプリケーションは起動時に警告が出る。また、動作速度にも特に問題は無さそうだ。

これから、Office 2007 や Visual Studio 2005 の動作やクリーン インストールの場合など、色々と試してみるつもり。

2006年12月01日

こみゅぷらす

141861813_94.jpg
本日、コミュプラス (COMU+) というコミュニティがスタート。

こみゅぷらす (COMU+) サイトより:


こみゅぷらす (COMU+) とは、INETA の加盟コミュニティのリーダーや Microsoft MVP などこれまでもアクティブに情報を発信してきたメンバーが集まって結成した団体です。

Windows 技術を中心に、さまざまな情報をオンライン、オフライン勉強会を通じて配信していきます。

メンバーのブログより:

VSUG DAY 2006 Winter

先週土曜日は以下のイベントに参加。


































VSUG DAY 2006 Winter
日時 2006年11月25日(土)
11:00~20:00 (懇親会: 18:20~)
会場
ベルサール神田
主催 VSUG (Visual Studio User Group)
受講料 無料 (懇親会のみ 1,000円)
詳細/申し込み http://vsug.jp/tabid/135/Default.aspx

dc112610.jpg
dc112620.jpg
期待していた以上に高度なセッションが多く、聴きごたえがあった。他のカンファレンスでは聴けない内容のものが多いのも、コミュニティのイベントならでは。

ライトニング トークスも素晴らしく、直ぐにでも役立つ知識が目白押しだった。それを5分で容赦なく切ってしまうのだから、とても贅沢だ。

昼は、何人かで「まんてん」のカレーを食べに行った。
dc112617.jpg

夜は、懇親会に参加。その後スタッフの皆さんと飲みにいった。
有意義な一日だった。

2007年03月01日

3月のイベント

前回に続き MVP Global Summit に参加予定。

2007 Microsoft Most Valuable Professional (MVP) Global Summit
期間 2007/03/12~15 (03/17 帰宅)
場所 ワシントン州レドモンド マイクロソフト コーポレーション

今日届いたジャケット。
dc030102.jpg

今日届いたノイズキャンセリング ヘッドホン。
dc030105.jpg dc030107.jpg

2007年03月12日

MVP Global Summit 2007

※ 「3月のイベント」の続き。

これから MVP Global Summit に向けて出発。

2007 Microsoft Most Valuable Professional (MVP) Global Summit
期間 2007/03/12~15 (03/17 帰宅)
場所 ワシントン州レドモンド マイクロソフト コーポレーション
荷造り完了。 DSCF8020.JPG 先ずは小松空港から成田空港へ移動。

2007年03月19日

MVP Global Summit 2007

MVP Global Summit 2007 を写真で振り返ってみる。

■ 一日目 (3/12)


小松空港を出発して成田へ


飛行機の窓から富士山が


Northwest航空で成田を出発


Seattle-Tacoma国際空港 (Sea-Tac) に到着


Seattle-Tacoma空港に集う日本のMVP達


Safeco Fieldへ


Safeco Field 記者会見が行われる部屋


Safeco Field グランドへの通路


Safeco Field グラウンドより


Bellevue SquareのWorld Wrappsでランチ


Bellevue SquareのCOACHで妻のバッグを購入


MVP Global Summit のレジストレーション時に受け取ったシャツ


SeattleダウンタウンのCrowne Plaza Hotelに到着


Seattleダウンタウンで見掛けた建物


Seattleダウンタウンの町並みを見ながら、日本のMVPだけのパーティの会場へ向かう


夜、翌日の会場となるSeattleダウンタウンの Washington State Convention and Trade Center の前を通る


初日の夜にビールやスナックを買った店

■ 二日目 (3/13)


Bill Gates氏のキーノート


Don Box 氏のセッション


夕方一時間だけ時間が空いたので、独りでPike Place Marketまで散歩


Pike Place Marketにて


Pike Place MarketにあるStarbucks Coffee一号店


マグカップを購入 (この後ボーイング社の航空博物館でMVPパーティ)

■ 三日目 (3/14)


三日目からの会場となるRedmondのマイクロソフトのキャンパスへ行き、先ずは朝食


昼食では、隣のテーブルに (Turbo PascalとDelphiとC#の父である) Anders Hejlsberg氏が


会場にはスナックやフルーツが沢山おいてあって自由に食べることができる


会場のスナックはどれもカロリーが高そう


飲み物は、コーヒーや紅茶の他、冷蔵庫には ROOT BEER などが用意されている


Vistaドリンクは、無糖の炭酸飲料


キャンパスでは桜が身頃だった


この日の夕方に買ったマイクロソフトのデバイス


セッションでもらったC#シャツと夜のプロダクト グループ毎のパーティでもらったTシャツとVisual Studio Team Systemのポロシャツ

■ 四日目 (3/15)


全てのセッションが終わり、昼からは最後のランチ


その日の夜、日本のMVPがシーフードレストランSalty'sに集まって行われた最後のパーティで出たSeattle名物のクラム・チャウダー


同じく蟹


Salty'sは海沿いにあって夜景が美しい


■ 四日目 (3/15)


最終日にSeattle近郊のSnoqualmie Fallsへ


NikeやEddie Bauerなどがある巨大なショッピング モールへ


Seattle-Tacoma国際空港にてBurger Kingで昼食


成田まで九時間近くと成田から一時間余りのフライトの上、無事小松空港に帰還

2007年04月15日

MVP Road Show 2007 金沢

昨日の夕方は、『MVP Road Show 2007 金沢』に参加。
マイクロソフトと北陸三県 (福井、石川、富山) から一名ずつ計四名が参加。

全員シアトルでの MVP Global Summit 2007 に参加していた。

色々と北陸の話ができたし、新しい情報も手に入った。
# LifeCam も貰った。

以下は、久々の金沢駅前の写真 (一番下は会場となった金沢都ホテル)。
dc041505.jpg
dc041506.jpg
dc041503.jpg
dc041511.jpg

2007年04月26日

こみゅぷらす (COMU+) & eパウダー Vista & 2007 Office Community Launch

こみゅぷらすで、来月半ばに以下のイベントを開催します。
雰囲気の良い素敵な店で、食事や酒等を楽しみながら参加いただけるという画期的な技術系イベントです。
どうぞ奮ってご参加ください。

こみゅぷらす (COMU+) & eパウダー Vista & 2007 Office Community Launchこみゅぷらす (COMU+) & eパウダー Vista & 2007 Office Community Launch
日時  2007年5月19日(土) 16:30 ~
場所 ピンクカウ (渋谷・表参道から徒歩7分)
主催/共催 こみゅぷらす (COMU+)eパウダ~
会費 ¥3,500 (おつまみ ¥1,000 + ビュッフェ ¥2,500)
詳細/申し込み 特設ページ
紹介サイト

2007年05月08日

VSUG DAY 2007 Summer

130x120_vsugday.gif
主催 VSUG (Visual Studio User Group)
日付 2007年6月1日(金)
会場 コクヨホール (東京都港区)
会費 無料 (VSUG 会員登録が必要)
テーマ 「次期VSがやってくる」
詳細 http://vsug.jp/tabid/151/Default.aspx

2007年05月17日

「第16回 codeseek 勉強会」と「こみゅぷらす (COMU+) & eパウダー Vista & 2007 Office Community Launch」

今週の土曜日は以下の二つのイベントに参加予定。
# どちらも少ししゃべる予定。

まだ申し込みを受け付けているので、興味のある方はお早めに。

第16回 codeseek 勉強会
日時 2007年5月19日(土)13:00~15:00
    こみゅぷらす Community Launch イベント直前
場所 渋谷貸し会議室を予定
主催 codeseek
協力 tk-engineeringこみゅぷらす (COMU+)
テーマ オブジェクト指向初心者脱出次の一手
内容 プログラミングのためのより現実的な視点でのオブジェクト指向についてサンプルコード を交えての解説と座談会形式での勉強会
参加費用 ¥1,000 飲み物込
申込期限 2007年5月18日(金) 18:00
詳細/申込先 こちら
comuplusclt.png > こみゅぷらす (COMU+) & eパウダー Vista & 2007 Office Community Launch
日時  2007年5月19日(土) 16:30 ~
場所 ピンクカウ (渋谷・表参道から徒歩7分)
主催/共催 こみゅぷらす (COMU+)eパウダ~
会費 ¥3,500 (おつまみ ¥1,000 + ビュッフェ ¥2,500)
プログラム
  • 16:30 ~ 19:00 スライドセッション
    • イベントのご説明、ご挨拶
    • 駆け足で語る Vista & Office 概要 (尾崎 義尚 - om(takanao))
    • C 言語から見た Windows Vista & JIS 2004 概要 (亀川 和史 - めさいあ)
    • Visual Studio 2005 Tools for the 2007 Microsoft Office System 概要 (永幡 直子 - なおこ(・∀・))
    • Windows SharePoint Services 3.0 概要 (NAL-6295)
  • 19:00 ~ 21:00 テーブルセッション
    • VB6 と Vista の怪しい関係 (衣川 朋宏)
    • Windows Workflow Foundation概要 (衣川 朋宏)
    • Office 2007 で今作る!豪華粗品の抽選プログラム (原敬一)
    • MacにVistaを入れました (原敬一)
    • DWM/Aero Overview (吉原 優一 - Elfaria)
    • リボンのリテン (尾崎 義尚 - om(takanao))
    • Groove 2007 (尾崎 義尚 - om(takanao))
    • Vista 時代のモバイル開発環境構築 (岡本 貴浩 - こた)
    • Windows の歴史 (ウィンドウズ以前~Vista) (小島 富治雄 Fujiwo)
    • サイドバー ガジェット (高須 淳一 - Moo)
    • 高橋メソッド.pptx (高須 淳一 - Moo)
    • TBD (村岡淳子)
    • IME2007チューンナップ (杉山 洋一 - かるあ)
    • Windows Communication Foundation 概要 (NAL-6295)
    • JIS 2004 詳細 (亀川 和史 - めさいあ)
  • 21:00頃 豪華粗品抽選会
  • 自由解散
詳細/申し込み 特設ページ

2007年07月02日

Micosoft MVP

この七月に、Micosoft MVP (Most Valuable Professional) Visual Developer - Visual C# のアワードを再受賞することができました。三年目になります。

お世話になっている多くの皆さんに感謝です。IT業界で知り合いうことのできた多くの皆さんとの会話が、私にとっての糧です。

今後とも宜しくお願い致します。

# よりよいIT業界を目指しましょう。

2007年07月03日

Tech・Ed 2007 Yokohama

bnr_teched_2007_120-90.gif
今年も Tech・Ed Yokohama に参加予定です。


Tech・Ed 2007 Yokohama
開催日2007/08/21(火)~24(金)
場所パシフィコ横浜

私は、以下の二つに登壇予定です。
コミュニティらしい和気藹藹としたセッションにしたいと思います。どうぞ奮ってご参加ください。

Birds of a Feather in Yokohama

Usergroup Street Live!

尚、こみゅぷらすの運営スタッフが登壇するその他のセッションは以下の通りです。

Usergroup Street Live!


2007年08月12日

Microsoft Tech・Ed 2007 in Yokohama

以下の通り Tech・Ed に参加予定。
Microsoft Tech・Ed 2007 in Yokohama
期間8月21日(火)~8月24日(金)
会場名称パシフィコ横浜
住所〒220-0012 横浜市西区みなとみらい1-1-1
電話045-221-2155
8月21日(火)
TIMEROOMセッション IDセッション タイトル
10:30 - 12:10A
オープニング & キーノート
「Dynamic IT for the People-Ready Business」マイクロソフト株式会社 ダレン ヒューストン
マイクロソフト コーポレーション イアン マクドナルド
13:50 - 15:05A
T3-302
Visual Studio 2008 概要近藤 和彦
15:05 - 15:25会議センター 3F
Street Live!
VB6 on Vista + Vista 時代のモバイル開発環境構築codeseek
衣川 朋宏 (tkinugaw) / 岡本 貴浩 (こた) / 原 敬一
15:25 - 16:40会議センター 3F
BOF09
今改めて語り合いたい。オブジェクト指向プログラミングをマスタするコツこみゅぷらす
小島 富治雄 / 尾崎 義尚 / 原 敬一
16:40 - 17:00会議センター 3F
Street Live!
わかっていますか? 女性のキモチ。~ 事例に見る “こんな時どうする” ~eパウダ~
村岡 淳子 / 山田 真由美 / 藤城 さつき
17:00 - 18:15会議センター 3F
BOF06
ソフトウェア開発の問題解決のための開発者コミュニティの活用方法についてINETA Japan
小井土 亨 / 小野 修司 / 奥津 和真
18:15 - 18:35会議センター 3F
Street Live!
マイクロソフト テクノロジの歴史を知ろうこみゅぷらす
尾崎 義尚 (om (takanao)) / 小島 富治雄 (Fujiwo)
8月22日(水)
TIMEROOMセッション IDセッション タイトル
9:30 - 10:45C
T1-403
Windows Communication Foundation のアーキテクチャと基本的な拡張手法アバナード株式会社
福井 厚
アバナード株式会社
木下 秀義
10:45 - 11:05会議センター 3F
Street Live!
CTP ってなに? どう使うとうれしいの? CTP の魅力について語ろうcodeseek
大橋 宏章 (NAL-6295) / 浜津 信弘 (deep) / 原 敬一
11:05 - 12:20B
T3-405
AJAX に対応した ASP.NET アプリケーション開発NRIラーニングネットワーク株式会社 矢嶋 聡
12:20 - 12:40会議センター 3F
Street Live!
CTP ってなに? どう使うとうれしいの? CTP の魅力について語ろうこみゅぷらす
杉山 洋一 (かるあ) / 亀川 和史 (めさいあ (kkmaegawa))
15:05 - 15:25会議センター 3F
Street Live!
Visual Basic 変数名 100 連発Tips of VB.NET
吉原 優一 (Elfaria) / 杉山 洋一 (かるあ)
15:25 - 16:40会議センター 3F
BOF04
DI と AOP による業務アプリケーション開発 - Seasar.NET杉本 和也 / 青木 淳夫 / 福井 厚
17:00 - 18:15会議センター 3F
BOF02
ソフトウェア ファクトリ BOF ~ソフトウェア開発の明日はどっちだ!?酒井 達明 / 森屋 英治 / 福井 厚 / マイクロソフト株式会社 DPE/エバンジェリスト 野村 一行
18:35 - 20:15パシフィコ横浜 展示ホール BCommunity & Networking Tech・Ed 2007 Attendee Party
INETA Japan ブース
8月23日(木)
TIMEROOMセッション IDセッション タイトル
11:00 - 12:00
INETA Japan リーダー・ミーティング
12:20 - 12:40会議センター 3F
Street Live!
C++、C++ / CLI、C# 適材適所の BOF の紹介えムナウのプログラミングのペー
児玉 宏之 (えムナウ) / 高萩 俊行 (とっちゃん) / 福田 文紀 (επιστημη)
12:20 - 13:50ヨコハマ グランド インターコンチネンタルホテル 3F ボールルーム / パシフィック / ベイビューCommunity & NetworkingPeerTalk Lunch
13:30 - 13:50会議センター 3F
Street Live!
ダイナミック言語のおもしろさわんくま同盟
中 博俊 / 大沼 安正 (usay) / 松下 和貴 (恣意の)
15:25 - 16:40会議センター 3F
BOF14
.NET Framework 3.0 をこう使いたいわんくま同盟
中 博俊 / 露木 敏博 / 森 博之
17:00 - 18:15会議センター 3F
BOF07
C++、C++ / CLI、C# 適材適所えムナウのプログラミングのペー
児玉 宏之 / 高萩 俊行 / 福田 文紀
20:00 - 20:30
INETA Japan リーダー・ミーティング
 - 
INETA Japan 懇親会
8月24日(金)
TIMEROOMセッション IDセッション タイトル
9:30 - 10:45会議センター 3F
BOF17
VSUG オフライン フォーラムVSUG
藤城 さつき / 社本 明弘 / 小野 雄太郎
10:45 - 11:05会議センター 3F
Street Live!
コミュニティへの参加とそのベネフィットCulminis
デイブ サンダース / サンジェイ シェティ
11:05 - 12:20A
T3-301
.NET における動的言語への取り組み荒井 省三
13:30 - 13:50会議センター 3F
Street Live!
ガジェットで VSUG しよう! ~ <VSUG Watcher>ガジェットのご紹介 ~VSUG : Visual Studio ユーザーグループ
井上 章
15:05 - 15:25会議センター 3F
Street Live!
ソフトウェア開発の自働化について日本 XP ユーザ会 (XPJUG)
小井土 亨 / 福井 厚
15:25 - 16:40D
T3-403
ジニアス平井の Visual Studio Team System のススメジニアス平井 (平井 昌人)
16:40 - 17:00会議センター 3F
Street Live!
Windows Vista の隠れた (?) 目玉、IIS 7.0 を使おうASP++
高須 淳一 (Moo) / 長田 直樹 (ナオキ)
19:30 - 
第三回平井さんを囲む会

2007年08月20日

Tech・Ed 2007 in Yokohama

bnr_teched_2007_120-90.gif
明日からいよいよ Tech・Ed Yokohama に参加予定です。

Tech・Ed 2007 Yokohama
開催日: 2007/08/21(火)~24(金)
場所: パシフィコ横浜

私は、主に以下の二つに登壇予定です。どうぞ奮ってご参加ください。

Birds of a Feather in Yokohama

・8/21(火) 15:25-16:40
BOF09: 『今改めて語り合いたい。オブジェクト指向プログラミングをマスタするコツ』

Usergroup Street Live!

こみゅぷらす
・8/21(火) 18:15-18:35
『マイクロソフト テクノロジの歴史を知ろう』

尚、こみゅぷらすの運営スタッフが登壇するその他のセッションは以下の通りです。

こみゅぷらす
・8/22(水) 12:20-12:40
『CTP ってなに? どう使うとうれしいの? CTP の魅力について語ろう』

codeseek
・8/21(火) 15:05-15:25
『VB6 on Vista + Vista 時代のモバイル開発環境構築』
・8/22(水) 10:45-11:05
『「読みやすいコード」とは?』

ASP++
・8/24(金) 16:40-17:00
『Windows Vista の隠れた (?) 目玉、IIS 7.0 を使おう』

Tips of VB.NET
・8/22(水) 15:05-15:25
『Visual Basic 変数名 100 連発』

また、その他関連コミュニティの BoF は以下の通りです。こちらにも顔を出したいと考えております。

アイネタ ジャパン
・8/21(火) 17:00-18:15
BOF06: 『ソフトウェア開発の問題解決のための開発者コミュニティの活用方法について』

VSUG (Visual Studio User Group)
・8/24(金) 09:30-10:45
BOF17: 『VSUG オフライン フォーラム』

2007年08月28日

Tech・Ed 2007 in Yokohama

※ 「Microsoft Tech・Ed 2007 in Yokohama」 の続き。

以下の通り Tech・Ed に参加した。

Microsoft Tech・Ed 2007 in Yokohama
期間8月21日(火)~8月24日(金)
会場名称パシフィコ横浜
住所横浜市西区みなとみらい1-1-1
DAY TIME ROOM セッション ID セッション タイトル スピーカー
8月21日(火) - 15:25 控え室
こみゅぷらす BoF 準備
15:25 - 16:40会議センター 3F BOF09今改めて語り合いたい。オブジェクト指向プログラミングをマスタするコツこみゅぷらす
小島 富治雄 / 尾崎 義尚 / 原 敬一
16:40 - 17:00会議センター 3F Street Live!わかっていますか? 女性のキモチ。~ 事例に見る “こんな時どうする” ~eパウダ~
村岡 淳子 / 山田 真由美 / 藤城 さつき
17:00 - 18:15 控え室  こみゅぷらす Street Live! 準備
18:15 - 18:35会議センター 3F Street Live!マイクロソフト テクノロジの歴史を知ろうこみゅぷらす
尾崎 義尚 (om (takanao)) / 小島 富治雄 (Fujiwo)
18:15 - 20:00 控え室   Community Happy Hour
8月22日(水) 9:30 - 10:45C T1-403Windows Communication Foundation のアーキテクチャと基本的な拡張手法アバナード株式会社
福井 厚 / 木下 秀義
10:45 - 11:05会議センター 3F Street Live! 「読みやすいコード」とは? codeseek
大橋 宏章 (NAL-6295) / 浜津 信弘 (deep) / 原 敬一
11:05 - 12:20B T3-405AJAX に対応した ASP.NET アプリケーション開発NRIラーニングネットワーク株式会社 矢嶋 聡
12:20 - 12:40会議センター 3F Street Live! CTP ってなに? どう使うとうれしいの? CTP の魅力について語ろうこみゅぷらす
杉山 洋一 (かるあ) / 亀川 和史 (めさいあ (kkmaegawa))
12:30 - 13:40   スペシャル セッションマイクロソフトの SOA と .NET Framework 3.X の適用についてMicrosoft Corporation Vittorio Bertocci
14:00 - 15:00 控え室  VSUG BoF 打ち合わせ
15:05 - 15:25会議センター 3F Street Live!Visual Basic 変数名 100 連発Tips of VB.NET
吉原 優一 (Elfaria) / 杉山 洋一 (かるあ)
15:25 - 16:40会議センター 3F BOF04DI と AOP による業務アプリケーション開発 - Seasar.NET杉本 和也 / 青木 淳夫 / 福井 厚
17:00 - 18:15会議センター 3F BOF02ソフトウェア ファクトリ BOF ~ソフトウェア開発の明日はどっちだ!?酒井 達明 / 森屋 英治 / 福井 厚 / マイクロソフトト 野村 一行
18:35 - 20:15パシフィコ横浜 展示ホール B Community & Networking Tech・Ed 2007 Attendee Party
Ask The Communiry Experts!
8月23日(木) 9:30 - 10:45 B オープニング & キーノートアプリケーション アーキテクトを目指すあなたに贈るスペシャル セッション
「LINQ to the Future ~ LINQ が創る次世代型データ処理技術 ~」
マイクロソフト株式会社 赤間 信幸
11:00 - 12:00    INETA Japan リーダー・ミーティング
12:20 - 13:50 ヨコハマ グランド インターコンチネンタルホテル 3F Community & Networking PeerTalk Lunch
13:30 - 13:50会議センター 3F Street Live! ダイナミック言語のおもしろさわんくま同盟
中 博俊 / 大沼 安正 (usay) / 松下 和貴 (恣意の)
13:50 - 15:05 Hands-on2 H-311 ASP.NET AJAX による Web アプリケーション開発概要 NRIラーニングネットワーク株式会社 矢嶋 聡
15:25 - 16:40   スペシャル セッション  Microsoft Corporation 波村 大悟
17:00 - 18:15会議センター 3F BOF07 C++、C++ / CLI、C# 適材適所えムナウのプログラミングのペー
児玉 宏之 / 高萩 俊行 / 福田 文紀
18:35 - 19:50E T4-405 Microsoft SharePoint Products and Technologies 2007 におけるワークフロー : 開発者向け詳説 マイクロソフト株式会社 松崎 剛
20:00 - 21:00     INETA Japan リーダー・ミーティング
21:00 -     INETA Japan 懇親会
8月24日(金) 9:30 - 10:45会議センター 3F BOF17VSUG オフライン フォーラムVSUG : Visual Studio ユーザーグループ
藤城 さつき / 社本 明弘 / 小野 雄太郎
10:45 - 11:05会議センター 3F Street Live!コミュニティへの参加とそのベネフィットCulminis
佐藤 千佳
11:05 - 12:20A T3-301.NET における動的言語への取り組み マイクロソフト株式会社 荒井 省三
13:30 - 13:50会議センター 3F Street Live!ガジェットで VSUG しよう! ~ <VSUG Watcher>ガジェットのご紹介 ~VSUG : Visual Studio ユーザーグループ
井上 章
13:50 - 15:05Hands-on1 H-208私もできた! Microsoft Office SharePoint Server 2007 の標準機能の活用  
15:05 - 15:25会議センター 3F Street Live!ソフトウェア開発の自働化について日本 XP ユーザ会 (XPJUG)
小井土 亨
15:25 - 16:40D T3-403ジニアス平井の Visual Studio Team System のススメ マイクロソフト株式会社 ジニアス平井 (平井 昌人)
16:40 - 17:00会議センター 3F Street Live!Windows Vista の隠れた (?) 目玉、IIS 7.0 を使おうASP++
高須 淳一 (Moo) / 長田 直樹 (ナオキ)
17:00 - 18:15Hands-on2 H-309LINQ を活用したデータ アクセス プログラミング NRIラーニングネットワーク株式会社 矢嶋 聡
19:30 - 横浜中華街  第三回平井さんを囲む会

2007年08月29日

私が日本の MVP ~コミュニティの達人~

マイクロソフトの「私が日本の MVP ~コミュニティの達人~」というサイトに載りました。

2007年09月12日

コミュニティ勉強会に参加しよう~第 1 回・東京編 (アイネタ ジャパン & マイクロソフト共催)

以下のイベントに参加する。三つ目のセッションを担当予定。

コミュニティ勉強会に参加しよう~第 1 回・東京編 (アイネタ ジャパン & マイクロソフト共催)
日時 9月29日(土) 13:00 -18:00
場所 マイクロソフト株式会社 新宿本社 小田急サザンタワー 5F セミナールーム
主催 アイネタ ジャパンマイクロソフト株式会社
参加費 無料 (事前登録制)
セッション内容及び申込み こちら

2007年09月14日

NET 関連情報 Web サイト

.NET 関連情報 Web サイト

2007年09月18日

LINQ to SQL

Chica's Blog - ScottGuさんのブログ翻訳 より 「LINQ to SQL」

2007年09月19日

REMIX07 TOKYO

今日は、以下に参加する。

REMIX07 TOKYO
日時 9/19(水) 10:00~18:15 (REMIX Night: 参加者パーティー 18:15~20:00)
場所 東京国際フォーラム
主催 マイクロソフト株式会社
参加費 カンファレンス ¥5,000、展示 無料
セッション内容 こちら

2007年10月01日

コミュニティ勉強会 (アイネタ ジャパン & マイクロソフト共催)

以下に参加した。

コミュニティ勉強会に参加しよう~第1回・東京編 (アイネタ ジャパン & マイクロソフト共催)』 (2007/09/29)
DSCF2200.jpg DSCF2198.jpg
雨の中、約70名の方が参加された。こうしたイベントには初めて参加される方も多かったようだ。

セッションは以下の三つ: また、セッション後には同じ会場での懇親会も行われた。
多くの参加者の方がその儘参加され、和気藹々と交流が行われた。

コミュニティ イベントとして、とても良いイベントだったように思う。

関連ブログ:

2007年10月10日

Sandcastle - September 2007 Community Technology Preview (CTP)

"Sandcastle" は、、ソースコードの XMLコメントから、ドキュメントを自動生成してくれるツールで、同様のツールであり .NET 2.0 への対応をやめてしまった NDoc の後継だ。

MSDN のような HTML Help (*.chm) を作ることができ、.NET 2.0に対応している。

コマンドライン ツールであるが、"Sandcastle Help File Builder" のような GUI もある。

"Sandcastle" の最新版である "September 2007 Community Technology Preview (CTP)" が 10/1 に、出、10/4 に "Sandcastle Help File Builder" の最新版である "1.6.0.0" が出た。

早速使ってみた。

  1. "Sandcastle" と "Sandcastle Help File Builder" の最新版をダウンロード。
  2. "Sandcastle - September 2007 Community Technology Preview (CTP)" をインストール。(*1)
  3. デフォルトのパス (*2) に "Sandcastle Help File Builder (1.6.0.0)" をインストール。
  4. 「スタートメニュー」-「すべてのプログラム」-「Sandcastle Help File Builder」-「Build Reflection Data (first use)」を実行。 (*3)
  5. 「ビルドに成功しました。」と表示されるまで、かなり時間が掛かるが待つ。
    sandcastlectp1.jpg
  6. Visual Studio のプロジェクトのプロパティで「ビルド」-「出力」-「XML ドキュメント ファイル」をオンにする。
    sandcastlectp2.jpg
  7. Visual Studio からプロジェクトをビルド。
  8. 「スタートメニュー」-「すべてのプログラム」-「Sandcastle Help File Builder」-「Sandcastle Help File Builder GUI」を実行。
    sandcastlectp3.jpg
  9. Sandcastle Help File Builder の "Project Properties" の "Paths" の "OutputPath" を確認 (デフォルトでは ".\Help\")。
  10. Sandcastle Help File Builder の "Project Properties" の "Help File" の "HtmlHelpName" を確認 (デフォルトでは "Documentation")。(*4)
  11. Sandcastle Help File Builder の "Assemblies to Document" の "Add" でドキュメントを出力したいアセンブリを追加。
  12. Sandcastle Help File Builder のメニューから "Documentation" の "Build Project" を実行。
  13. ビルドが成功したことを確認。(*5)
    sandcastlectp4.jpg
  14. MSDN 形式のヘルプファイルが作成されていることを確認 (デフォルトでは ".\Help\Documentation.chm")。(*6)
    sandcastlectp5.jpg

(*1) 古いバージョンの Sandcastle がインストールされていた場合は、先にアンインストールする必要がある。
(*2) デフォルトのパスにインストールしないと 4 の手順に失敗する。
(*3) Vista の場合、権限が足りなくて実行に失敗することがある。その場合は、"Sandcastle Help File Builder" をインストールしたフォルダ内の "BuildReflectionData.bat" を「管理者として実行」する。
(*4) 新バージョンでは、"Project Properties" の "Help File" の "Language" の選択肢に「日本語(日本)」が出て来ない。
(*5) この時 public なクラスがなかったり XML コメントがないと、ビルドエラーになる。
(*6) 日本語に十分対応していないため、左側のペインのフォントの設定が不適切で文字化けを起こすことがある。

続きを読む "Sandcastle - September 2007 Community Technology Preview (CTP)" »

2007年10月17日

Microsoft On

DSCF2603.JPG
本日社内で開催。

Microsoft On - 出張ワークショップ
『開発者テストからはじめるチーム開発の実践 Visual Studio Team System の活用エッセンス』

講師: マイクロソフト デベロッパーエバンジェリスト 長沢 智治氏
開催日時: 2007年10月17日 13時00分~16時00分
受講者数: 31名

まとまっていて、とても分かり易いお話だった。感謝。

印象に残った点や思ったことを以下に箇条書き。

  • プレゼンで使用していた拡大ツールがとても良さげだったので教えていただいた
  • Visual Studio 2008 で単体テストが随分軽くなり、リズムカルな開発が可能になった
  • Visual Studio 2008 は機能アップもさることながら、使い勝手が軽快になったことが嬉しい
  • Team Foudation Server 2008 へのマイグレーションについて
  • 変更セットってのは開発におけるトランザクション
  • 改善活動では、原因療法をやらず対症療法をやってる人ほど「うちではその方法は使えない」と言う
  • Team Foundation Server のガイドが翻訳される件について

2007年11月13日

VSUG DAY 2007 Winter

VSUG Day 2007 Winter
東京会場 日付 2007年12月8日(土)
会場 ベルサール西新宿 1Fホール
大阪会場 日付 2008年1月19日(土)
会場 新梅田研修センター
会費 無料(VSUG 会員登録が必要となります)

2007年11月14日

マイクロソフト デベロッパー フォーラム

先週の金曜日、以下のイベントに参加してきた。

『マイクロソフト デベロッパー フォーラム -Microsoft's 2020 vision of technology-』
日時 2007年11月9日(金) 11時15分~13時30分
会場 ホテル ニューオータニ 東京

会場には、マイクロソフトの人たちや、Microsoft MVP 等の開発者、学生、プレスの方など何十人かの人たちが集まっていた。

やがて、明るい照明のもと、本日の主役であるマイクロソフト最高経営責任者 (CEO) スティーブ バルマー氏が登場した。

DSCF2786.jpg

そして彼のプレゼンテーションが始まった。"Microsoft's 2020 vision of technology" ということで、「Software + Services」をメイン テーマに、今後十年位のマイクロソフトのビジョンが語られた。

その後は、質問タイムとなった。予め寄せられた質問やその場での質問にバルマー氏が答えてくれるのだ。

DSCF2785.jpg

私も、その場で思い付いた質問をしてみた。「IT技術者はこれから、多くの多能工が必要とされていくのか、それとも分業化が益々進むのか、どちらだと考えているか?」というような質問をした (勿論日本語で)。バルマー氏が答えてくれた。「分業化が進むだろう…」(勿論英語で)。

答えてくれている数分間、彼とはずっと目が合った儘だった。バルマー氏の話を聴くのはこれが三回目だろうか。でもこんなに間近で見つめ合ったのは初めてだ。目力 (めぢから) を感じたというか、なんとも迫力があった。とてもエネルギッシュに話す人だ。

というわけで、生バルマーを堪能した。ただ、一ドル札に氏のサイン (謎) をもらえなかったのがちょっと残念だった。

その後、インタビューを受けたり、懇親会でマイクロソフトの人や他の MVP の人と話をしたりした。

尚、このイベントの模様は、近日以下の場所でWebキャストにて公開されるそうだ。ちょっと楽しみだ。

2007年11月28日

MSDN オフラインセミナー「.NET Framework における次世代データアクセステクノロジー概要」[北陸編]

以下に参加してきた。

dc112805.JPG

MSDN オフラインセミナー
「.NET Framework における次世代データアクセステクノロジー概要」[北陸編]
日時 2007年11月28日
13:30~16:30
場所 住所 石川県金沢市 金沢パークビル 11F
会場 マイクロソフト株式会社 北陸支店 セミナールーム
講師 マイクロソフト株式会社 デベロッパーエバンジェリスト 小高太郎氏

dc112809.JPG dc112818.JPG

■ 内容と気になった言葉

  • ADO.NET 概要
    • データ プロバイダ
    • 二つの接続形態
      • 接続型
      • 非接続型
        • DataSet
        • 型付き DataSet
        • データバインド
        • TableAdapterManager
  • ADO.NET の進化
    • LINQ関連
      • LINQ to SQL等
    • Entity Data Model (EDM)
  • LINQ to SQL
    • 「LINQ はデータベースを扱うものではなく、オブジェクトを扱うもの」
    • 遅延評価
    • DataContext
    • DML (SQL) のサポート
    • デザイナー
      • "LINQ to SQL Classes" の追加
      • Table の Drag & Drop
    • 「Select の本質は『射影』だよ」
    • LEFT OUTER JOIN のデモ
    • LinqDataSource
  • LINQ to Object
    • IEnumerable<T>
    • 「Where、OrderBy、Select の部分はパイプ処理だよ」
    • 『埋め込みクエリ方式』と『拡張メソッド方式』
    • LINQ to Object と LINQ to SQL の JOIN
    • LINQ to DataSet
  • Entity Data Model (EDM)
    • 論理データモデル (DOA のモデルと OO のモデル) と概念データモデル (EDM)
    • スキーマ記述言語
      • CSDL (概念スキーマ定義言語 )、MSL (マッピングスキーマ言語 )、SSDL (ストアスキーマ定義言語)
    • "Entity Designer"
    • "ADO.NET Entity Framework Beta2" と "ADO.NET Entity Framework Tools CTP"
    • デザイナー
      • "ADO.NET Entity Data Model" (edmx ファイル) の追加
      • DSL (モデル駆動開発のための)
  • ADO.NET Entity Framework
    • EDM に対するクエリ
    • EntityClient
      • Entity SQL (Transact SQL に似て非なるもの)
        • DML クエリは未だない
    • Object Services
      • Entity SQL
      • QueryBuilder
      • LINQ to Entity
  • ガイダンスとまとめ
    • 三つの方式
      • 接続型/非接続型
        • Agile
      • LINQ to SQL
        • 中間型
        • Visual Studio 2008 でサポート
      • Entity Framework
        • Enterprise
        • Visual Studio 2008 への追加機能
          • 2008年前半位に?
    • LINQ
      • 「様々なデータソースへの統括的なアクセス方法」
    • 概念レベルのDBアプリケーション
    • "Easy of Development"

■ 所感

久々に訪れた金沢。新しくできたマイクロソフト株式会社 北陸支店を初めて訪れた。

素晴らしいセミナーだった。

小高氏の話は、技術の背景から技術の要点、そして適用範囲まで、とても纏まっていて、技術の本質を見ることのできる分かりやすいものだった。また、沢山のデモがあって、かなり具体的に知ることができた。

Tech・Ed でも同内容を扱ったセッションは結構あったが、今回はそれをより深いところまで学ぶことができたようだ。

新しい ADO.NET について包括的に理解することができたように思う。

特に概念データモデルに関して、

  • 概念データモデルを作る
  • 概念データモデルとデータベースの論理モデルのマッピングを行う
  • 概念データモデルに対するクエリを LINQ (to Entity) で書く

とすることで、随分すっきりと「意図したことだけが書ける」ものだと感嘆したし、この一連のデモが目の前でいとも簡単に動いたときには深い感動を覚えた。

概念データモデルの考え方は、私が初めてデータベース プログラミングを学んだときに感じた、「ロジック部分の設計モデルとデータベースの設計モデルとの間を埋めるものが経験から来る暗黙知のようなものでしかないことからくる違和感」を解消してくれた気がした。

そしてまた、この概念モデルを書くための DSL (ドメイン特化言語) が追加され、それをまたサポートするツールが追加されたことが面白いと感じた。 用途に応じて DSL を使い分ける、ということの意図が段々と判ってきたような気がした。

余談だが、これらのDSL は右脳人間向きかも知れない、とか思った。

モデルを、より包括的に且つよりグラフィカルに表現するのは、左脳人間より右脳人間の方が得意とするところかも知れない。とすると、これまでのプログラミング言語は論理の組み立ての得意な左脳人間が操るものだったのが、今後の幾つかの新たなパラダイムから来ている言語に関しては、寧ろ右脳人間が得意とするようになるのかも知れない。

帰りに、「Power To The PRO」と書かれたカップヌードルRefillを頂いた (二個目)。

dc112829.JPG

dc112826.JPG

2007年12月01日

こみゅぷらす いちねん

1stAnniversary.PNG

こみゅぷらすスタートから今日で一年。新しい年度がスタートした。

運営スタッフは、大分増えて、現在 14名。

やったこと (オフライン):

今年度も色々とゆるい感じでやることになりそう。

2007年12月03日

マイクロソフト デベロッパー フォーラムの模様公開

※ 「マイクロソフト デベロッパー フォーラム」続き。 11月9日にスティーブ バルマー氏が来日して行われたマイクロソフト デベロッパー フォーラムの模様がビデオ等で公開された。 私は、以下の二箇所に:

2007年12月13日

マグカップ

なんか届いた。
DSCF3269.JPG

2007年12月14日

Visual Studio 2008 日本語版

早速ダウンロード。

慌てて、『VS 2008 最終リリースのインストール前にVS 2008ベータ2をアンインストールする方法』をやって、インストール。

また、"Microsoft Silverlight Tools Alpha for Visual Studio 2008 Beta 2" をアンインストールして、"Microsoft Silverlight 1.1 Tools Alpha for Visual Studio 2008" をダウンロード。

参考:


2007年12月25日

第23回 codeseek勉強会 & こみゅぷらすと codeseek の忘年会 2007

第23回 codeseek勉強会 & こみゅぷらすと codeseek の忘年会 2007」への参加を、ざわざわニュースで伝えてみるテスト。

2008年01月21日

VSUG DAY 2007 Winter 大阪

日帰りで、「VSUG DAY 2007 Winter 大阪」に行ってきた。
dc012004.JPG dc012018.JPG dc012006.JPG

飛び入りで Silverlight のライトニングト-クスまでやらせてもらった。

懇親会と二次会にも出て、幸せな時を過ごせた。参加して良かった。


# 行きは高速バス、帰りは「急行ゆきぐに」で帰ってきたのだが、乗り過ごしたりして、往復に東京との往復に通常掛かる時間の4倍位掛かってしまった。距離でいうと半分位の筈なのだが。

2008年01月30日

Source Code Outliner PowerToy for Visual Studio 2008

「Source Code Outliner PowerToy for Visual Studio 2008」が公開されたので試した。

下記からダウンロードしてインストールすることで、Visual Studio の拡張機能として使うことができる。

開いているソースコードの概要 (クラスやメソッド、プロパティ等) がツリー状に表示される。
Source Code Outliner PowerToy for Visual Studio 2008

  • Visual Studio 2008 用
  • C#/Visual Basic 用 (C++ネイティブやC++/CLI は非対応)
  • ソースコードのカーソル位置にあたる部分がハイライトし、現在位置を示す
  • Source Code Outliner 側でクラスやメソッド等を選択すると、ソースコードのその箇所にジャンプすることができる

ソースコードの全体での現在位置を把握しながら、あちこちにジャンプしながら閲覧したり編集したりするのには、便利だと感じた。

2008年01月31日

.NET Framework ライブラリのソースコードが利用可能に

.NET Framework ライブラリのソース コードが部分的に利用可能になったのでやってみた。

これは、Visual Studio 2008 からデバッグ時に .NET Framework ライブラリのソース コードが参照できるようになったものだ。

詳しい手順は、以下に載っている (英語)。

上記の手順によれば、先ず、Visual Studio 2008 QFE という Hotfix をインストールすることになっている。

だが、これは「このソフトウェア更新の対象製品はこのコンピュータにインストールされていません。」というエラーが出てできなかったのでやらなかった。
Source Code Outliner PowerToy for Visual Studio 2008

以降、手順通りでうまく行った。

  1. メニュー -「ツール」-「オプション」でオプションのダイアログを開き、「デバッグ」-「全般」で、
    1. 「'マイ コードのみ'設定を有効にする」をオフ
      Source Code Outliner PowerToy for Visual Studio 2008
    2. 「ソース サーバー サポートを有効にする」をオン
      Source Code Outliner PowerToy for Visual Studio 2008
  2. オプションのダイアログの「デバッグ」-「シンボル」
    Source Code Outliner PowerToy for Visual Studio 2008
    1. 「シンボル ファイル (.pdb) の場所」に新規で "https://referencesource.microsoft.com/symbols" を追加
    2. 「シンボル サーバーからシンボルをキャッシュするディレクトリ」として適当な (存在する) ディレクトリを指定
    3. 「シンボルが手動で読み込まれるときのみ上記の場所を探す」は、ここではオンにしておく
      Source Code Outliner PowerToy for Visual Studio 2008
  3. デバッグを開始する。
    Source Code Outliner PowerToy for Visual Studio 2008
  4. 適当なところで、
    1. メニュー-「デバッグ」-「ウィンドウ」-「呼び出し履歴」から「呼び出し履歴」ウィンドウを表示する (Ctrl+Alt+C でも可)
    2. 「呼び出し履歴」の中の .NET Framework 部分に当たる適当な行で右クリックし、ポップアップ メニューから「シンボルの読み込み」をクリック
      Source Code Outliner PowerToy for Visual Studio 2008
    3. ライセンスに関する規約に同意するかどうか聞かれるので、"Yes" と答える
      Source Code Outliner PowerToy for Visual Studio 2008
    4. 読み込みに成功すると、「呼び出し履歴」のその行をダブル クリックすることで、.NET Framework のソースコードにジャンプできるようになる
  5. 次に Ctrl+Alt+U で「モジュール」ウィンドウを開く
    • 「モジュール」の中の適当な行を右クリックし、ポップアップ メニューから「シンボルの読み込み」をクリック
      Source Code Outliner PowerToy for Visual Studio 2008
      Source Code Outliner PowerToy for Visual Studio 2008
  6. ソースコードの .NET Framework の呼び出しのところで、ステップ イン (F11) をすると、シンボルが読み込み済みのモジュールであればそのソースにステップ インできる
    Source Code Outliner PowerToy for Visual Studio 2008
  7. 毎度手動でシンボルを読み込む場合は、上記の通りであるが、オプションのダイアログの「デバッグ」-「シンボル」-「シンボルが手動で読み込まれるときのみ上記の場所を探す」をオフにしておけば、キャッシュ用のディレクトリにシンボルが自動で読み込まれ、必要なときはそこを参照するようになるため、毎度手動でシンボルを読み込む手間が省ける。
    Source Code Outliner PowerToy for Visual Studio 2008

2008年02月01日

亞剌比亞數字の大字への變換

大字への變換の Sample Code を書いてみた。

例.

  • "0" → ""
  • "12,345" → "壹萬貳阡參佰肆拾伍"
  • "987,654,321" → "玖億捌阡柒佰陸拾伍萬肆阡參佰廿壹"
  • "122,090,654,002,030" → "壹佰廿貳兆玖佰陸億伍阡肆佰萬貳阡卅"
  • "34,500,000,000,128,034" → "參京肆阡伍佰兆壹拾貳萬捌阡卅肆"
  • "10,000,000,000,000,000,000" → "壹阡京"
// Source Code by C#
static string 大字への變換(string 亞剌比亞數字)
{
    亞剌比亞數字           = 亞剌比亞數字.Replace(",", "");
    ulong 數;
    if (!ulong.TryParse(亞剌比亞數字, out 數))
        throw new ArgumentException();
    var   數表現           = new[] { "", "壹", "貳", "參", "肆", "伍", "陸", "柒", "捌", "玖" };
    var   小位表現         = new[] { "", "拾", "佰", "阡" };
    var   大位表現         = new[] { "", "萬", "億", "兆", "京" };
    var   大位表現使用濟み = new[] { false, false, false, false, false };
    var   大字             = string.Empty;
    for (var 位 = 0; 數 != 0ul; 數 /= 10ul, 位++) {
        var 或る位の數 = (int)(數 % 10ul);
        if (或る位の數 != 0) {
            var 大位 = 位 / 4;
            if (大位 >= 大位表現.Length)
                throw new OverflowException();
            if (!大位表現使用濟み[大位]) {
                大字                   = 大位表現[大位] + 大字;
                大位表現使用濟み[大位] = true;
            }
            大字 = 數表現[或る位の數] + 小位表現[位 % 4] + 大字;
        }
    }
    return 大字.Replace("貳拾", "廿").Replace("參拾", "卅");
}

2008年03月03日

『C#2.0&3.0勉強会』in 福井

以下の無料勉強会を福井でやります。奮ってご参加ください。

C#2.0&3.0勉強会
開催日 2008年3月12日(水)
開催時間 13:30~17:30 (受付開始 13:00)
会場 ふくい産業支援センター ふくい産業支援センター パソコン実習室B
参加費 無料
主催 福井情報技術者協会 (FITEA)
内容 C#2.0 や 3.0 で追加された最新言語仕様を用いて、品質が高く保守しやすいきれいなコードを書くための技術を学びます (例: yield、ラムダ式、クロージャ、LINQ等)。備え付けのPCを使った実習も予定しています。
詳細/申込先 http://fitea.org/?p=74

コミュニティ勉強会に参加しよう~第4回・福井編

以下の無料勉強会が福井で開催されます。奮ってご参加ください。

InetaStudyLogo4.jpgコミュニティ勉強会に参加しよう~第4回・福井編
開催日 2008年3月29日(土)
開催時間 13:00~18:00 (受付開始 12:30)
会場 福井県中小企業産業大学校 特別教室 (福井県福井市下六条町16-15)
参加費 無料
主催/共催 アイネタ ジャパンマイクロソフト/福井情報技術者協会 (FITEA)
内容 コミュニティ支援団体であるNPO法人アイネタ ジャパンとマイクロソフトが共催で勉強会を開催します。2007 年東京、福岡、2008 年は岡山に引き続き 4 回目の開催となります。講師には各コミュニティで活躍されている方とマイクロソフトのエバンジェリストが登場し、最新製品・テクノロジに関する情報やいま話題のトピックなど、通常のマイクソロフト イベントとは違った視点から解説します。
詳細/申込先 http://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032371939&Culture=ja-JP

2008年03月21日

コミュニティ勉強会に参加しよう ~第4回・福井編

28646999_80.jpg

福井で、.NET の無料セミナーを開催します。奮ってご参加ください。

  • 福井県内外からマイクロソフトのエバンジェリストや Microsoft MVP が多数スピーカーとして参加します。
  • 参加費無料です。
  • 懇親会で軽食の提供があります (無料)。
  • 抽選で、NetAdvantage for WPFNetAdvantage for .NET (正規版) が当たります (登録のため当日名刺が必要)。
コミュニティ勉強会に参加しよう ~第4回・福井編
主催/共催 アイネタ ジャパンマイクロソフト福井情報技術者協会 (FITEA)
内容 コミュニティ支援団体であるNPO法人アイネタ ジャパンとマイクロソフトが共催で勉強会を開催します。2007年東京、福岡、2008年岡山に引き続き4回目の開催となります。講師には各コミュニティで活躍されている方とマイクロソフトのエバンジェリストが登場し、最新製品・テクノロジに関する情報やいま話題のトピックなど、通常のマイクソロフト イベントとは違った視点から解説します。
詳細/申込先 http://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032371939&Culture=ja-JP

※ ライトニング トークス参加者募集!

FITEA の担当セッションで、5分間のトークス (.NET 関係ならなんでも O.K.) をして頂ける方を募集します。
ご協力頂ける方・興味のある方は、info@fitea.org までご連絡ください。

2008年04月12日

2008 Microsoft MVP Summit (其零)

dc041202.JPG
明日より 2008 Microsoft MVP Summit に参加すべくシアトル及びレドモンドのマイクロソフト本社へ。

# ちなみにチベット仏教最高指導者ダライ・ラマ14世も今シアトルに滞在中の筈。

2008年04月24日

2008 MVP Global Summit 参加レポート

VSUG にコラムとして書きました。

2008 MVP Global Summit 参加レポート

以前 VSUG にコラムとして書いたものをここで公開します。

はじめに

皆様こんにちは。春暖の候如何お過ごしでしょうか。

新人の方が入ってこられ、活気があふれている職場も多いかと想像しています。

さて今回は、私が先週参加してまいりました "2008 MVP Global Summit" についてご紹介したいと思います。

2008 MVP Global Summit
参加者 Microsoft MVP
※ Microsoft MVP についてご存じない方は、こちらをご参照ください。
開催期間 2008年4月14日(月)~17日(木)
場所 米国ワシントン州
   

2008 MVP Global Summit というのは、年に一度位の頻度でマイクロソフト本社とその近くで開かれるイベントです。

今回は、日本から75名のMVP 、全世界では 1700 名を超えるMVPが参加し、マイクロソフトの製品グループや他のMVPとの直接の交流を行いました。

また、数百の技術セッションが行われNDA(Non-Disclosure Agreement=秘密保持契約)の元、様々な技術情報が提供されました。

NDAの関係でセッションの内容はお知らせできませんけれど、シアトルの様子などをレポートしてみたいと思います。

2008 MVP Global Summit

■ 会場など

私が参加したセッションの会場は三箇所です。

○ Washington State Convention & Trade Center

先ずは、Washington State Convention & Trade Center


・ Washington State Convention & Trade Center の外観。


・ Summit 会場入り口。


・ Summit 会場入り口の立て看板。


・ 多くの MVP が会場前に集っていました。


・ キーノート会場。


・ キーノートの様子。

今年のキーノートは、マイクロソフトのCEOのスティーブ・バルマー氏とチーフ ソフトウェア アーキテクトのレイ ・オジー氏でした。

スティーブ・バルマー氏の話はとても熱く、迫力満点で圧倒されるものでした。

○ マイクロソフト キャンパス

二つ目の会場は、レドモンドのマイクロソフト キャンパスです。

技術系のセッションは主にここで行われました。





・ 会場には、様々な高カロリーそうなものやコーヒーなどが置いてあって、セッションの合間に楽しむことができます。


・ セッションの様子。

セッションは基本的に英語のみです。

しかもセッションは、日本の多くの場合と異なり、スピーカーの話を大人しく座って聴いている、という形式ではありません。

参加者が MVP ばかり、ということもあるでしょうけれど、聴衆から頻繁に質問の手が挙がります。そして、スピーカーとの活発なやり取りがはじまります。

筆者は今回三回目の参加となるのですが、今回はディスカッション形式のセッションがとても多く、聴き取るのが難しいものだらけでした。

一応 iKnow などを利用して、少しだけアリングを鍛えてきたつもりでした。しかし、ディスカッションが始まると、資料も図解も何もないところで英語のネイティブなスピーカーが中心となって早口で話し、論点も次々と変化していきます。そのため、全然話に付いていくことができませんでした。無念でした。

○ シェラトン シアトル

三つ目の会場は、会期中私が宿泊したホテルシェラトン シアトルです。

朝食の一部はここで提供されました。


・ シェラトン シアトルでの朝食風景。

■ パーティ

夜毎にはパーティが開催されました。

二日目夜のプロダクト グループ毎のディナーでは、他国の C# のMVP の方と話をすることができました。

三日目のアテンディー パーティは、Experience Music Project という場所で行われました。

世界中の MVP の方がいて、様々な交流がありました。

また、最後の夜には、日本から参加している MVP だけのディナーがあり、日本の MVP とも交流しました。

シアトルってどんなところ?

次に、シアトルの紹介をしてみたいと思います。

シアトルは米国ワシントン州最大の都市です。ワシントン州はアメリカ西海岸最北部の州で、カナダとの国境があります。

シアトルは、ワシントン湖やレーニエ山など、美しい自然に囲まれた街です。治安も良いようで、とても良い場所であるように感じました (但し消費税率が約9%でした)。

ちなみに、日本との時差は16時間でした。前回ほどではありませんでしたが、時差ぼけになり、セッション中に気を失うように眠りに落ちてしまうこともありました。日本の家族と会話をするために、Skype のテレビ電話機能を使っていたのですが、家族の夕食時に掛けるためには、午前2時から5時の間に電話する必要がありました。

■ 気候

シアトルは一年を通じて穏やかな気候だということでした。4月の気温は摂氏6度から14度くらい。夏以外には雨の降る日が多いことで知られているそうです。

Summit 中も毎日のように雨が降っていました。また、日本よりもかなり肌寒く感じました。

但し、シアトルの雨は小雨が多く、傘を使うことはありませんでした。日本での服装に、撥水性のウィンドウ ブレーカーを羽織ると丁度良い感じでした。

■ 企業

シアトルとその近郊には、マイクロソフトをはじめとして、ボーイングスターバックスAmazon.comなどがあります。

マイクロソフトの本拠地があるのは、シアトル郊外 (東側) のレドモンドという町です。ここには、とても広い敷地内に沢山の建物があって、「マイクロソフト キャンパス」と呼ばれています。

■ 観光など

○ 観光名所

合間に少しシアトル観光もしてきました。写真でレポートしてみます。


・ まずは、Northwest航空でSeattle-Tacoma空港 (通称SEA-TAC空港)に到着したところから。


・ メジャーリーグのシアトル マリナーズには、現在イチロー選手や城島選手が在籍しています。この写真は、イチローが打席にいるところ。


・ シアトルの市場パイク・プレイス・マーケットは代表的な観光スポット。新鮮な魚介類を中心に様々なものが売られています。


・ パイク・プレイス・マーケットのシンボル。「レイチェル」という豚のブロンズ像。


・ パイク・プレイス・マーケットの果物店。


・ パイク・プレイス・マーケットには魚屋が沢山あります。


・ スターバックス一号店もパイク・プレイス・マーケットにあります。看板の色は緑ではなく茶色。


・ シアトル ダウンタウンのウエスト レイク センターというショッピング センター。


・ ウエスト レイク センターの地下にはダイソーがありました。品揃えは日本と余り変わりませんでした。


・ シアトルはスターバックスコーヒー発祥の地。街の到るところにスターバックスをはじめとしたシアトル コーヒーの店があります。


・ 丁度ダライ・ラマ14世がシアトルに滞在中でした。

○ 飲食物

ワシントン州はワインの産地としても知られていて、とても美味しいワインを楽しむことができました。

レッドフックブルワリーをはじめとしたシアトルの地ビールもとても美味しく連日堪能しました。

おわりに

英語漬けの毎日でしたが、とても貴重な体験でした。

次のようなことを学べたような気がしています。

  • 直接話すことの重要性
  • ディスカッションをするときの態度
  • Q&A 中心のセッション
  • プレゼンテーションはどうあるべきか
  • 主張するときは、結論から
  • 英語の重要性

やはり、英語力不足のためにコミュニケーションが不十分になってしまうのは、とても歯痒くつらい経験でした。

直接のコミュニケーションはもちろん、オンラインでの海外との情報のやりとりも英語が必要です。

エンジニアとして、英語を使う機会をもっと増やしていこう、と決意を新たにした次第です。

2008年05月15日

INETA Day 2008

今週末は、以下のイベントに参加。

私は、VSUG (Visual Studio User Group) 担当のセッション 1-1 「Visual Studio 2008による開発環境・プログラミングの進化」で、LINQ周りのプログラミングの話をする。

噂によると、数百名の参加登録があるそうだ。セッション後には、懇親会 (無料) も予定されているし、とても楽しみだ。

『INETA Day 2008~コミュニティパワーをリアルに体験しよう』
日時 2008年5月17日(土)
時間 13:00-19:00 (受付開始 12:30)
場所 会場 日本科学未来館
住所 東京都江東区青海2-41
参加費 無料 (事前登録制)
詳細 サイト
参加申込み Microsoft Events ― INETA Day 2008~コミュニティパワーをリアルに体験しよう

2008年05月20日

INETA Day 2008 に参加

INETA Day 2008 (5/17) 終了。

懇親会での参加者の方々の楽しげな顔は忘れられない。
コミュニティならではの素敵なイベントだった。

私は、「Visual Studio 2008による 開発環境・プログラミングの進化」と
題して、プログラミング スタイルにどのような新たな選択肢が追加された
のか、という話をした。

C# によるデモが中心で、

"手続き型なべあつ" → "マルチパラダイムなべあつ" → "音声なべあつ" → "LINQなべあつ" → "コンポーネントなべあつ"

と進化させてみた。なべあつがマルチパラダイムで「三が付く数字と、三の倍数の時だけ、アホになり、五の倍数のときは、犬っぽく」なる。音声出力にも挑戦してみた。

# 時間の関係で、説明をとても端折ることになったのが残念だった。
# どこかでもう一度ちゃんとやりたい。


INETA のサイトで資料とデモ用サンプルソースが公開された。
http://www.ineta.jp/tabid/173/Default.aspx

  • セッション 1-1 「Visual Studio 2008による 開発環境・プログラミングの進化」スピーカー: 小島 富治雄
  • デモ用サンプルソース ("手続き型なべあつ" → "マルチパラダイムなべあつ" → "音声なべあつ" → "LINQなべあつ" → "コンポーネントなべあつ")

2008年06月07日

[こみゅぷらす] Community Launch 2008

Community Launch イベントを開催します。
# 私も「Visual Studio 2008による開発環境・プログラミングの進化」で登壇予定です。

CLT2008.PNG
日時 2008年6月14日(土) 12:00~16:00
場所 英国風パブ HUB 新宿駅近く
主催/共催 こみゅぷらすeパウダ~codeseek
参加費 ¥1,000
今回 Culminis 様や、アイネタジャパン様、株式会社東京紙工様よりご支援をいただき、低い参加費を実現しました!
定員 50名限定
おしゃれなお店をお借りして、お酒を飲みながら実施いたしますので、お気軽にご参加ください。詳細/申し込みは、特設ページをご覧ください。

2008年06月15日

[こみゅぷらす] Community Launch 2008

CLT2008.PNG
昨日新宿で、「こみゅぷらす Community Launch 2008」が開催されました。

ご参加いただいた皆様、ご後援いただいた皆様、スタッフの皆さん、ありがとうございました!
お陰様でとても楽しいイベントになりました。

昨年は渋谷のピンクカウで「パブで飲みながら技術セッション」をやりました。そして、今年も開催することができました。

7998739_1587496162.jpg
7998739_3880356612.jpg
7998739_2389132330.jpg
7998739_3104531324.jpg
7998739_1011540939.jpg

# 私は、HUB のフィッシュ&チップスなどを楽しみながら、12時から12時まで12時間ビールとなりました。

2008年06月29日

VSUG Leaders Summit 2008 in 宮崎


伊丹空港から一風変わった飛行機で宮崎へ。


宮崎空港に到着。


会場のあるホテルへ。


VSUG Leaders Summit 2008。ライトニングトークスをやった。


宮崎料理の店。


宮崎のポピュラーな芋焼酎。


鶏炭火焼。柚子胡椒と一緒に食べる。びっくりするほど柔らかくて美味。


カワハギの刺身の真を実力を知った。肝も新鮮でぷりぷり。


大量の冷汁。


夜の街。


宿泊したホテルを朝出発。


昼間の飲み屋街。


有名な宮崎県庁。


みやざき物産館で昼地ビール。


昼の街並み。


昼食のチキン南蛮。


次に目指した釜揚げうどんの店「戸隠」は残念ながら定休日だった。


釜揚げうどんの店「重乃井


釜揚げうどん。やわらかくてもちもちして美味い。最後にお湯に付け汁を入れて飲む。


中が異様にカラフルな電車で宮崎空港へ。


宮崎空港にあった東国原人形。


空港で揚げたての「おび天」を食べた。


宮崎空港の中央部。南国ムード。


宮崎空港で食べられるマンゴーのソフトクリーム。


宮崎のデパートで買ってきた家への土産。花畑牧場 (北海道十勝の田中義剛の牧場)の生キャラメル。宮崎と何の関係もないがとても美味。

2008年07月02日

Micosoft MVP

Micosoft MVP (Most Valuable Professional) for Development Tools - Visual C# のアワードを再受賞することができました。四年目になります (Jul. 2005 - Jun. 2009)。

思えば、随分多くの方のお世話になってきました。

NIFTY-Serve のプログラマ系フォーラムでのオフラインでのコミュニケーション。そして、2002年頃からのオンラインでのコミュニケーション。

IT業界で多くの皆さんとお話をさせていただいたことが、私にとっての宝です。

いつもありがとうございます。

今後とも宜しくお願い致します。

2008年07月15日

Visual Studio Team System ホワイトペーパー

Visual Studio Team System ホワイトペーパー

  • Team Foundation Server を利用したアプリケーション開発概観 ― マイクロソフトのコンサルタントの執筆による、アプリケーション開発の際に利用されるTeam Foundation Server の主要な機能の使い方とその機能を利用する際の推奨事項 (PDF)
  • Web アプリケーションのパフォーマンス テスト ガイダンス ― Web アプリケーションにおけるパフォーマンス テストの実践についてベストプラクティスやポイントを解説 (PDF)
  • アジリティ (俊敏性) 向上のためのツール ― アジャイル開発等で著名なケント・ベックによる、ツールとアジャイル開発に関するホワイトペーパー(PDF)
  • Visual Studio Team System 2008 を使用したコミュニケーションとコラボレーション (PDF)
  • Visual Studio Team System 2008 を使用した予測可能性の向上 (PDF)
  • Visual Studio Team System 2008 を使用した早期かつ頻繁な品質確認 (PDF)
  • Visual Studio Team System 2008 を使用した頻繁な作業の統合 (PDF)
  • Visual Studio Team System 2008 を使用したリアルタイムの意思決定 (PDF)
  • Visual Studio Team System 2008を使用したチーム ワークフローの管理 (PDF)
  • Visual Studio Team System 2008 での馴染みのあるツールの使用 (PDF)
  • 分岐のガイダンス ― Team Foundation Server における分岐のベストプラクティス (PDF)
  • 要求管理と Team System (PDF)

2008年08月05日

こみゅぷらす Tech・Ed BoF

今年のTech・Ed Yokohama でも こみゅぷらすで BoF に出ます。

2008年08月30日

Tech・Ed 2008 Yokohama

四日間+αの長い夏祭りが終わりました。

Microsoft Tech・Ed 2008 Yokohama
2008/08/26~29

色々な体験がありました。

・沢山のセッション
BoF12 の準備と本番
・各種飲み会

お付き合いいただいた方、お話をさせていただいた方、BoF12 にご参加いただいた方 (聴衆とスタッフとスピーカーの皆様)、本当にありがとうございました。

■ 会場のパシフィコ横浜
20251262_3222786486.jpg 20251262_3038266433.jpg

■ 今年の宿泊先は横浜桜木町ワシントンホテル
20251262_659822036.jpg

■ 沢山のセッションやハンズオン等にも参加
20251262_1501235040.jpg 20251262_1501235040.jpg 20251262_3211852185.jpg 20251262_3728042009.jpg 20251262_207862212.jpg 20251262_1036764034.jpg 20251262_3456375616.jpg

■ 巨大なトークスタイマー
20251262_1151214832.jpg

■ 飲み会関連
20251262_3671258968.jpg 20251262_1458193690.jpg 20251262_1455125579.jpg

2008年09月15日

Micosoft 最新技術最強セミナー in 福井~Tech・Ed 2008 記念~

dc033007.JPG

Tech・Ed 2008 Yokohama のスピーカーを多数に招いてのセミナーを福井で実現しました。この機会をお見逃しなく!
お席に限りがございますので、皆様お早目のお申し込みをお願いいたします。

『Micosoft 最新技術最強セミナー in 福井~Tech・Ed 2008 記念~』
日時 2008年10月3日(金) 13:00~18:00 (受付は12:30より開始)
 ※終了後に有志で懇親会を開催します。こちらもお気軽にどうぞ。
 ※懇親会に参加される方はセミナー終了後福井駅前に移動して行います。
場所 福井県中小企業産業大学校 特別教室
定員 60名
内容
  1. 『ASP.NETの仕組み』
     講師:小野 修司氏
     あおい情報システム株式会社
     Microsoft MVP for ASP/ASP.NET (April 2007 - March 2009)
     Visual C# (April 2003 - March 2007)

  2. 『Silverlight 2.0』
     大西 彰氏
     マイクロソフト株式会社 デベロッパー&プラットフォーム統括本部

  3. 『Hyper-Vハンズオン』
     杉下 朋年氏
     D&UNITE株式会社 代表
     Microsoft MVP for Development Tools - Visual Basic
     Jul. 2004 - Jun. 2009

  4. 『Software+Services入門』
     福井 厚氏
     アバナード株式会社 ソリューション・アーキテクト
     MCA (Microsoft Certified Architect) Solutions Certifications
       Microsoft MVP for ASP/ASP.NET

  5. 『ライトニングトークス』
     5分間の短いプレゼンを交代で行います。
    • 平鍋 健児氏 株式会社チェンジビジョン 代表取締役社長
    • 橋本 賢一氏 管理工学研究所 北陸分室
    • 中西 孝之氏 (株)アイジュピタ
    • 小島 富治雄氏 福井コンピュータ(株) Microsoft MVP for Development Tools - C#
※内容、順番は変更されることがあります。
参加費 無料
 ※懇親会に参加される方は別途。
 ※今回のイベントはINETA Japanの支援を受けて開催する予定です。
対象者
  • Tech Edに行けなくてくやしかった方。
  • マイクロソフト系の技術を仕事で使われている、又は興味のある方。
  • 最新トレンドについて広く浅く頭に入れておきたい人。
  • 最近の流れになかなかついていけてないプログラマー。
  • その他、コミュニティイベントに興味のある方どなたでも。
■難易度:★★★☆☆(3)or ★★★★☆(4)
お申し込み締切 2008年9月30日(火)迄
 →詳細はこちら:http://fitea.org/?p=84
 →お申し込みはこちら:http://fitea.org/enterevents.html
 ※お席に余裕がありませんので、お早めにお申し込み下さい。
主催  
  FITEA (福井情報技術者協会)
 後援 
  アイネタ・ジャパン
留意事項 ※会場へのアクセスはこちらをご覧下さい。産業会館の隣です。
 約400台収容可能な無料駐車場があります。
 http://www.fukui-iic.or.jp/fiib/access.html
※無料のフレンドリーバスが通っています。「生活学習館前」です。
 福井駅前を12:00に出れば間に合います。帰りは18:04、19:04のバスが使えると思います。
 http://www.library.pref.fukui.jp/guide/bus.html
イベント終了後の懇親会について 福井駅近く「庄内家」にて18:40開始の予定です。ご予算はお一人5000~6000円程度の予定です。
 http://gourmet.gyao.jp/0003003173/
スピーカーの方も参加されますので、この機会に交流を深めましょう。

2008年11月09日

Open Day 2008 Tokyo

昨日は、九段で Open Day 2008 Tokyo に参加。

日本の Microsoft MVP の半数位に当たる 100名が一堂に会した。マイクロソフトの方も多くいらしていた。
私は、Windows 7 や Visual Studio 2010 などのセッションや懇親会に参加。
dc1109032.JPG dc110903.JPG dc110928.JPG
※ 最後の写真は、とある MVP の方に頂いた PDC のお土産。ジャグリング用。

2008年12月05日

Silverlight 2 SDK 日本語ドキュメント

Silverlight 2 SDK 日本語ドキュメント認知度アップキャンペーン。

Silverlight 2 アプリケーション開発に必要なドキュメントが日本語になっているので紹介。

2009年01月15日

CES2009 Keynote

CES2009 (International Consumer Electronics Show 2009) でのマイクロソフト CEO スティーブ バルマー氏の基調講演です。

途中でシャルロット・ジョーンズ氏による Windows 7 ベータと Windows Mobile と Windows Liveのデモがあります。
# 他に、ロビー バック氏による Zune と Xbox のデモ、ジャネット ガロアによる Microsoft Research の未来のコンピュータのデモもあります。

※ 動画を観るには Silverlight が必要です。

※ 口述筆記したものはこちら

2009年05月29日

こみゅぷらす Community Launch 2009 ~Windows 7 コミュニティ勉強会 with Tech Fielders 東京編~

今年もやります。

7CLTLogo.png
『こみゅぷらす Community Launch 2009
~Windows 7 コミュニティ勉強会 with Tech Fielders 東京編~』

■ 会場: マイクロソフト株式会社 新宿本社
■ 日時: 2009年6月27日 13:00 ~ 18:30
■ 主催: こみゅぷらす、アイネタ ジャパン、codeseek
  後援: マイクロソフト

※ 詳しくは、http://comuplus.net/clt2009/ へ。

2009年06月19日

INETA Day 2009 募集開始

INETA Day 2009 の参加者募集が開始された。

マイクロソフト 萩原正義氏とアークウェイ 森屋英治氏がクラウドのアーキテクチャの話をされる。
クラウドに関するお二人の話を続けて聴ける、というのは、中々にすごいことだと思う。

  • 萩原氏 「クラウドでのアーキテクチャの原則と考慮点」
  • 森屋氏 「Windows Azure アーキテクチャと開発」

私はスタッフとして参加する他、最新技術トラックで Visual Studio 2010 と .NET Framework 4.0 をご紹介する予定。

『INETA Day 2009 ~コミュニティパワーをリアルに体験しよう』
日時 2009年7月11日(土) 13:00-17:30 (受付開始 12:15)
会場 日本科学未来館
  • みらいCANホール (7F) 受付
  • 会議室1 (7F)
  • 会議室2 (7F)
申込 http://www.ineta.jp/tabid/232/Default.aspx
定員 300名
参加費 無料 (事前登録制・要 INETA への登録)
セッション詳細 【最新技術トラック】
  • 13:00-14:15 (75)
    Microsoft ソフトウェアアーキテクト 萩原 正義 氏
    「クラウドでのアーキテクチャの原則と考慮点」
  • 14:30-15:45 (75)
    株式会社アークウェイ 代表取締役 森屋 英治 氏
    「Windows Azure アーキテクチャと開発」
  • 16:00-17:15 (75)
    こみゅぷらす/VSUG 小島 富治雄
    「C#4.0 と Visual Studio 2010/.NET Framework 4.0~ここが Cool! いけてる機能10連発!~」
【コミュニティトラック】
  • 13:00-13:40 (40)
    VSUG
    「タイトル未定」
  • 13:45-14:25 (40)
    インフラジスティックス・ジャパン(株) デベロッパー エバンジェリスト 池原 大然 氏 (アイネタビューロ登録スピーカー)
    「Prism ではじめる Silverlight LOB アプリケーション開発」
  • 14:30-15:10 (40)
    WisdomSoft 赤坂 玲音 氏
    「Silverlight カスタムコントロール開発」
  • 15:15-15:55 (40)
    .Netユーザーエクスペリエンス研究所 児玉 宏之 氏
    「WPFの新しいコントロール」
  •  16:00-17:15 (75)
    パネルディスカッション
    「レガシー資産をどうされていますか?」
【チュートリアルトラック】
  • 13:00-15:45 (165)
    XPJUG 小井土 亨 氏
    「オブジェクト指向設計入門」

2009年07月01日

こみゅぷらす Community Launch 2009 ~Windows 7 コミュニティ勉強会 with Tech Fielders 東京編~ レポート

7CLTLogo.png
イベントレポートをあげました。

ご参加いただいた皆様、本当にありがとうございました。
おかげさまで充実したときを過ごすことができました。

2009年07月03日

Microsoft Tech·Ed Japan 2009

ことしも横浜で開催予定。楽しみだ。

2,000 人の IT エンジニアが集う 3 日限りの真夏の祭典 | Microsoft Tech·Ed Japan 2009
開催日程 2009年8月26日(水)~28日(金)
開催場所 パシフィコ横浜
参加料 有償 (早期割引 7月17日まで)

2009年07月13日

INETA DAY 2009 無事終了

INETA DAY 2009 に参加してきました。
ご参加いただいた皆様、ありがとうございました。

DSCF2099.JPG DSCF2084.JPG DSCF2094.JPG

終了後は、懇親会 → ガンダム → 新橋で飲み、という流れになりました。
エンジニアとして尊敬する方たちとの、本当に素敵なひと時でした。貴重な機会に感謝。

関連記事:

2009年07月17日

ReMIX Tokyo 09

下記に参加してきた。

ReMIX Tokyo 09
日時 2009/07/16(木)
場所 東京ミッドタウン・ホール
主催 マイクロソフト

キーノートでは、米 Microsoft のスコット・ガスリー氏 (Scott Guthrie, デベロッパープラットフォームコーポレートバイスプレジデント) が登場。

dc071727.jpg

話題の中心は、Silverlight 3。
ちなみにこの日、「Microsoft Visual Studio 2008 Service Pack 1用 Silverlight 3 Tools 日本語版」や「Silverlight 3 ソフトウェア開発キット 日本語版」の提供が開始された。

他には、Microsoft Expression 3 のデモも興味深いものだった。

昼には、『スコットガスリーMVPスペシャルセッション』にも参加。

dc071776.jpg

午後からは、3トラックに分かれて、セッションが行われた。

dc0717124.jpg

多くのブースが出展していて、Silverlight 3 や Experssion 3、Windows Surface などのデモを見ることができた。

dc071787.jpg

会場で、Silverlight 3 の本をゲット。

dc0717126.jpg

セッション後の参加者交流パーティでは、マイクロソフトの方をはじめとした多くの技術者と話をすることができた。

dc0717128.jpg

■ 関連記事:

2009年07月19日

Windows 7 コミュニティ勉強会 with Tech Fielders 北陸編

下記に参加してきました。

Windows 7 コミュニティ勉強会 with Tech Fielders 北陸編
日付 2009/07/18(土)
会場 HOTEL KANAZAWA
主催 北陸エンジニア交流支援 Redmine
dc071937.jpg dc071901.jpg dc071915.jpg

私は、Visual Studio 2010 のデモを中心としたセッションをやってきました。

スピーカーの多くが Mac ユーザー、開発環境に関しても Eclipse の話題が多く、マイクロソフト技術の勉強会なのにアウェー感の中でのセッションとなりました。

聴いていただいた皆様、スタッフの皆様、ありがとうございました。

■ 関連記事:

2009年08月24日

Tech·Ed の BoF で「美しいソース コードのための考え方」というのをやります

Tech·Ed Japan 2009Birds of a Feather で、

BOF-08 ~マルチパラダイム時代のプログラムの書き方~
日時: 8月27日(木) 15:15-16:25

というのをやります。宜しければご参加ください。

2009年08月31日

Microsoft Tech·Ed Japan 2009 の写真

Microsoft Tech·Ed Japan 2009
開催日程 2009年8月26日(水)~8月28日(金)
開催場所 パシフィコ横浜

一日目

二日目

三日目

2009年11月22日

Visual Studio 2010 Beta 2 インストール前にアンインストールするもの

現在 Micirosoft Visual Studio 2010 Beta 2 日本語版 がダウンロード可能だが、インストール前に Visual Studio 2010 Beta 1 や Silverlight 3.0 SDK などをアンインストールしておく必要がある。

詳しくは以下を参照:

Visual Studio 2010 Beta 1 に比較してかなり安定しており軽快になったようだ。

2010年01月29日

Microsoft Tech·Days 2010

イベント名 Microsoft Tech·Days 2010
日時 平成22年2月23日(火)-24日(水)
会場 ホテル グランパシフィック LE DAIBA (東京都港区台場 2-6-1)
主催 マイクロソフト
参加費 有料
詳細/申し込み http://www.microsoft.com/japan/events/techdays/2010/

マイクロソフトの次世代プラットフォームを紹介するイベント PDC (Professional Developers Conference) の日本版です。

私はコミュニティ トラックの「Visual Studio 2010 でプチ・パラダイムシフトせよ!」 <2/23(火) 15:50-17:00 ROOM F> で登壇します。

2010年02月21日

Microsoft 2010 MVP Global Summit

Microsoft 2010 MVP Global Summit
日時 2010/02/16(火)-21(日)
場所 米国ワシントン州 ベルビュー (ベルビュー会場:Hyatt Regency Bellevue、レドモンド会場:マイクロソフト本社 オフィス)
主催 マイクロソフト

※ 一日目のつぶやき。

  • MVP Global Sunmit へ出発。先ずはバスで小松へ。
  • 成田空港に到着。 http://twitpic.com/13i0pp
  • 長旅に備えて成田空港のラウンジで色々充電中。コーヒー・ビールとともにテレビでオリンピック。
  • 成田空港での昼食は森さんとタイカレー。うまい。
  • 出国した。これから搭乗。

※ 二日目のつぶやき。

※ 三日目のつぶやき。

  • MVP Global Summit 二日目の朝7:30。今日は技術系のセッションを色々受ける。
  • マイクロソフトキャンパスでセッションを受講中。
  • 英語に耳が慣れてきた。
  • VB/C#のディナー中。Visual Studio グッズをもらった。
  • 今日はパキスタンの人とギリシャの人、ルーマニアの人、韓国の人、米国の人、ロシアの人等と話した。

※ 四日目のつぶやき。

  • MVP Global Summit 三日目の朝。昨夜は知り合った韓国の5人のMVPと部屋飲みした。
  • 今日の最初のセッションはパラレルプログラミング。

※ 五日目のつぶやき。

  • MVP Global Summit 最終日。キーノート セッション中。
  • MVP Global Summit 最終日。午後のセッション中。
  • MVP Global Summit 最終日終了。明日帰国。
  • MVP Global Summit finished today. I'm going to flight back to Japan tomorrow morning. #MVP10
  • Thanks for the great summit! #mvp10

※ 六日目のつぶやき。

  • Good morning! I'm going to flight back from MVP Global Summit today. #mvp10
  • SEA-TAC 空港に到着。
  • 無事帰国。
  • これから成田空港から小松空港に移動。
  • 成田空港で他人のパスポートを拾った。届けた。
  • 成田から小松空港に到着。飛行機で隣合わせたベルリン出身の女子学生と一時間話した。

2010年02月24日

Microsoft Tech·Days 2010

Microsoft Tech·Days 2010
■ 日時: 2010/02/23(火)、24(水)
■ 場所: ホテル グランパシフィック LE DAIBA (東京都港区台場)
■ 主催: マイクロソフト株式会社

○ 02/23(火)
・キーノート「3 スクリーン + クラウドの世界を切り拓くマイクロソフトの最新テクノロジ ~ Let's dream and then Let's build ~」大場 章弘氏、スティーブ マークス 氏
・「Visual Studio 2010 でプチ・パラダイムシフトせよ!」 小島 富治雄氏、原 敬一氏
・「クラウド時代のアーキテクチャ + Azure + Data」 森屋 英治氏

○ 02/24(水)
・「並列プログラミングのパターンと Visual Studio 2010 を使ったその適用」 川西 裕幸氏
・「.NET Framework 4 時代の言語」 荒井 省三氏
・「インテルの並列化プログラミングへの取り組みとインテル Parallel Universe Portal のご紹介」 菅原 清文氏
・「ISV 向け Windows Azure による SaaS アプリケーション開発」 平井 昌人氏
・「クラウド コンピューティングのデータ、アプリケーション、開発手法のメタアプローチ」 萩原 正義氏


※ 一日目のつぶやき。

  • これから Tech•Days へ。小松空港へバスで移動中。 http://twitpic.com/14s97z
  • 羽田を出てバスで台場へ移動中。
  • キーノート会場に到着。 #techdaysj
  • 富士通って nifty のクラウドは使わないんだろうか。
  • 技術者ってテクノロジーが好きだから、何ができるか、の前に、どうやってやってるか、をしゃべっちゃうんだよなー。気持ちはわかるけど。黙ってデモった方が説得力がある気がする。
  • マイノリティレポートのデモ。
  • Windows Phone のデモはいけてるなー。 #techdaysj
  • と思ったら瞬時に終わってしまった #techdaysj
  • 近藤さんによる Visual Studio 2010 の紹介中。インテリトレースという新機能のデモ。 #techdaysj
  • 森屋さんの「クラウド時代のアーキテクチャ+Azure+Data」が始まった。 #techdaysj

※ 二日目のつぶやき。

  • TechDays 二日目。最初のセッションは川西さんの「並列プログラミングのパターンと Visual Studio 2010 を使ったその適用」。

  • Tech Days。荒井さんの「.NET Framework 4 時代の言語」中。キーワードは、動的、宣言型、コンカレンシー。
  • Tech Days ランチ セッション中。
  • Tech Days。午後一のセッションは平井さんの「ISV 向け Windows Azure による SaaS アプリケーション開発」。
  • 萩原さんの「クラウド コンピューティングのデータ、アプリケーション、開発手法のメタアプローチ」中。 #techdaysj
  • データ設計はデータの意味によって行われ、正規化はその後に起こる。 #techdaysj
  • 「データ設計ではデータの分割が重要。分割しなければスケールしない。」 #techdaysj
  • 「データ間の多重度の変更 (1:多 → 多:多) が起こることがある。データ設計では多重度の変更に関する安定性が課題。」 #techdaysj
  • 「なぜ JOIN が必要なのか? データには『もの』以外に『こと』が発生するから。そして『もの』は目的じゃない。『こと』が目的で『もの』は『こと』のためにある。」 #techdaysj
  • 「高階述語モデル。例. 教わる(学生、科目、教官) = 教わる(選択する(学生、科目)、担当する(科目、教官))。RDBは一階述語モデル」 #techdaysj
  • 「現実世界は高階述語モデルなのに、それを一階述語モデルであるRDBに落とし込もうとすることから却って複雑さが生まれる」 #techdaysj
  • 「述語モデルのコツ。『もの』と『もの』には『こと』を介在させる。『こと』は『もの』と『もの』とを組み合わせるコンテキスト」 #techdaysj
  • 「高階述語モデルにおいて、述語の変更は述語を伝播するが、スケールアウトのためには述語の変更頻度に合わせて配置する必要がある」 #techdaysj
  • 「『事実が起こった』ことと『事実が観測される』ことは別のこと。これまでは同じとして設計していたが、クラウドでは別のものとして設計する」 #techdaysj
  • 「人にとって『事実の記憶』と『知識の構築』は別のプロセス。前者は起きているとき、後者は眠っているときに発生する。クラウドでもこれらは別のプロセス」 #techdaysj
  • 「クラウドの物理的法則。操作とその結果の観測は同時にはできない」 #techdaysj
  • 「『複雑さに対する人間の理解力の限界』が、問題に対する『論理レベルの制約』になる」 #techdaysj
  • Tech Days 終了。帰ります。お会いできた皆様、ありがとうございました。
  • 羽田空港到着。ラウンジでコーヒー中。
  • 帰ってきた。There is no place like home!

DSCF8713.JPG

DSCF8653.JPG

DSCF8658.JPG

DSCF8659.JPG

DSCF8662.JPG

DSCF8708.JPG

2010年04月04日

2010 Community Open Day with INETA Japan - Osaka ~ Microsoft Visual Studio 2010 編

『2010 Community Open Day with INETA Japan - Osaka ~ Microsoft Visual Studio 2010 編』
■ 日時: 2010/04/03(土) 13:00-19:00
■ 場所: マイクロソフト株式会社 関西支店 セミナールーム
■ 主催: マイクロソフト株式会社アイネタジャパン

■ セッション:

  1. 『VS 2010 で快適マルチパラダイム プログラミング』 小島 富治雄 氏
  2. 『VS 2010、進化した WPF および Siliverlight デザイナー』 森 博之 氏
  3. 『VS 2010 による Extensibility プログラミング』 松江 祐輔 氏
  4. 『自動テストのススメ by VS 2010』 小井土 亨 氏

IMG_0832.JPG
雷鳥で福井から大阪へ。

dc040407.JPG
マイクロソフト大阪支店に到着。

dc040413.jpg
小井土氏の司会でイベント開始。

dc040419.jpg
森氏の『VS 2010 による Extensibility プログラミング』。

dc040422.JPG
小井土氏「自動テストのススメ by VS 2010」。

dc040434.JPG
四つのセッションが終わってハッピーアワ ーが始まった。

dc040450_stitch.jpg dc040462.JPG
全て終わって新大阪から電車で帰宅。

2010年04月13日

VSUG Day 2010

VSUG Day 2010 Summer Tokyo
VSUG Day 2010 Summer Osaka

2010年06月29日

『こみゅぷらす Community Launch 2010』 7/10(土)

『こみゅぷらす Community Launch 2010』
こみゅぷらす恒例、酒あり食べ物あり、参加者が入り乱れた楽しいラウンチイベントです。
料金もかなりリーズナブル!
豪華な景品や粗品が当たる抽選会も計画中です!

今回は Visual Studio 2010 のラウンチ イベントです。

・主催  : こみゅぷらす、codeseek
・開催日:  2010年7月10日(土)
・会場  :  新宿の居酒屋(詳細な店舗名はお申し込みした方にメールで) (東京都)
・定員  :  50名
・参加費 : 3,000円 (※ この会費は、利益を出すものではありません。店の食事代になります)
・詳細/お申込み:  http://comuplus.net/clt2010/


予定セッション
■ 「Expression Blend 4とVisual Studio 2010によるSilverlight 4アプリケー
ション開発」
マイクロソフト 大西 彰 氏
■ 「IronPython、IronRuby on Silverlight」
マイクロソフト 荒井 省三 氏
■ 「Visual Studio デバッギング (仮)」
亀川 和史 (めさいあ) 氏
■ 「Visual Studio 2010 で C# ・WPF アプリケーション」
宇宙仮面 氏
■ 「C# VS. F# on Visual Studio 2010」
小島 富治雄 氏 (Fujiwo)
■ その他
素晴らしいスピーカー陣を予定しています。

奮ってご参加ください。

『Hokuriku.NET Vol.4』7/17(土)

ユーザーインタフェースからクラウドまで、幅広いマイクロソフト技術をまとめて学べる贅沢なセミナーが福井で開催されます。

マイクロソフトは近年、Visual Studio 2010やWPF、Silverlight、LINQ、Windows Azureなど、さまざまな分野のテクノロジーを続々と発表しています。これらの技術について、一度に学べるセミナーは福井ではめったにありません。日本海側最大級!!

マイクロソフトエバンジェリストやマイクロソフトMVPが多数講師として参加しますので、この機会をお見逃し無く!

お席に限りがございますので、皆様お早目のお申し込みをお願いいたします。

■ セミナー名: 『Hokuriku.NET Vol.4』
■ 主旨: マイクロソフト系の技術を広く語り合う定期開催勉強会
■ 開催日: 2010年7月17日(土) 13:00 - 17:30 (12:40受付開始)
■ 場所: アオッサ6F 研修室603 (http://www.aossa.jp/access.html) JR福井駅から徒歩1分
■ 参加費: 500円程度のカンパをお願いしています
■ 主催: FITEA (http://fitea.org)/北陸エンジニアグループ (http://groups.google.co.jp/group/hokuriku_engineer)
■ 定員: 40名
■ 内容
タイムテーブル:

○12:40 受付開始

○13:00 開会挨拶 FITEA/北陸エンジニアグループ

○13:10 基調講演 『Visual Studio 2010の世界観』
 マイクロソフト株式会社 デベロッパー&プラットフォーム統括本部 エバンジェリスト 長沢智治氏

○14:00 技術セッション 『Silverlight での開発 (仮)』
 北陸エンジニアグループ 西村誠氏

○14:30 休憩

○14:40 Team WF による特別講演 第一部 『最適なテクノロジーの選択とアーキテクチャ』
 Team WF (株式会社アークウェイ 代表取締役、Microsoft MVP 森屋英治氏、
アバナード株式会社 ソリューション・アーキテクト、Microsoft MVP、MCA 福井厚氏)、
福井コンピュータ株式会社 シニアエキスパート、Microsoft MVP 小島 富治雄氏

 「マイクロソフトは、UI層、サービス層、データアクセス層のそれぞれにおいて様々なテクノロジーを提供しています。UI層であれば WinForm、WPF、ASP.NET、WebForm、ASP.NET MVC、Silverlightなど、サービス層であればASMX、WCFなど、データアクセス層であれば、レガシーADO.NET、Entity Framework、LINQ to SQLなど。さらにWindows Azureを加えるとテクノロジーの選択肢は広がります。これら多数のテクノロジーの特長を理解し、要求の応じた最適なテクノロジーを選択するためにはどうすればよいかを、ホワイトボードを使いながら、ディスカッション形式で解説します」

○15:30 休憩

○15:40 Team WF による特別講演 第二部 『Entity Framework Deep Dive』
 Team WF (株式会社アークウェイ 代表取締役、Microsoft MVP 森屋英治氏、
アバナード株式会社 ソリューション・アーキテクト、Microsoft MVP、MCA 福井厚氏)

 「マイクロソフトのORマッピング実装であるEntity Frameworkを深く掘り下げて解説します。エンティティ継承とテーブル マッピング、レイジーロード、テスト容易性、状態管理とシリアライゼーション、N層アプリケーションへの対応などを扱います」

○16:50 ライトニングトークス 中西 孝之氏 ((株)アイジュピタ)、小島 富治雄氏 (福井コンピュータ(株)、Microsoft MVP) 他

○17:25 締めのあいさつ・アンケート

■懇親会:
終了後福井駅近くで懇親会を予定しています。
場所は決まり次第お知らせいたします。

■お申し込み:
こちらからどなたでもお気軽にどうぞ
http://fitea.org/enterevents.html

ライトニング トークスの参加者を募集しています。
ライトニング トークスをしていただける方は、参加お申し込み時にお願いします。

■ 参考:
・Hokuriku.NET の Webサイト: http://sites.google.com/site/hokurikunet/

・Hokuriku.NET Vol.3 は石川県野々市町で先週土曜日に開催されました。
http://atnd.org/events/4182
http://blog.livedoor.jp/coelacanth_blog/archives/55384003.html

2010年07月11日

『こみゅぷらす Community Launch 2010』 7/10(土)

7/10(土) に開催した『こみゅぷらす Community Launch 2010』の様子を写真で紹介。

  • 「Expression Blend 4とVisual Studio 2010によるSilverlight 4アプリケーション開発」 マイクロソフト 大西 彰 氏
  • 「IronPython、IronRuby on Silverlight」 マイクロソフト 荒井 省三 氏
  • 「Visual Studio デバッギング (仮)」 亀川 和史 (めさいあ) 氏
  • 「Visual Studio 2010 で C# ・WPF アプリケーション」 宇宙仮面 氏
  • 「C# VS. F# on Visual Studio 2010」 小島 富治雄 氏 (Fujiwo)

2010年08月17日

Microsoft Tech·Ed で BoF やります

今年も Microsoft Tech·Ed で BoF やります。

「BOF-05 最速導入 Visual Studio 2010」(Day 2: 8/26 13:45-14:55、会議センター 3F)。
宜しければご参加ください。

http://www.microsoft.com/japan/teched/2010/session/session.aspx?SessionID=BOF-05

2010年08月29日

Microsoft Tech·Ed Japan 2010 の写真

Microsoft Tech·Ed Japan 2010 に行ったときの写真。

  • 開催日程: 2010年8月25日(水)~8月27日(金)
  • 開催場所: パシフィコ横浜 (横浜市西区みなとみらい)
■ 宿泊先の横浜桜木町ワシントンホテルから会場のパシフィコ横浜
■ 会場のパシフィコ横浜
■ 基調講演
■ 会場内の様子
■ セッションの様子
■ Hand-on Lab の様子
■ Birds of a Feather の様子
■ Attendee Party (8/25 18:05-19:30) の様子
■ MVPs & Tech·Ed Speakers Lunch (8/26 12:15-13:30) の様子
■ Windows Azure Community 発足パーティ (8/26 18:30-19:15) の様子
■ PeerTalk Lunch (8/27 12:05-13:45) の様子

2010年09月15日

[C#/.NET Tips] struct を使うときの注意点

using System;
using System.Collections.Generic;
 
class TestProgram
{
    struct StructCounter
    {
        public int Number       { getset; }
        public void Increment() { Number++; } // struct に内部の状態を変えるようなメソッドを持たすときは要注意。
    }
 
    class ClassCounter
    {
        public int Number       { getset; }
        public void Increment() { Number++; }
    }
 
    // struct では、readonly なときとそうでないときで、内部の状態を変えるようなメソッドを呼んだときの挙動が異なるので要注意。
    // 勘違いから思わぬバグの元になることも。
    class ReadOnlyStructTest
    {
        // class を使った例。
        ClassCounter           classCounter         = new ClassCounter();
        readonly ClassCounter  readOnlyClassCounter = new ClassCounter();
 
        // struct を使った例。
        StructCounter          structCounter;
        readonly StructCounter readOnlyStructCounter;
 
        public void Run()
        {
            // クラスの場合はどっちも結果は同じなので、違いを特に意識しなくても大丈夫。
            classCounter.Increment();
            Console.WriteLine(classCounter.Number         ); // 結果は 1。
            readOnlyClassCounter.Increment();
            Console.WriteLine(readOnlyClassCounter.Number ); // 結果は 1。
 
            // struct の場合は、readonly なときとそうでないときで、要素の内部の状態を変えるようなメソッドを呼んだときの挙動が異なる。
            structCounter.Increment();
            Console.WriteLine(structCounter.Number        );  // 結果は 1。structCounter そのものにアクセスしている。
            readOnlyStructCounter.Increment();
            Console.WriteLine(readOnlyStructCounter.Number);  // 結果は 0。readOnlyStructCounter そのものではなく、コピーにアクセスしている。
        }
    }
 
    // struct の配列とリストでは、要素の内部の状態を変えるようなメソッドを呼んだときの挙動が異なるので要注意。
    // 勘違いから思わぬバグの元になることも。
    static void StructArrayTest()
    {
        // class の場合はどっちも結果は同じなので、違いを特に意識しなくても大丈夫。
        var classArray = new ClassCounter[] { new ClassCounter() };
        classArray[0].Increment();
        Console.WriteLine(classArray[0].Number); // 結果は 1。
 
        var classList = new List<ClassCounter>() { new ClassCounter() };
        classList[0].Increment();
        Console.WriteLine(classList[0].Number); // 結果は 1。
 
        // struct の場合は、配列とリストでインデクサ経由で要素の内部の状態を変えるようなメソッドを呼んだときの挙動が異なる。
        var structArray = new StructCounter[] { new StructCounter() };
        structArray[0].Increment();
        Console.WriteLine(structArray[0].Number); // 結果は 1。structArray のインデクサを通じて、内部のアイテムに直にアクセスできる。

        var structList = new List<StructCounter>() { new StructCounter() };
        structList[0].Increment();
        Console.WriteLine(structList[0].Number); // 結果は 0。structList のインデクサは、内部のアイテムのコピーを返す。
    }
 
    static void Main()
    {
        new ReadOnlyStructTest().Run();
        StructArrayTest();
    }
}

2010年12月28日

【Tips: ADO.NET】 IDENTITY 列を持ったテーブルに行を挿入した時の IDENTITY 列の値の取得

SQL Server の IDENTITY 列は、テーブルに新しい行が挿入されるときに自動的にその値がインクリメントされます。主キーとしてたいへん便利です。

行の挿入後に IDENTITY 列の値を取得したいことがあります。ADO.NET における、そのやり方を見て行きましょう。

※ 動作環境
  • ここで使用する Visual Studio は Visual Studio 2010 です。
  • 想定している .NET Framework のバージョンは .NET Framework 4.0 です。

■ 1. SQL Server 側のテーブルの準備

先ず、IDENTITY 列を持ったテーブルを SQL Server 側に準備しましょう。

  1. Visual Studio の「サーバーエクスプローラ」 (Visual Studio Express Edition の場合は「データベース・エクスプローラ」) を開きます。
  2. 「データ接続」を右クリックし、ポップアップ メニューから「接続の追加」または「新しい SQL Server データベースの作成」をします。
  3. その接続の「テーブル」 - 「新しいテーブルの追加」でデータベースにテーブルを追加します。

  1. ここでは、試しに以下のような "Item" というテーブルを追加することにします。
Item
列名 主キー データ型 Null を許容 IDENTITY の設定
Id int × はい
Name × nvarchar(100) × いいえ
  1. Id 列を主キーとし、IDENTITY の設定を「はい」にしておきます。

  1. または、SQL Server Management Studio などから以下の SQL を実行することでも上記 Item テーブルを作成することができます。
CREATE TABLE [dbo].[Item](
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[Name] [nvarchar](100) NOT NULL,
 CONSTRAINT [PK_Item] PRIMARY KEY CLUSTERED 
(
	[Id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

データベース側の準備ができましたので、これから ADO.NET の各種手法において、新規行の IDENTITY 列の値の取得方法を見ていきましょう。


■ 2. SqlCommand の場合

SQL Server で使用する Transact-SQL では、"SCOPE_IDENTITY" という関数を使って、INSERT 後に、自動採番された値を取得することができます。

※ 参考: MSDN - SCOPE_IDENTITY (Transact-SQL)

それでは、実際のプログラムで試してみましょう。

  1. Visual Studio を起動し、メニューの「ファイル」 - 「新規作成」 - 「プロジェクト」から「新しいプロジェクト」ダイアログ ボックスを開きます。
  2. 「Visual C#」 - 「コンソール アプリケーション」で新しいプロジェクトを作成します。
  3. ここでは、以下のようなサンプルで試してみることにします。
    (connectionString の値は、適切なものに変更してください。下の例では、ローカルの SQL Server Express に作成した "Test" という名前のデータベースに接続しています。)
using System.Data.SqlClient;
 
namespace TypedDataSetUpdateWithIdentityColumn
{
    class Program
    {
        static void Main()
        {
            const string connectionString = @"Data Source=localhost\SQLEXPRESS;Initial Catalog=Test;Integrated Security=True";
            using (var connection = new SqlConnection(connectionString)) {
                connection.Open();
                // 新規 Item の INSERT
                using (var command = new SqlCommand("INSERT INTO Item (Name) VALUES (@Name);SELECT CAST(SCOPE_IDENTITY() AS int)", connection)) {
                    command.Parameters.Add(new SqlParameter("@Name""Hoge"));
                    var newId = (int)command.ExecuteScalar();
                }
            }
        }
    }
}
  1. 実行結果をデバッグ実行で確認してみましょう。

  1. command を ExecuteScalar することで、新たな行が挿入され、その行の Id 列の値が返ってきています。その直後の "newId" の値が、新たな Id 値である "1" になっているのが確認できます。

■ 3. 型付き DataSet の場合

型付き DataSet の場合はどうでしょう。試してみましょう。

  1. Visual Studio で、新しい「コンソール アプリケーション」を作成し、「ソリューション エクスプローラー」でプロジェクト名を右クリックします。
  2. ポップアップ メニューから、「追加」 - 「新しい項目」を選択します。

  1. 「新しい項目の追加」ダイアログ ボックスが開きます。「Visual C# アイテム」 - 「データセット」を選択します。
  2. ここでは「名前」を "ItemDataSet.xsd" と、し「追加」ボタンを押します。

  1. 暫く待つと、"ItemDataSet.xsd" というファイルが開きます。
  2. 「サーバーエクスプローラ」 (Visual Studio Express Edition の場合は「データベース・エクスプローラ」) から、「データ接続」 内の接続 - 「テーブル」 - Item を "ItemDataSet.xsd" 内にドラッグ アンド ドロップします。

  1. "ItemDataSet.xsd" 内に以下のようなアイテムが追加されます。

  1. このアイテムを右クリックして出てきたポップアップ メニューから「構成」を選択します。

  1. 「TableAdapter 構成ウィザード」が開きます。

  1. 「詳細オプション」ボタンを押します。
  2. 「詳細設定」ダイアログ ボックスが開きます。以下のように設定して「OK」ボタンを押してみましょう。「データ テーブルの更新」のチェックを外しておきます。

  1. いくつかのソースコードが自動生成されます。
  2. 自動生成された "ItemDataSet.Designer.cs" というファイルの中を覗いて、どのような INSERT 文が生成されたか見てみることにしましょう。
  3. "INSERT" でファイルの中を検索してみると、以下のような記述が見つかります。
this._adapter.InsertCommand.CommandText = "INSERT INTO [dbo].[Item] ([Name]) VALUES (@Name)";
  1. INSERT 文が生成されているのが分かります。INSERT 後に、自動採番された値を取得するような記述は見当たりません。
  2. 以下のようなサンプル コードで試してみます。
using TypedDataSetUpdateWithIdentityColumn.ItemDataSetTableAdapters;
 
namespace TypedDataSetUpdateWithIdentityColumn
{
    class Program
    {
        static void Main()
        {
            using (var itemTableAdapter = new ItemTableAdapter()) {
                // Item テーブルの SELECT
                var itemTable = new ItemDataSet.ItemDataTable();
                itemTableAdapter.Fill(itemTable);
 
                // 新規 Item の INSERT
                var newItemRow = itemTable.NewItemRow();
                newItemRow.Name = "Hoge";
                itemTable.Rows.Add(newItemRow);
                itemTableAdapter.Update(itemTable);
            }
        }
    }
}
  1. デバッグ実行して、結果を確認してみましょう。

  1. 予想通り、新たな Id の値は取得できていません。
  2. ちなみに、このときの実際のデータベースのテーブルの値は以下のようになっています。

  1. では、設定を変えて試してみましょう。
  2.  先ほどと同じように、"ItemDataSet.xsd" ファイルを開き、中のアイテムを右クリック、ポップアップ メニューから「構成」を選び、「TableAdapter 構成ウィザード」上の「詳細オプション」ボタンを押して「詳細設定」ダイアログ ボックスを開きます。
  3. 今度は、以下のように「データ テーブルの更新」にチェックを入れて「OK」ボタンを押します。

  1. 先ほどと同様に、"ItemDataSet.Designer.cs" 中を覗いて、どのような INSERT 文が生成されたか見てみることにしましょう。
this._adapter.InsertCommand.CommandText = "INSERT INTO [dbo].[Item] ([Name]) VALUES (@Name);\r\nSELECT Id, Name FROM Item WHERE (Id = SCOPE_IDENTITY())";
  1. 今度は、INSERT の後に "SCOPE_IDENTITY()" と一致する Id を持った行を SELECT しているのが分かります。
  2. デバッグ実行して結果を確認してみます。

  1. 今度は、自動生成された Id の値が取得できています。

■ 4. LINQ to SQL の場合

LINQ to SQL の場合はどうでしょう。試してみましょう。

  1. Visual Studio で、新しい「コンソール アプリケーション」を作成し、「ソリューション エクスプローラー」でプロジェクト名を右クリックします。
  2. ポップアップ メニューから、「追加」 - 「新しい項目」を選択します。
  3. 「新しい項目の追加」ダイアログ ボックスが開きます。「Visual C# アイテム」 - 「LINQ to SQL クラス」を選択します。
  4. ここでは「名前」を "ItemDataClasses.dbml" と、し「追加」ボタンを押します。

  1. 暫く待つと、"ItemDataClasses.dbml" というファイルが開きます。

  1. 「サーバーエクスプローラ」 (Visual Studio Express Edition の場合は「データベース・エクスプローラ」) から、「データ接続」 内の接続 - 「テーブル」 - Item を "ItemDataClasses.dbml" 内にドラッグ アンド ドロップします。
  2. "ItemDataClasses.dbml" 内に以下のようなアイテムが追加されます。

  1. 自動生成された "ItemDataClasses.designer.cs" を開いて、中の "Item" クラスの "Id" プロパティを見てみましょう。
[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.Item")]
public partial class Item : INotifyPropertyChanging, INotifyPropertyChanged
{		
	// ……途中省略……

	[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Id", AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
	public int Id
	{
		get
		{
			return this._Id;
		}
		set
		{
			if ((this._Id != value))
			{
				this.OnIdChanging(value);
				this.SendPropertyChanging();
				this._Id = value;
				this.SendPropertyChanged("Id");
				this.OnIdChanged();
			}
		}
	}
  1. この中の、
AutoSync=AutoSync.OnInsert

という指定によって、行の挿入時に自動で Id 列の値を取得する SELECT 文が発行されます。

  1. 以下のサンプル コードで試してみましょう。
using System;
 
namespace TypedDataSetUpdateWithIdentityColumn
{
    class Program
    {
        static void Main()
        {
            using (var itemData = new ItemDataClassesDataContext()) {
                // 生成される SQL を標準出力ストリームに出力
                itemData.Log = Console.Out;
 
                // 新規 Item の INSERT
                var newItem = new Item { Name = "Hoge" };
                itemData.Item.InsertOnSubmit(newItem);
                itemData.SubmitChanges();
            }
        }
  1. 実行結果は、以下のようになります。

  1. 挿入直後の、"newItem" の Id に新たな値が入っているのが確認できます。
  2. コンソールには、以下のように出力されます。

  1. Linq to SQL によって、生成された SQL は以下のようなものです。
INSERT INTO [dbo].[Item]([Name])
VALUES (@p0)

SELECT CONVERT(Int,SCOPE_IDENTITY()) AS [value]
-- @p0: Input NVarChar (Size = 4000; Prec = 0; Scale = 0) [Hoge]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.0.30319.1
  1. 新たな Id 列の値を取得する SELECT 文が生成されているのが分かります。

■ 5. Entity Framework の場合

最後に、Entity Framework の場合を見てみましょう。

  1. Visual Studio で、新しい「コンソール アプリケーション」を作成し、「ソリューション エクスプローラー」でプロジェクト名を右クリックします。
  2. ポップアップ メニューから、「追加」 - 「新しい項目」を選択します。
  3. 「新しい項目の追加」ダイアログ ボックスが開きます。「Visual C# アイテム」 - 「ADO.NET Entity Data Model」を選択します。
  4. ここでは「名前」を "ItemModel.edmx" と、し「追加」ボタンを押します。

  1. 「Entity Data Model ウィザード」が開きます。
  2. ここでは、「データベースから生成」を選択し、「次へ」ボタンを押します。

  1. 次の画面では、既存のデータ接続を選択し、「エンティティ接続設定に名前を付けて App.Config に保存」では "ItemEntities" と入力して「次へ」ボタンを押します。

  1. 次の「データベース オブジェクトの選択」の画面では、「テーブル」の中の "Item" をチェックします。
  2. 「生成されたオブジェクトの名前を複数化または単数化する」にもチェックを入れ、「モデル名前空間」をここでは "ItemModel" として、「完了」ボタンを押します。

  1. 暫く待つと、"ItemModel.edmx" というファイルが開きます。中に以下のようなアイテムが追加されているのが分かります。

  1. 今度は、以下のサンプル コードで試してみましょう。
namespace TypedDataSetUpdateWithIdentityColumn
{
    class Program
    {
        static void Main()
        {
            using (var itemData = new ItemEntities()) {
                // 新規 Item の INSERT
                var newItem = new Item { Name = "Hoge" };
                itemData.AddToItems(newItem);
                itemData.SaveChanges();
            }
        }
    }
}
  1. 実行結果は、以下のようになります。

  1. 挿入直後の、"newItem" の Id に新たな値が入っているのが確認できます。

2011年03月10日

2011 MVP Global Summit 参加レポート

2011/2/27(日) ~ 3/6(日) に米国のシアトルとその周辺に行き、Microsoft 主催の 2011 MVP Global Summit に参加してきた。

世界中から千数百人の Microsoft MVP が集まり、沢山のセッションやパーティが開かれた。

とても貴重で、思い出に残る体験だった。

2011/02/27: 出発 (10:30頃) ~ 関西国際空港 ~ Delta

えちぜん鉄道 → JR サンダーバード → Delta (関西国際空港シアトル・タコマ国際空港)

  • 呟き (2011/02/27 10am): さてそろそろベルビューに向けて出発する準備完了。
  • 呟き (2011/02/27 11am): サンダーバードで大阪へ移動中。
  • 呟き (2011/02/27 1pm): はるかに乗り換え。 @ JR新大阪駅

  • 呟き (2011/02/27 3pm): チェックイン カウンターに並び中。
  • 呟き (2011/02/27 3pm): そじ坊で蕎麦。 @ 関西国際空港 (Kansai International Airport)
  • 呟き (2011/02/27 4pm): 出国した。
  • 呟き (2011/02/27 4pm): 関西空港のラウンジで色々充電しながらビール中。
  • 呟き (2011/02/27 5pm): 間もなく搭乗。 @ 関西国際空港 (Kansai International Airport)


機内食。

無事シアトル・タコマ国際空港に到着。そして入国。昨年までは必要だった I-94W と呼ばれる入国カードが不要になっていた。

空港内には S.T.S. (Satellite Transit System) と呼ばれる無人の地下鉄があって、これでターミナルから移動する。

シアトル・タコマ国際空港からは Shuttle ExpressHyatt Regency Bellevue へ移動。 Shuttle Express は片道 $19 位。タクシー ($30-40) より大分安い。バスよりは高いが、空港からホテルまでドア・ツー・ドアなのが魅力。

2011/02/27: ベルビュー 一日目

ベルビューでの宿泊先 Hyatt Regency Bellevue


ホテルの隣にある Bellevue Place

ホテル近くのベルビューの街並み。

  • 呟き (2011/02/27 1pm): Sea-Tac 空港から Shuttle Express で Hyatt。チェックイン完了。 @ Hyatt Regency Bellevue
  • 呟き (2011/02/27 2pm): ベルビュー。 @ Microsoft - Lincoln Square
  • 呟き (2011/02/27 4pm): シアトル名物の BEECHER'S のチーズと RED HOOK のビール。 @ Hyatt Regency Bellevue

Global Summit の Registration

ホテルの直ぐ近くにあるショッピング モール The Bellevue Collection (Bellevue Square, Bellevue Place, Lincoln Square) マイクロソフト ショップもある

"JAPAN DINNER"

日本・韓国の MVP との部屋飲みパーティ

昨年部屋飲みした韓国の MVP の人たちと、今年もご一緒した。

2011/02/28: ベルビュー 二日目

Microsoft Redmond campus へ移動

ホテルから Microsoft Redmond campus へはバスで移動。

Microsoft Redmond campus での Breakout Sessions

  • 呟き (2011/02/28 11am): MVP Global Summit 三つ目のセッション中。昨夜ちゃんと寝たので初日から眠くない。
  • 呟き (2011/02/28 1pm): 鶏肉メインの昼食終了。何かのセッション中。 #mvp11
  • 呟き (2011/02/28 1pm): 今年は渡米の前日とフライト中にこっちの朝に起きるようにしたので時差ボケがあまりないようだ。
  • 呟き (2011/02/28 1pm): デモのときの Visual Studio のソースコード エディターの文字は17ポイント以上が良いと思うよ。
  • 呟き (2011/02/28 5pm): ベルビューのQFCというスーパーで「安い栓抜きはないか」と店員に訊いたら「知らないけどビール売り場にあるかも」とか「あの辺」とか「これより安いのはない」と言いつつも店員によってその値段が全然違ってしかも実際にはそのどれも一番安くないやつだったり、アバウトな対応が面白かった。

Welcome Reception at Hyatt Regency Bellevue Hotel

Insiders Party at MVP Summit 2011

Bellevue Square の前を通って移動。

Insiders Party at MVP Summit 2011

2011/03/01: ベルビュー 三日目

キーノート

ホテルで朝食

バスでキーノート会場 (Meydenbauer Center) へ

間もなくキーノートが始まる。

キーノートが始まった。

Microsoft の現 CEO の Steven Ballmer 登場。

Seattle Mariners のマスコットのMariner Moose 登場。

キーノート後の昼食 (Meydenbauer Center)

Microsoft Redmond campus で午後のセッション

Commons での Evening Event の後、日本・韓国・米国の MVP との部屋飲みパーティ

2011/03/02: ベルビュー 四日目

  • 呟き (2011/03/02 7am): 昨晩は、韓国の人達のご招待で部屋飲み。韓国人、日本人、アメリカ人で10人以上。

ホテルで朝食の後 Microsoft Redmond campus へ

セッション


右端は Turbo PascalDelphi そして C# の父である Anders Hejlsberg

Attendee Party (Safeco Field in Seattle)


会場を Mariner Moose がうろついていた。

グラウンではロックバンドの他にバッティングやベースランニングが楽しめる。

その他、イチローが記者会見をしているプレス用の部屋やロッカールーム等、球場内の様様な施設を見学できた。

2011/03/03: ベルビュー 五日目~シアトル 一日目

ホテルで Post Summit Session

タクシーで Holiday Inn Seattle-SeaTac Intl Airport へ移動

  • 呟き (2011/03/03 0pm): MVP Global Summit 終了。タクシーでホテルから移動。
  • 呟き (2011/03/03 1pm): 今晩泊まるホテルに到着。 @ Holiday Inn Sea-Tac

シアトル一日観光

今回初めて Light Rail に乗った。一日券を買うと経済的。ホテル近くの駅からシアトルのダウンタウンへ。

Light Rail には、Seattle-SeaTac Airport から、シアトルのダウンタウンに中心にある終点の Westlake まで乗る。片道 $2.5 だが、$5 の一日券を買うと経済的。

シアトルのダウンタウンに到着。

ダウンタウンの中心に位置するショッピングモール "WESTLAKE CENTER" にやってきた。ここにはダイソーもある。

Starbucks を始めとしたシアトル コーヒーの店が至るところに。

徒歩で Pike Place Market へ。ここはシアトル名物の魚介類や野菜や果物、土産物等、様様なものが売られている市場。

徒歩で Beecher's Handmade Cheese へ。

Beecher's の名物メニュー "MAC & CHEESE"。

すぐ近くには Starbucks の一号店がある。ここはいつ来ても数人のパフォーマーが音楽を演奏している。

Pike Place Market は海沿いにある。

Pike Place Market では様様なものが売られている。

Pike Place Market を出て海沿いの道へ向かうと、レストランや土産物屋が多く並んでいる。この頃雨が強めに降り出した。シアトルは雨が多い街だ。

Seattle Aquarium (シアトル水族館) が有った。

雨が激しくなってきたので、土産物屋やゲームセンターで雨宿り。

漸く雨が上がった。

再び Light Rail に乗って、宇和島屋に向かう。中華街の近く。

宇和島屋は、シアトル最大の日本食スーパー。日本のスーパーマーケットのように日本の食品が売られている。日本で買うより大分高め。

宇和島屋店内には紀伊國屋書店があった。

また Light Rail に乗ってダウンタウンに戻ってきた。

今度は PACIFIC PLACE へ。ここもショッピング モール。

PACIFIC PLACE 内の COACH で財布等を購入。COACH は米国ニューヨークのブランド。

夜のシアトルを歩いて、再び海沿いのレストラン街へ。

シアトル名物のクラムチャウダーで有名なシーフード レストラン Ivar's にやってきた。

シアトルの地ビールは最高。

生牡蠣を注文。三種類の牡蠣を楽しんだ。その他のシーフードもとても美味しかった。しかもリーズナブル。

四度 Light Rail に乗って、シアトル・タコマ空港近くの Holiday Inn Seattle-SeaTac Intl Airport へ戻った。

  • 呟き (2011/03/04 0am): シアトル観光に行ってきた。夕食は Ivars で地ビールや生牡蠣、クラムチャウダーなどを堪能。SEA-TAC空港近くのホテルにチェックインした。

2011/03/04: シアトル 二日目

  • 呟き (2011/03/04 5am): さてそろそろ帰国しないと。

ホテルのシャトルバスでシアトル・タコマ国際空港

  • 呟き (2011/03/04 7am): ホテルの朝食後、SEA-TAC空港に。セキュリティ チェックを通過した。
  • 呟き (2011/03/04 8pm): 休憩中。 @ Seattle-Tacoma International Airport (SEA)
  • 呟き (2011/03/04 8pm): MVP Global Summit 中はいつもネット環境に不自由してたけど、終わってからは今までシアトルで全然苦労してない。
  • 呟き (2011/03/04 9pm): 今日も Ivar's。スモークサーモン クラムチャウダー。 @ Ivar's Seafood Bar - SeaTac Airport

関西国際空港行きの Delta

隣席の米国人夫妻と話す。なんでも関西にいる妹のところに行って観光するとのこと。

そして、離陸から4時間程経ったときだった。機内に機長からの声が流れた。
「電気系統の故障が見つかったので、Uターンしてシアトルに戻りつつある」という内容だった。

なんと中間地点まで飛んだのにこれから引き返すというのだ。
米国人の奥さんはショックで泣き出してしまった。

そしてシアトル・タコマ国際空港に戻ってきた

乗客はホテルと別の便の手続きをするために長い列を作った。

この手続には夜9時から1時間位掛かったが、ホテルのクーポンをくれただけで、帰りの飛行機は手配してくれる訳ではない。
帰りの飛行機を勝手に電話を掛けて確保してくれとのことだった。
多くの日本人が帰りの飛行機を確保出来ずに困っていた。

  • 呟き (2011/03/04 9pm): I'm still in Seattle. 飛行機が帰路なかばでシアトルに戻ってしまった。計器の故障とのことだが、詳しい原因は不明。次の飛行機がどうなるか待ち中。
  • 呟き (2011/03/04 10pm): まだSEA-TAC にいる。電話で交渉してなんとか明日の成田便を押さえられた。最後の1席だった。関空行きは月曜夕方着が最速。

Delta に電話をかけ、なんとか帰りの飛行機を予約した。何も伝わっていないので、事情を説明するところからしなければならない。
結局翌日の関西空港行きは既にいっぱいで取れず、同日の成田空港行きを取った。成田から自宅までの交通費分余計に掛かるが仕方がない。
その後、用意された Doubletree Hotel Seattle Airport にシャトルバスで向かう。

  • 呟き (2011/03/04 11pm): Delta が用意してくれた Doubletree Seattle Airport にチェックイン完了。それにしても半分飛んでシアトルに戻るとは 思わなかった。
  • 呟き (2011/03/05 0am): Delta に電話して帰りの便を頼んだら、明日LAに飛んでホノルルに飛んでそこで一泊して、明朝の便で、とか訳のわからないことを言う。それか翌々日まで待てと。ちょっと何言ってるか分からないので、関空は諦めて成田に飛ぶことにした。JR の切符が無駄になり、成田からの足はこれから。
  • 呟き (2011/03/05 0am): 成田からの足確保完了。これで帰りの飛行機がまた壊れたりしなければ、日曜日の夜10時過ぎに帰宅予定。

2011/03/05: シアトル 三日目

Doubletree Hotel Seattle Airport からシャトルバスでシアトル・タコマ国際空港 (四回目) に

  • 呟き (2011/03/05 7am): はやおきして時間があるのでゆっくりとホテルで朝食中。 @ Doubletree Hotel Seattle Airport

Doubletree Hotel Seattle Airport を出発。

Delta (シアトル・タコマ国際空港成田国際空港) → ANA (成田国際空港小松空港) → 空港連絡バス → 帰宅 (3/6 22時過ぎ)

  • 呟き (2011/03/05 9am): 今日もセキュリティ チェックを通過。 @ Seattle-Tacoma International Airport (SEA)

  • 呟き (2011/03/05 9am): Ivar's (三日目) @ Seattle-Tacoma International Airport (SEA)
  • 呟き (2011/03/05 9am): Taco Breakfast @ Anthony's Airport Grill - SeaTac Airport

昨日の飛行機で隣り合わせた米国人夫妻と空港でばったり。関西空港行きの飛行機が取れたとのこと。少し話して、お互いの幸運を願って別れた。


これから搭乗する Delta 機。今度のは、成田行きなので、各座席には映画などが観られるモニターが付いている。

  • 呟き (2011/03/05 11am): 今日乗るやつは壊れないかな。 @ Seattle-Tacoma International Airport (SEA)
  • 呟き (2011/03/05 11am): さて搭乗。

無事成田空港に到着した Delta 機。

  • 呟き (2011/03/06 4pm): 今回は無事成田に到着。隣の席が偶然マレーシアのMVP で雑談しながら帰ってきた。楽しかった。
  • 呟き (2011/03/06 5pm): 取り敢えずそじ坊でざる蕎麦。 @ 成田国際空港 (Narita International Airport)
  • 呟き (2011/03/06 6pm): 【今日の機内で聞きたくない一言】「お客様の中にパイロットはいらっしゃいますか?」

成田空港小松空港行きの ANA の小型機に乗り換え。

  • 呟き (2011/03/06 9pm): 小松空港に到着。
  • 呟き (2011/03/06 10pm): 7日半ぶりに帰宅した。

買ってきたもの

シアトルに行く前から欲しかったものを少し買って帰った。

シアトル名物 Beecher's のチーズとクラッカーと RED HOOK のビール。それぞれ米国でも食べたり飲んだりした。

前回の 2010 MVP Global Summit の Atendee Party で米国の人に勧められたバーボン「KNOB CREEK

Microsoft 製のマウスや Web カメラ。

その他、COACH の財布、シアトル チョコレート、子供向けの土産等。

2011年03月31日

Visual Studio のテスト機能に関する記事

Visual Studio のテスト機能に関する記事

2011年05月20日

Sleep Sort の C#/.NET4 版

※ 参考: Sleep sortの各言語での実装まとめ

// Sleep Sort C#/.NET4 版

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

public static class SleepSort
{
    public static IEnumerable<T> Sort<T>(this IEnumerable<T> collection, Func<T, int> convert)
    {
        var queue = new ConcurrentQueue<T>();
        collection.AsParallel().ForAll(
            value => {
                Thread.Sleep(convert(value));
                queue.Enqueue(value);
            }
        );
        return queue;
    }
}  

static class Program
{
    static void Main()
    {
        var sortedCollection = new[] { 30, 200, 70, 400, 800, 35 }.Sort(x => x);
        foreach (var item in sortedCollection)
            Console.WriteLine(item);
    }
}

2011年06月14日

[Event] VSUG Day 2011 Summer

__120%C3%97120.gif
▼日付: 2011年06月19日(日)受付9:30~
▼会場: マイクロソフト品川オフィス 31F ・MAP
▼参加費用: 無料 (VSUG 会員登録が必要となります)
▼詳細・申込み: こちら

2011年08月01日

[Event] Hokuriku.NET Vol.7 in 福井 (2011.07.30)

Hokuriku.NET Vol.7 in 福井」というイベントを開催した。

レポートはこちらに書いた。

2011年08月21日

[Event] こみゅぷらす 『Tech・Aid 2011 夏』

以下のイベントを開催した。

2007年から毎年夏にやっている恒例のこみゅぷらすイベントで、今年は、震災の影響で開催が延期されてしまった Microsoft Tech・Ed Japan の代わりのイベントである「Tech Party 2011」の一環で開催した。

例によって、飲み放題付き。

こみゅぷらす 『Tech・Aid 2011 夏』
開催日時 2011年8月20日(土) 10:00~17:00
開催場所 新宿
主催 こみゅぷらすcodeseek日本C#ユーザー会
関連サイト Tech Party 2011
会場
  • セッション 1: 『最新 ASP.NET Web 開発オーバービュー』
    日本マイクロソフト株式会社 井上 章氏
  • セッション 2: 『Windows Phone アプリケーション開発実践 ~XNA 編~』
    日本マイクロソフト株式会社 田中 達彦 氏
  • ライトニングトークス
    • 『Kinect プログラミング始めました』
      田中孝佳(@tanaka_733)氏
    • 『linq.js - Linq to Objects for JavaScript』
      neuecc氏
    • 『5 分でわかった気になる ASP.NET MVC のローカライズ』
      しばやん氏
    • 『ある意味データの視覚化』 池原 大然氏
  • セパレート セッション
    • 『1スクリーン独占 仕込み一切なし! ぶっつけ本番! ライブコーディング』
      岩永 信之氏
    • 『@IT のチューニング話追補』』
      亀川 和史氏
    • 『オフショアネタ』
      衣川 朋宏氏
    • 『HTML5 on ASP.NET』
      小島 富治雄氏
    • 『Visual Studio LightSwitch 2011』
      宇宙仮面氏
    セパレート セッションの様子
  • 抽選会
    抽選会の景品
  • 懇親会
    懇親会の様子

2011年09月08日

[Event] Tech Party 2011 in 北陸

Tech Party 2011 in 北陸
日時 8月27日(土) 10:00~17:00
会場 石川工業高等専門学校 (石川県河北郡津幡町北中条タ1)
参加費 無料
Webサイト http://atnd.org/events/18440
関連イベント Tech Party 2011
会場の様子
  • 挨拶 ― @kabakiyo さん
  • 『Windows Phoneのアプリケーションライフサイクルについて』 ― @kabakiyo さん
  • 『デスクトップエージェント「小雪ちゃん」の紹介』 ― 石川高専4年生 林 真利奈 さん
  • 『これまでに作った作品の紹介』 ― 石川高専・専攻科1年 越田 和基さん
  • 『群集シミュレーションの開発』 ― 石川高専・専攻科2年 奥成 貴大さん
  • 『フィジカルコンピューティングのすゝめ』 ― 坪倉 輝明さん
  • 『Lync を使って、ここまでできる! (リアルタイム コミュニケーション開発)』 ― 日本マイクロソフト松崎 剛さん
  • 『Windows 7 活用術 ~おさらい編~』 ― @ramu_mystery さん
  • 『ASP.NET + HTML5』 ― @Fujiwo さん
  • 抽選会 日本マイクロソフトさんから多数のノベルティをいただいた
  • 懇親会

2011年09月12日

C# を作ってみた

最近我が家で、滑石(ろうせき)(やすり)紙鑢(サンドペーパー)で削り、勾玉(まがたま)等の様様な形を作るのが流行っている。

滑石は加工し易い石で、これを棒鑢や荒い (80番程度の) 紙鑢 で大雑把に形を決めた後、細部は精密鑢で削っていく。最後は細かい (1500番程度の) 耐水性紙鑢に水を付けて擦り、新聞紙で磨いて艶を出す。

子供達とやっていると、結構楽しい。

C# を作ってみた (ちゃんと ♯ じゃなくて #)。

DSCF7713.JPG

2011年09月14日

[Tool] WebMatrix

Web アプリケーションを簡単編集できる無償ツール WebMatrix ダウンロードはこちら

WebMatrix は Microsoft 製の Windows 上に Web サイトを構築するための無料ツール。 簡単に ASP.NET ベースや PHP ベースの Web サイトを準備できる。 必要なデータベースを同時にインストールしたり、後から編集したりすることもできる。

ASP.NET の新しい構文である Razor にも対応。

※ ちなみに、以前リリースされた ASP.NET Web Matrix とは別物。

WebMatrix 自体も Microsoft Web Platform Installer によって簡単にインストールできる。 必要なコンポーネント (適切なバージョンの .NET Framework、SQL Server 関連コンポーネント、IIS Express 等) も自動でインストールされる。 ASP.NET ベースや PHP ベースの Web アプリケーション (CMS 等) のインストールも行うことができる。

使い始めるには先ず「Microsoft WebMatrix おすすめ学習コンテンツ」で紹介されているコンテンツがお勧め。現時点では以下のようなものが紹介されている。

他には以下の記事が参考になる:

2011年09月15日

[Event] Microsoft BUILD 2011 (9.13-16) Day1-2 関連記事・コンテンツのまとめ

Microsoft BUILD 2011 (9.13-16, Anaheim, California US) Day1-2 関連記事・コンテンツのまとめ

Microsoft の開発者向けカンファレンス「BUILD」が開催されている。

Windows 8 を初めとして非常に多くの新たなテクノロジーが発表され、Preview 版等が使用できるようになっている。

以下で 関連記事・コンテンツを紹介したい。

■ Day 1 (9.13):

■ Day 2 (9.14):

■ アップデート:

2011年09月21日

F# も作ってみた

# 「C# を作ってみた」の続き。

C# に続いて F# も作ってみた。

DSCF7807.JPG

2011年10月10日

Visual Studio のロゴっぽい何かを作ってみた

以前 C#F# を作ってみた。

C# と F#。

今度は Visual Studio のロゴっぽいものに挑戦。 

  1. 削る前の石。
  2. 鉛筆で下書きし、金鋸と大きめの鉄工 (やすり)、ドリルで大まかな形を作る。石とは云っても其れ程固くないので割りと簡単に削れる。
  3. 小さい鑢で更に形を作っていく。余り細くすると折れるのでこの辺り迄とした。
  4. 100番程度の粗いものから 200番、400番と徐々に細かい紙鑢 (かみやすり)で削っていく。
  5. 1500番程度の耐水性紙鑢で磨き、最後は新聞紙で擦って艶を出す。
  6. 裏はこんな感じ。
Windows のロゴっぽい何かにも挑戦。
こちらは @ (アットマーク)

2012年03月29日

[Event] Windows Phone Hackathon 2012 in Fukui 開催予定 (2012-04-14, 21)

福井で Windows Phoneハッカソンが開催される。

14日はアイデアソンで、Windows Phone 開発の概要説明やアイディアの出し合いなどが行われる。21日は、開発し、実際にマーケットに登録。

Windows Phone Hackathon 2012 in Fukui
日時 2012年4月14日(土) 10:00~夕方、 4月21日 10:00~18:00
会場 福井県産業情報センター システム設計室 (福井県坂井市丸岡町熊堂第3号7番地1-16)
参加費用 4月14日: 500円、4月21日: 500円 (会場費)
主催 Hokuriku.NET
詳細/申し込み http://atnd.org/events/26574

2012年03月31日

2012 MVP Global Summit

mvp2012_0053.JPG

2012 MVP Global Summit (2012/02/28-03/02) に参加してきた。


参加レポート: 2012 MVP Global Summit 参加レポート - slideshare


2012年04月04日

[Event] Windows Developer Days

開発者の皆様へ ~ Windows 8 のアプリケーション開発を始めませんか。 | Windows Developer Days
Windows Developer Days
日時 2012年4月24日(火)~25日(水) 10:00-18:00
会場 ザ・プリンスパークタワー東京
参加費 ¥84,000 (早期割引 4月18日 16:59 迄 税込¥63,000)
詳細/参加登録 Windows Developer Days

2012年04月10日

モンティ・ホール問題

モンティ・ホール問題 というのがある。 アメリカのゲームショー番組の中で行われた以下のようなゲームに関する問題である。

  1. 三つのドアのどれか一つの後ろに当たりの商品が隠されている。残りの二つはハズレだ。
  2. プレイヤーである番組参加者は、一つのドアを選ぶ。
  3. 番組司会者のモンティは、残り二つのドアのうちからハズレのドアを一つ開けて見せる。
  4. モンティはプレイヤーに「ドアを選びなおしても良い」と言う。
  5. プレイヤーはドアを選びなおすべきだろうか?
montyhallproblem0.png
当たりはどれか一つ。

この問題の正解は、「選びなおした方が良い。何故なら当たる確率が倍になるから」というものだ。

しかし、これを正解とするということに納得しない人が多いらしい。直感と異なるからだ。

教授レベルの数学者を含む多くの人が反論したらしい。

大きな論争となったこの問題は、結局コンピューター上でのシミュレーションで決着がついたそうだ。

とても興味深い題材なので、私も C# でシミュレーションをやってみた。

// モンティ・ホール問題 - Wikipedia
// http://ja.wikipedia.org/wiki/%E3%83%A2%E3%83%B3%E3%83%86%E3%82%A3%E3%83%BB%E3%83%9B%E3%83%BC%E3%83%AB%E5%95%8F%E9%A1%8C

using System;
using System.Linq;

namespace モンティ・ホール問題
{
    static class プログラム
    {
        static void Main(string[] コマンドライン引数)
        { シミュレーター.シミュレート(コマンドライン引数から繰り返し回数を得る(コマンドライン引数)); }

        static int コマンドライン引数から繰り返し回数を得る(string[] コマンドライン引数)
        {
            if (コマンドライン引数.Length < 0)
            {
                int 繰り返し回数;
                if (int.TryParse(コマンドライン引数[0], out 繰り返し回数))
                    return 繰り返し回数;
            }
            const int デフォルトの繰り返し回数 = 1000000;
            return デフォルトの繰り返し回数;
        }
    }

    static class シミュレーター
    {
        public static void シミュレート(int 試す回数)
        { 結果.表示(選びなおす場合に当たる確率: シミュレート(試す回数, 選びなおす: true),
            選びなおさない場合に当たる確率: シミュレート(試す回数, 選びなおす: false)); }

        static double シミュレート(int 試す回数, bool 選びなおす)
        {
            var 当たりの回数 = Enumerable.Range(1, 試す回数).Count(_ =< ゲーム.プレイする(選びなおす));
            var 当たる確率 = (double)当たりの回数 / 試す回数;
            結果.表示(選びなおす, 試す回数, 当たりの回数, 当たる確率);
            return 当たる確率;
        }

        static class 結果
        {
            public static void 表示(double 選びなおす場合に当たる確率, double 選びなおさない場合に当たる確率)
            { 表示(選びなおす場合と選びなおさない場合の当たる確率の比:
                選びなおす場合に当たる確率 / 選びなおさない場合に当たる確率); }

            public static void 表示(double 選びなおす場合と選びなおさない場合の当たる確率の比)
            { Console.WriteLine("・結論: 選びなおす場合は、選びなおさない場合に比べて、{0} 倍当たりやすい。",
                選びなおす場合と選びなおさない場合の当たる確率の比); }

            public static void 表示(bool 選びなおした, int 試した回数, int 当たった回数, double 当たる確率)
            { Console.WriteLine("・{0}場合は、当たりの回数は: {1} 回中 {2} 回で、当たる確率は {3}。",
                選びなおした ? "選びなおした" : "選びなおさなかった", 試した回数, 当たった回数, 当たる確率); }
        }
    }

    static class ゲーム
    {
        const int 全ドアの数 = 3;

        public static bool プレイする(bool 選びなおす)
        {
            var 当たりのドア = ランダムなドア();
            var プレイヤーが選択したドア = ランダムなドア();
            var モンティの開けたドア = 残りのドアから一つ(当たりのドア, プレイヤーが選択したドア);

            if (選びなおす)
                プレイヤーが選択したドア
                    = 残りのドアから一つ(プレイヤーが選択したドア, モンティの開けたドア);

            return プレイヤーが選択したドア == 当たりのドア;
        }

        static int 残りのドアから一つ(int 当たりのドア, int プレイヤーが選択したドア)
        {
            return 当たりのドア == プレイヤーが選択したドア
                   ? 残りのドアからランダムに一つ(除外するドア: 当たりのドア)
                   : 残りのドアからどれでも一つ(一つ目の除外するドア: 当たりのドア,
                                    二つ目の除外するドア: プレイヤーが選択したドア);
        }

        static int 残りのドアからランダムに一つ(int 除外するドア)
        { return 或るドアから数えてX番目のドア(除外するドア,
                    そのドアから数えてX番目: ランダム.一から或る数までの乱数(全ドアの数 - 1)); }

        static int 或るドアから数えてX番目のドア(int 或るドア, int そのドアから数えてX番目)
        { return (或るドア + そのドアから数えてX番目) % 全ドアの数; }

        static int 残りのドアからどれでも一つ(int 一つ目の除外するドア, int 二つ目の除外するドア)
        { return Enumerable.Range(0, 全ドアの数)
                    .First(ドア =< ドア != 一つ目の除外するドア && ドア != 二つ目の除外するドア); }

        static int ランダムなドア()
        { return ランダム.零から或る数までの乱数(全ドアの数); }
    }

    static class ランダム
    {
        static readonly Random 乱数 = new Random();

        public static int 零から或る数までの乱数(int 或る数)
        { return 乱数.Next(或る数); }

        public static int 一から或る数までの乱数(int 或る数)
        { return 乱数.Next(或る数) + 1; }
    }
}

以下のように正解の通りの結果となった。

  • 選びなおした場合は、当たりの回数は: 1000000 回中 666662 回で、当たる確率は 0.666662。
  • 選びなおさなかった場合は、当たりの回数は: 1000000 回中 333802 回で、当たる確率は 0.333802。
  • 結論: 選びなおす場合は、選びなおさない場合に比べて、1.99717796777731 倍当たりやすい。

実は、プログラムを書いていく過程で、問題が整理されていったため、途中から実行する迄もなく結果は明白なように感じていた。

以下のように考えたのだ。

  1. 最初にプレイヤーがドアを選んだ時点で、そのドアが当たりである確率は、1/3 だ。
  2. そのとき、プレイヤーが選ばなかった残りの二つのドアが当たりである確率も、それぞれ 1/3 だ。
  3. 即ち、残りの二つのドアのどちらかが当たりである確率は 2/3。
  4. つまり、「残りの二つのドアのどちらかが当たりである確率」は「プレイヤーが最初に選んだドアが当たりである確率」の倍。
  5. ところが、モンティは、「残り二つのドアのどちらがハズレか」を必ず教えてくれる。
  6. 残りの二つのドアのどちらかが当たりである確率は 2/3 だが、残りの二つのドアのうちモンティが開けて見せた方が当たりである確率は 0 で、残りの二つのドアのうちモンティが開けなかった方が当たりである確率は 2/3。
  7. 残りの二つのドアのうちモンティが開けなかった方を選びなおした方が、当たる確率が倍、ということだ。
montyhallproblem.png

続きを読む "モンティ・ホール問題" »

2012年05月18日

[Event] VSUG Day 2012 Summer

vsugday2012summer80665.png

▼日付: 2012年06月16日(土)受付9:55~予定
▼会場: マイクロソフト品川オフィス 31F
▼参加費用: 無料(VSUG 会員登録が必要となります)

詳細/申し込み: http://vsug.jp/tabid/228/EventID/21/Default.aspx

2012年11月14日

[Event] VSUG Day 2012 Winter

2012.12.15(Sat) マイクロソフト品川ビル31F

▼日付: 2012年12月15日(土) 受付9:30~予定
▼会場: マイクロソフト品川オフィス 31F
▼参加費用: 無料 (VSUG 会員登録が必要となります)

詳細/申し込み: http://vsug.jp/tabid/228/EventID/22/Default.aspx

2012年11月20日

[Windows 8][Windows Store アプリ][XAML] Windows Store アプリと Windows Phone アプリ、Silverlight アプリ、WPF アプリでソースコードを共通化する方法に関する記事

Windows Store アプリと Windows Phone アプリ、Silverlight アプリ、WPF アプリは、どれも XAML を使って開発することができ、共通する部分も多い。

そこで、これらでソースコードを共通化する方法に関する記事を紹介する。

先ずは @IT の岩永 信之氏の記事から。"Portable Class Library" と呼ばれるマルチ プラットフォーム クラス ライブラリによるソースコードの共通化、MVVMパターンによるビューとモデルの分離方法について、判りやすく書かれている。

  • XAMLファミリ共通開発のすゝめ - @IT

続いて Windows Store アプリ開発に関する記事を多く書かれている山本 康彦氏のスライドから。

こちらでは、スライドの30ページ目から Portable Class Library について書かれている。

このスライドでは以下の記事が引用されている:

この記事では、MVVM パターンを用いている。各プラットフォーム用に View を分け、Model と ViewModel を Portable Class Library で共通化することを推奨している。

Portable Class Library に関して幾つか他の記事を紹介する:

プラットフォーム間での XAML の違いについては、以下の記事も参考になる:

2012年11月28日

[C#][ラムダ式][LINQ][式木] 匿名メソッドとラムダ式の違い

Expression

この記事では、匿名メソッドとラムダ式の意味の違いについて考えてみたい。

■ 同じように使える匿名メソッドとラムダ式

匿名メソッドとラムダ式は、同じように使うことができる場面が多い。

例えば、以下のようなデリゲートを引数にとるメソッドがあったとして、

using System;
using System.Collections.Generic;

static class Enumerable
{
    // 述語に基づいて値のシーケンスをフィルター処理
    public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
        foreach (var item in source) {
            if (predicate(item))
                yield return item;
        }
    }
}

この predicate には、通常のメソッドも匿名メソッドもラムダ式も渡すことができ、いずれも同じ結果となる。

using System;

class Program
{
    static bool IsEven(int number)
    {
        return number % 2 == 0;
    }

    static void Main()
    {
        var numbers = new[] { 3, 7, 1, 6, 4, 5 };

        // 普通のメソッドをデリゲートに入れて渡す例 
        var evenNumbers1_1 = numbers.Where(new Func<int, bool>(IsEven));
        // 上に同じ
        var evenNumbers1_2 = numbers.Where(IsEven);
        // 匿名メソッドを使った例
        var evenNumbers2 = numbers.Where(delegate(int number) { return number % 2 == 0; });
        // ラムダ式を使った例
        var evenNumbers3 = numbers.Where(number => number % 2 == 0);
    }
}

いずれの場合も、同じ型のデリゲートとして渡されることになるのだ。

以下の例でも同じ型のデリゲートとして受けられることが判る。

     Func<int, bool> delegate1_1 = new Func<int, bool>(IsEven);
    Func<int, bool> delegate1_2 = IsEven; // 上と同じ
    Func<int, bool> delegate2 = delegate(int number) { return number % 2 == 0; };
    Func<int, bool> delegate3 = number => number % 2 == 0;

上の例では、一行目 delegate1_1 の例と二行目 delegate1_2 の例は全く同じ意味だ。書き方が違うだけの、単なる糖衣構文 (syntax sugar) に過ぎない。

では、三行目の匿名メソッドを使った delegate2 の例 と四行目のラムダ式を使った delegate3 の例も、単なる糖衣構文 (syntax sugar) なのだろうか?

確かに、ラムダ式を使った方が、型推論の恩恵を存分に受けられ、書き方がぐっとシンプルになる。だが、書き方だけの違いなのだろうか?

■ 匿名メソッドとラムダ式で挙動が異なる例

今度は、両者で違いが出る例を見てみよう。

LINQ to SQL を使った例だ。

予め SQL Server に Employee というシンプルなテーブルを用意した。

Employee テーブル

次に、Visual Studio でプロジェクトを作り、EmployeeDataClasses.dbml という名前で「LINQ to SQL クラス」を追加した。

「LINQ to SQL クラス」を追加
・LINQ to SQL - ラムダ式で書いた場合

では、ラムダ式を使ってデータ アクセスを行う例から見てみよう。

// LINQ to SQL の例 - ラムダ式で書いた場合
using LambdaExpressionSample; // EmployeeDataClassesDataContext の名前空間
using System;
using System.Linq;

class Program
{
    static void Main()
    {
        var dataContext = new EmployeeDataClassesDataContext();
        dataContext.Log = Console.Out; // 発行された SQL をモニターする為に、コンソールに出力

        var data1 = dataContext.Employee.Where(employee => employee.Name.Contains("山"));
        var data2 = data1.OrderBy(employee => employee.Name);
        var data3 = data2.Select(employee => employee.Name);

        data3.ToList().ForEach(Console.WriteLine);
    }
}

この例で発行された SQL は、以下の通り。

SELECT [t0].[Name]
FROM [dbo].[Employee] AS [t0]
WHERE [t0].[Name] LIKE @p0
ORDER BY [t0].[Name]
-- @p0: Input NVarChar (Size = 4000; Prec = 0; Scale = 0) [%山%]

三行に分けて書いているのに、SQL の発行は data3.ToList() のときの一回だけだ。data1、data2、data3 と三回も戻り値を受けているのに、ToList() されるまで評価が遅延されている訳だ。

SQL の発行は一回だけだが、where は WHERE、OrderBy は ORDER BY、Select で Name だけなのは SELECT で Name だけと、ちゃんと狙い通りのものが発行されているのが判る。

LINQ to SQL (や Entity Framework) の凄いところだ。

因みに、上ではメソッド構文を使って書いているが、クエリ構文を使って書くこともできる。

以下の二つは意味的には同じで、糖衣構文 (syntax sugar) だ。

// メソッド構文
var data3 = dataContext.Employee
            .Where(employee => employee.Name.Contains("山"))
            .OrderBy(employee => employee.Name)
            .Select(employee => employee.Name);
// クエリ構文
var data3 = from employee in dataContext.Employee
            where employee.Name.Contains("山")
            orderby employee.Name
            select employee.Name;
・LINQ to SQL - 匿名メソッドで書いた場合

次に、この例を匿名メソッドに置き換えてやってみよう。ラムダ式が匿名メソッドの単なる糖衣構文 (syntax sugar) なら同じ結果になる筈だ。

// LINQ to SQL の例 - 匿名メソッドで書いた場合
using LambdaExpressionSample;
using System;
using System.Linq;

class Program
{
    static void Main()
    {
        var dataContext = new EmployeeDataClassesDataContext();
        dataContext.Log = Console.Out; // 発行された SQL をモニターする為に、コンソールに出力

        var data1 = dataContext.Employee.Where(delegate(Employee employee) { return employee.Name.Contains("山"); });
        var data2 = data1.OrderBy(delegate(Employee employee) { return employee.Name; });
        var data3 = data2.Select(delegate(Employee employee) { return employee.Name; });

        data3.ToList().ForEach(Console.WriteLine);
    }
}

結果はこうなった。

SELECT [t0].[Id], [t0].[Name]
FROM [dbo].[Employee] AS [t0]

ラムダ式の場合と大きく異なる。

WHERE による行の絞り込みも SELECT による列の絞り込みも ORDER BY による並べ替えも全然反映されていない。

つまり、この例では、LINQ to SQL で単純に Employee テーブルの全列、全行を取ってきて、後は LINQ to Object でオンメモリで処理している訳だ。

これでは全く非効率だ。ラムダ式を使うべき、と云うことになろう。

・LINQ to SQL - ラムダ式と匿名メソッドを混在させた場合

序でに、両者を混在させた例も見ておこう。

// LINQ to SQL の例 - ラムダ式と匿名メソッドを混在させた場合
using LambdaExpressionSample; // EmployeeDataClassesDataContext の名前空間
using System;
using System.Linq;

class Program
{
    static void Main()
    {
        var dataContext = new EmployeeDataClassesDataContext();
        dataContext.Log = Console.Out; // 発行された SQL をモニターする為に、コンソールに出力

        var data1 = dataContext.Employee.Where(employee => employee.Name.Contains("山"));
        var data2 = data1.OrderBy(delegate(Employee employee) { return employee.Name; });
        var data3 = data2.Select(employee => employee.Name);

        data3.ToList().ForEach(Console.WriteLine);
    }
}

結果はこうなった。

SELECT [t0].[Id], [t0].[Name]
FROM [dbo].[Employee] AS [t0]
WHERE [t0].[Name] LIKE @p0
-- @p0: Input NVarChar (Size = 4000; Prec = 0; Scale = 0) [%山%]

SQL に反映されているのは、ラムダ式を使った Where までで、匿名メソッドより後ろは LINQ to Object で処理されているのが判る。

この例で、ラムダ式は匿名メソッドと意味的に同じものではない、ということが判った。

■ 匿名メソッドとラムダ式の違い

では、両者はどう意味が違うのだろうか。

上の LINQ to SQL の例の Where の部分を比べてみよう。

    // ラムダ式を使った例
    var data1 = dataContext.Employee.Where(employee => employee.Name.Contains("山"));
    // 匿名メソッドを使った例
    var data1 = dataContext.Employee.Where(delegate(Employee employee) { return employee.Name.Contains("山"); });

Visual Studio を使って、両者の Where の部分を右クリックし、「定義へ移動」してみよう。

一見同じ Where メソッドを呼んでいるようだが、異なった Where メソッドを呼んでいることが判るだろう。

// ラムダ式を使った例から呼ばれる Where メソッド
namespace System.Linq
{
    public static class Queryable
    {
        public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
    }
}
// 匿名メソッドを使った例から呼ばれる Where メソッド
namespace System.Linq
{
    public static class Enumerable
    {
        public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
    }
}

匿名メソッドの方は Func<TSource, bool>、即ちデリゲートだが、ラムダ式の方は Expression と云う違うもので受けている。

参考までに、Func と Expression の定義は以下のようになっている。

// Func はデリゲート
namespace System
{
    public delegate TResult Func<in T, out TResult>(T arg);
}
// Expression はデリゲートではない
namespace System.Linq.Expressions
{
    public sealed class Expression<TDelegate> : LambdaExpression;
}

ラムダ式は、あくまでも「式」として渡されているのだ。

対して、匿名メソッドは Expression として扱うことができない。

試してみよう。

using LambdaExpressionSample; // EmployeeDataClassesDataContext の名前空間
using System;
using System.Linq.Expressions;

class Program
{
    static void Main()
    {
        // デリゲートとして使う分には、どちらでも OK
        Func<Employee, bool> delegate1 = employee => employee.Name.Contains("山");
        Func<Employee, bool> delegate2 = delegate(Employee employee) { return employee.Name.Contains("山"); };

        // だが、Expression として使えるのはラムダ式の方だけ
        Expression<Func<Employee, bool>> expression1 = employee => employee.Name.Contains("山");
        //Expression<Func<Employee, bool>> expression2 = delegate(Employee employee) { return employee.Name.Contains("山"); }; // コンパイル エラーになる
    }
}

匿名メソッドの方は、「匿名メソッド式を式のツリーに変換することはできません」と云うコンパイル エラーとなる。

「匿名メソッド式を式のツリーに変換することはできません」と云うコンパイル エラー

即ち、ラムダ式を Expression として扱っている場合は、匿名メソッドは代わりにはならない、ということになる。

ラムダ式は匿名メソッドの糖衣構文 (syntax sugar) などではないのだ。

一般的には、ラムダ式を使うことで書き方もシンプルになることだし、匿名メソッドは使わずにラムダ式で書くようにした方が良いだろう。

■ 今回のまとめ

今回は、匿名メソッドとラムダ式の意味の違いについて考察した。

ラムダ式はデリゲートのみならず、式としても扱うことができる、と云うことだ。

LINQ to SQL のような LINQ プロバイダーでは、ときにラムダ式を式として扱うことが大切だろう、と想像できる。

だが、それ以外のプログラミングでラムダ式を式として扱って便利なことってあるんだろうか?

と云う訳で、次回は、ラムダ式を Expression として扱うと便利な例について書く予定だ。

2012年11月29日

[C#][ラムダ式][式木] Expression として扱えるラムダ式と扱えないラムダ式

Expression

前回、「匿名メソッドとラムダ式の違い」と云う記事で、匿名メソッドとラムダ式の意味の違いについて考えた。

それについて、少し補足しておきたい。

「ラムダ式を Expression として扱っている場合は、匿名メソッドは代わりにはならない」と述べたが、ラムダ式であれば何でも Expression として扱える訳ではないのだ。

■ Expression として扱えるラムダ式

ラムダ式として足し算を行うだけの (x, y) => x + y と云うシンプルなものを用意し、これを Expression で受けてみる。

using System;
using System.Linq.Expressions;

class Program
{
    static void Main()
    {
        Expression<Func<int, int, int>> expression = (x, y) => x + y;
    }
}

これは特に問題ない。

■ Expression として扱えないラムダ式

ラムダ式の => より右の部分には式だけでなくステートメントのブロックも置くことができる。

このラムダ式の => より右の部分を、式ではなく、ステートメントのブロックに変えてみよう。
例えば、(x, y) => { return x + y; } とし、これを Expression で受けてみる。どうなるだろうか?

using System;
using System.Linq.Expressions;

class Program
{
    static void Main()
    {
        Expression<Func<int, int, int>> expression = (x, y) => { return x + y; };
    }
}

すると今度は、「ステートメント本体を含むラムダ式は、式のツリーに変換できません」というコンパイル エラーになる。

「ステートメント本体を含むラムダ式は、式のツリーに変換できません」というコンパイル エラー

つまり、ラムダ式の => の右の部分が式である場合は Expression として扱えるが、ステートメントのブロックである場合は Expression として扱えないのだ。

■ 考察

「式でないものは式として扱えない」と云う、云わば当たり前の結果だ。

ところで、F# や Scala、Haskell などの関数型プログラミングをより得意とする言語では、if 等多くのものが式として扱える。

それらと比較すると、C# ではまだまだ式として書ける範囲は少ない。
例えば、if は文だ (勿論 C# でも三項演算子で書けるものは式で表現できる)。
だが、C# でもっと多くのものが、F# や Scala、Haskell などの言語のように文でなく式で表せるようになれば、益益 Expression で扱える範囲が増える、と云うことになるだろう。

■ 今回のまとめ

今回は、前回の記事の補足を軽く行った。

今後、ラムダ式を Expression として利用する例を何回かに分けて、紹介していく予定だ。

次回は、先ず Expression そのものへの理解を深めるために Expression の構造を調べてみたいと思う。

2012年11月30日

[C#][ラムダ式][式木] Expression の構造を調べてみる

Expression

匿名メソッドとラムダ式の違い」と云う記事で、匿名メソッドとラムダ式の意味の違いについて考えた。

「ラムダ式を Expression として扱っている場合は、匿名メソッドは代わりにはならない」と述べたが、ラムダ式を Expression として扱う例について、これから数回に分けて書いていきたい。

ラムダ式を Expression として扱う例を挙げる前に、今回は、先ずは Expression 自体について理解を深めたいと思う。

Expression はどのような構造をしているのだろうか? 中がどうなっているのかを見てみることにしよう。

■ Expression をデバッガーで観察してみよう

手始めに、Visual Studio のデバッガーを用いて Expression の中を覗いてみる。

ラムダ式として足し算を行うだけの (x, y) => x + y と云うシンプルなものを用意し、これを Expression で受ける。

using System;
using System.Linq.Expressions;

class Program
{
    static void Main()
    {
        Expression<Func<int, int, int>> expression = (x, y) => x + y;
    }
}

これをデバッグ実行して、デバッガーで expression の中を覗いてみる。

デバッガーで expression の中を覗いてみる 1

Body、Name、NodeType、Parameters、Type 等の各プロパティとその値が見えているのが判るだろう。

Name はなく、NodeType は Lambda だ。名前がないと云うことと、式の種類がラムダ式であることを表している。
Type は Func`3、つまり TResult Func<in T1, in T2, out TResult> になっている。

Body の中を見てみよう。

デバッガーで expression の中を覗いてみる - Body

ラムダ式の中の => より後ろの x + y の部分であることが判る。引数の x が見えている。NodeType は Add で足し算の式であることを表している。
Type は Int32、つまり int だ。
Left は x、Right は y となっていて、二項演算である足し算の左オペランドが x、右オペランドが y であることを表している。

更に Left の中を見てみる。

デバッガーで expression の中を覗いてみる - Body - Left

Name は x、Type は Int32 つまり int。
NodeType は Parameter で式の種類が引数であることを表している。

続いて Parameters。

デバッガーで expression の中を覗いてみる - Parameters

Parameters は要素数 2 のコレクションになっているようだ。ラムダ式の中の => より前の部分の引数を表していることが判る。

Parameters の一つ目の要素を見てみよう。

デバッガーで expression の中を覗いてみる - Parameters の一つ目の要素

引数の x が見えている。NodeType は Parameter つまり式の種類は引数、Type は Int32 つまり int だ。

■ Expression はツリー構造

上の例では、expression のNodeType は Lambda であり、この式の種類がラムダ式であることを表していた。

その他にも、幾つかの式が出てきている。

例えば、Body の部分。ラムダ式の中の => より後ろの x + y の部分だが、ここも式であり、式の種類は二項演算の足し算の式であった。

デバッガーでよく見てみると、これらの式も Expression であることが判る。

Expression はその中に再帰的に Expression を持つことでツリー構造になっているようだ。

■ Expression の種類

Expression には幾つかの種類があることも判る。

ラムダ式、二項演算の式、などだ。

実は、Expression クラスには沢山の派生クラスがあり、それぞれが様様な式の種類を表している。

以下を参照してほしい:

その一部を示すと、こんな感じだ。

Expression クラスの派生クラス 種類 NodeType プロパティの値
NodeType NodeType の説明
LambdaExpression ラムダ式 Lambda ラムダ式
BinaryExpression 二項演算式 Add 足し算
AddChecked オーバーフロー チェックを行う足し算
Subtract 引き算
SubtractChecked オーバーフロー チェックを行う引き算
Multiply 掛け算
MultiplyChecked オーバーフロー チェックを行う掛け算
Divide 割り算
Modulo 剰余演算
Power 累乗
UnaryExpression 単項演算式 Negate 単項マイナス演算
NegateChecked オーバーフロー チェックを行う単項マイナス演算
UnaryPlus 単項プラス演算
Not ビット補数演算または論理否定演算
Convert キャスト演算
ConvertChecked チェック付きキャスト演算
TypeAs as による型変換
ArrayLength 配列の長さを取得する演算
Quote 定数式

■ Expression のツリー構造を見てみよう

それでは、Expression のツリー構造を実際に見てみよう。

ツリー構造になった Expression を再帰的に辿りながら表示していくクラスを作ってみた。

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq.Expressions;

static class EnumerableExtensions
{
    // コレクションの各要素に対して、指定された処理をインデックス付きで実行
    public static void ForEach<TItem>(this IEnumerable<TItem> collection, Action<TItem, int> action)
    {
        int index = 0;
        foreach (var item in collection)
            action(item, index++);
    }
}

// Expression の中身をダンプ (ラムダ式、二項演算式、単項演算式以外は簡易表示)
static class ExpressionViewer
{
    // Expression の中を (再帰的に) 表示
    public static void Show(this Expression expression, int level = 0)
    {
        if (expression as LambdaExpression != null)
            // ラムダ式のときは詳細に表示
            ShowLambdaExpression((LambdaExpression)expression, level);
        else if (expression as BinaryExpression != null)
            // 二項演算のときは詳細に表示
            ShowBinaryExpression((BinaryExpression)expression, level);
        else if (expression as UnaryExpression != null)
            // 単項演算のときは詳細に表示
            ShowUnaryExpression((UnaryExpression)expression, level);
        else if (expression != null)
            // それ以外も沢山あるが、今回は省略してベース部分だけ表示
            ShowExpressionBase(expression, level);
    }

    // Expression のベース部分を表示
    static void ShowExpressionBase(Expression expression, int level)
    {
        ShowText(string.Format("☆Expression: {0}", expression), level);
        ShowText(string.Format("ノードタイプ: {0}", expression.NodeType), level + 1);
    }

    // LambdaExpression (ラムダ式) の中を (再帰的に) 表示
    static void ShowLambdaExpression(LambdaExpression expression, int level)
    {
        ShowExpressionBase(expression, level);
        ShowText(string.Format("名前: {0}", expression.Name), level + 1);
        ShowText(string.Format("戻り値の型: {0}", expression.ReturnType), level + 1);
        ShowParameterExpressions(expression.Parameters, level + 1); // 引数のコレクション
        ShowText(string.Format("本体: {0}", expression.Body), level + 1);
        expression.Body.Show(level + 2); // 本体を再帰的に表示
    }

    // BinaryExpression (二項演算式) の中を (再帰的に) 表示
    static void ShowBinaryExpression(BinaryExpression expression, int level)
    {
        ShowExpressionBase(expression, level);
        ShowText(string.Format("型: {0}", expression.Type), level + 1);
        ShowText(string.Format("左オペランド: {0}", expression.Left), level + 1);
        expression.Left.Show(level + 2); // 左オペランドを再帰的に表示
        ShowText(string.Format("右オペランド: {0}", expression.Right), level + 1);
        expression.Right.Show(level + 2); // 右オペランドを再帰的に表示
    }

    // UnaryExpression (単項演算式) の中を (再帰的に) 表示
    static void ShowUnaryExpression(UnaryExpression expression, int level)
    {
        ShowExpressionBase(expression, level);
        ShowText(string.Format("型: {0}", expression.Type), level + 1);
        ShowText(string.Format("オペランド: {0}", expression.Operand), level + 1);
        expression.Operand.Show(level + 2); // オペランドを再帰的に表示
    }

    // 引数の式のコレクションを表示
    static void ShowParameterExpressions(ReadOnlyCollection<ParameterExpression> parameterExpressions, int level)
    {
        ShowText("引数群", level);
        if (parameterExpressions == null || parameterExpressions.Count == 0)
            ShowText("引数なし", level);
        else
            parameterExpressions.ForEach((parameterExpression, index) => ShowParameterExpression(parameterExpression, index, level + 1));
    }

    // 引数の式の中を表示
    static void ShowParameterExpression(ParameterExpression parameterExpression, int index, int level)
    {
        ShowText(string.Format("引数{0}", index + 1), level + 1);
        ShowExpressionBase(parameterExpression, level + 1);
        ShowText(string.Format("引数の型: {1}, 引数の名前: {2}", parameterExpression.NodeType, parameterExpression.Type, parameterExpression.Name), level + 2);
    }

    // 文字列をレベルに応じてインデント付で表示
    static void ShowText(string itemText, int level)
    {
        Console.WriteLine("{0}{1}", Indent(level), itemText);
    }

    // インデントの為の文字列を生成
    static string Indent(int level)
    {
        return level == 0 ? "" : new string(' ', (level - 1) * 4 + 1) + "|-- ";
    }
}

この ExpressionViewer クラスを使って、先程の expression の中を見てみよう。

class Program
{
    public static void Main()
    {
        Expression<Func<int, int, int>> expression = (x, y) => x + y;
        ((Expression)expression).Show();
    }
}

実行してみると、こうなる。

☆Expression: (x, y) => (x + y)
 |-- ノードタイプ: Lambda
 |-- 名前:
 |-- 戻り値の型: System.Int32
 |-- 引数群
         |-- 引数1
         |-- ☆Expression: x
             |-- ノードタイプ: Parameter
             |-- 引数の型: System.Int32, 引数の名前: x
         |-- 引数2
         |-- ☆Expression: y
             |-- ノードタイプ: Parameter
             |-- 引数の型: System.Int32, 引数の名前: y
 |-- 本体: (x + y)
     |-- ☆Expression: (x + y)
         |-- ノードタイプ: Add
         |-- 型: System.Int32
         |-- 左オペランド: x
             |-- ☆Expression: x
                 |-- ノードタイプ: Parameter
         |-- 右オペランド: y
             |-- ☆Expression: y
                 |-- ノードタイプ: Parameter

「☆Expression」とあるところが式 (Expression) だ。ツリー構造になっている。

「引数群」のところでは、x と y がそれぞれ引数という種類の式としてネストしている。

「本体」は x + y の部分で、足し算を表す式としてネストしている。
その足し算の左オペランドである x と右オペランドである y が、それぞれ引数タイプの式として更にネストしている。

次に、ちょっとだけラムダ式を複雑にしてみよう。(x, y) => x + y を (x, y, z) => x + y + z に変えてみる。

こうだ。

class Program
{
    public static void Main()
    {
        Expression<Func<int, int, int, int>> expression = (x, y, z) => x + y + z;
        ((Expression)expression).Show();
    }
}

さて、結果はどう変化するだろうか。

☆Expression: (x, y, z) => ((x + y) + z)
 |-- ノードタイプ: Lambda
 |-- 名前:
 |-- 戻り値の型: System.Int32
 |-- 引数群
         |-- 引数1
         |-- ☆Expression: x
             |-- ノードタイプ: Parameter
             |-- 引数の型: System.Int32, 引数の名前: x
         |-- 引数2
         |-- ☆Expression: y
             |-- ノードタイプ: Parameter
             |-- 引数の型: System.Int32, 引数の名前: y
         |-- 引数3
         |-- ☆Expression: z
             |-- ノードタイプ: Parameter
             |-- 引数の型: System.Int32, 引数の名前: z
 |-- 本体: ((x + y) + z)
     |-- ☆Expression: ((x + y) + z)
         |-- ノードタイプ: Add
         |-- 型: System.Int32
         |-- 左オペランド: (x + y)
             |-- ☆Expression: (x + y)
                 |-- ノードタイプ: Add
                 |-- 型: System.Int32
                 |-- 左オペランド: x
                     |-- ☆Expression: x
                         |-- ノードタイプ: Parameter
                 |-- 右オペランド: y
                     |-- ☆Expression: y
                         |-- ノードタイプ: Parameter
         |-- 右オペランド: z
             |-- ☆Expression: z
                 |-- ノードタイプ: Parameter

一段ネストが深くなったのがお判りだろうか。

x + y + z のところが (x + y) + z、即ち「『x + y という足し算の式』を左オペランドとし、z を右オペランドとする足し算の式」になっている。

では、更にネストを深くする為に、もっと複雑なラムダ式を使ってみよう。

「デリゲートを入力としデリゲートを出力とするラムダ式」でやってみる。

例えばこんなやつ。

    Func<Func<double, double, double>, Func<int, int, int>> convertFunc = func => ((x, y) => (int)func((double)x, (double)y));

余談だが、一応このラムダ式について説明してみる。

このラムダ式は、「int 二つを引数とし int を返すメソッド」を「double 二つを引数とし double を返すメソッド」に変換する。
double を対象とした足し算を int を対象とした足し算に変換したり、double を対象とした引き算を int を対象とした引き算に変換したりできることになる。

余り意味のない例だが、こんな感じだ。

using System;

static class Program
{
    static double Add(double x, double y)
    {
        return x + y;
    }

    static double Subtract(double x, double y)
    {
        return x - y;
    }

    public static void Main()
    {
        Func<Func<double, double, double>, Func<int, int, int>> convertFunc = func => ((x, y) => (int)func((double)x, (double)y));
        int answer1 = convertFunc(Add )(1, 2);
        int answer2 = convertFunc(Subtract)(4, 3);
    }
}

このラムダ式を ExpressionViewer クラスを使ってダンプしてみよう。

class Program
{
    public static void Main()
    {
        Expression<Func<Func<double, double, double>, Func<int, int, int>>> expression = func => ((x, y) => (int)func((double)x, (double)y));
        ((Expression)expression).Show();
    }
}

結果は下のようになる。

☆Expression: func => (x, y) => Convert(Invoke(func, Convert(x), Convert(y)))
 |-- ノードタイプ: Lambda
 |-- 名前:
 |-- 戻り値の型: System.Func`3[System.Int32,System.Int32,System.Int32]
 |-- 引数群
         |-- 引数1
         |-- ☆Expression: func
             |-- ノードタイプ: Parameter
             |-- 引数の型: System.Func`3[System.Double,System.Double,System.Double], 引数の名前: func
 |-- 本体: (x, y) => Convert(Invoke(func, Convert(x), Convert(y)))
     |-- ☆Expression: (x, y) => Convert(Invoke(func, Convert(x), Convert(y)))
         |-- ノードタイプ: Lambda
         |-- 名前:
         |-- 戻り値の型: System.Int32
         |-- 引数群
                 |-- 引数1
                 |-- ☆Expression: x
                     |-- ノードタイプ: Parameter
                     |-- 引数の型: System.Int32, 引数の名前: x
                 |-- 引数2
                 |-- ☆Expression: y
                     |-- ノードタイプ: Parameter
                     |-- 引数の型: System.Int32, 引数の名前: y
         |-- 本体: Convert(Invoke(func, Convert(x), Convert(y)))
             |-- ☆Expression: Convert(Invoke(func, Convert(x), Convert(y)))
                 |-- ノードタイプ: Convert
                 |-- 型: System.Int32
                 |-- オペランド: Invoke(func, Convert(x), Convert(y))
                     |-- ☆Expression: Invoke(func, Convert(x), Convert(y))
                         |-- ノードタイプ: Invoke

「引数群」のところは、単に一つのデリゲートになっている。

「本体」のところは、ラムダ式がネストしている。
そして、ネストしたラムダ式は、通常のラムダ式と同じようにツリー構造に展開されている。

■ 今回のまとめ

と云う訳で、今回は Expression の構造を見てみた。

次回以降で、愈愈ラムダ式を Expression として利用する例を紹介したい。

2012年12月01日

[C#][ラムダ式][匿名メソッド] ラムダ式は常に匿名メソッドよりシンプルに書ける?

Expression

匿名メソッドとラムダ式の違い」と云う記事で、匿名メソッドとラムダ式の意味の違いについて考えた。

それについて、些細な補足をする。

上の記事中で、「ラムダ式を使った方が、型推論の恩恵を存分に受けられ、書き方がぐっとシンプルになる。」、「一般的には、ラムダ式を使うことで書き方もシンプルになることだし、匿名メソッドは使わずにラムダ式で書くようにした方が良いだろう。」と述べた。

一般的にはこれは真実だ。しかし、ごく稀にだが、ラムダ式よりも匿名メソッドの方がシンプルになることがある。

■ ラムダ式が匿名メソッドよりシンプルな書き方になる例

上の記事で述べたように、通常は、ラムダ式の方が型推論が強力なので、シンプルに書ける。

かなり極端な例だが、

using System;

class Subject
{
    public event Action<Subject, object, object, object, object, object> Update;
}

class Program
{
    static void DoSomething(Subject subject)
    {}

    static void Main()
    {
        var subject = new Subject();

        // 匿名メソッドで書いた場合
        subject.Update += delegate(Subject sender, object a, object b, object c, object d, object e) { DoSomething(sender); };

        // ラムダ式で書いた場合
        subject.Update += (sender, a, b, c, d, e) => DoSomething(sender);
    }
}

ラムダ式の方では、型名の記述その他が省略できてシンプルな記述になっている。

■ ラムダ式より匿名メソッドの方がよりシンプルな書き方になる例

ところが、内部で引数を使わないような下の例ではどうだろう。

using System;

class Subject
{
    public event Action<Subject, object, object, object, object, object> Update;
}

class Program
{
    static void DoSomething()
    {}

    static void Main()
    {
        var subject = new Subject();

        // 匿名メソッドで書いた場合
        subject.Update += delegate { DoSomething(); };

        // ラムダ式で書いた場合
        subject.Update += (_, a, b, c, d, e) => DoSomething();
    }
}

匿名メソッドの方が、ちょっとだけシンプルになっている気がしないだろうか。

■ 本日のまとめ

とは云え、これは上で述べているように極端で、そして余り意味のない例だ。

こんなに引数が多いデリゲートなど殆ど必要ないだろう。

矢張り、「一般的には、ラムダ式を使うことで書き方もシンプルになることだし、匿名メソッドは使わずにラムダ式で書くようにした方が良いだろう。」と云って問題ないと思う。

2012年12月03日

[C#][ラムダ式][式木] Expression を使ってラムダ式のメンバー名を取得する

Expression

前回「Expression の構造を調べてみる」と云う記事で、Expression の内部のツリー構造を調べた。

その中で、ラムダ式を Expression として扱うことで、式の中の名前が取れることが判った。

今回は これを利用してラムダ式のメンバー名を取得する例を挙げてみたい。

■ ラムダ式のメンバー名を取得する例

先ずシンプルなクラスを一つ用意する。

class Item
{
    public int Id { get; set; }
}

そして、item => item.Id というラムダ式を渡してプロパティ名を取得するメソッドを作ってみよう。

前回の知識が役に立つ。

前回の調査で、ラムダ式の => の右側の式は Body で取れることが解かっている。

次に、Visual Studio のデバッガーなどを駆使して、Body の式の種類を調べよう。Body は MemberExpression であることが判る筈だ。

MemberExpression であれば、プロパティの内の Member の Name で名前が取得できる。

実際にメソッドを作ってみるとこんな感じだ:

using System;
using System.Linq.Expressions;

public static class ObjectExtensions
{
    // Expression からメンバー名を取得
    public static string GetMemberName<ObjectType, MemberType>(this ObjectType @this, Expression<Func<ObjectType, MemberType>> expression)
    {
        return ((MemberExpression)expression.Body).Member.Name;
    }
}

早速使ってみよう。

using System;

class Item
{
    public int Id { get; set; }
}

class Program
{
    static void Test()
    {
        var memberName = new Item().GetMemberName(item => item.Id);
        Console.WriteLine(memberName);
    }

    public static void Main()
    {
        Test();
    }
}

実行結果は下の通りで、ラムダ式からプロパティ名が取得できた。

Id

次に、Item クラスの内部からもやってみよう。

今度のラムダ式は () => Id だ。

ObjectExtensions クラスに先程作ったメソッドから第一引数を無くしたメソッドを追加して、

using System;
using System.Linq.Expressions;

public static class ObjectExtensions
{
    // Expression からメンバー名を取得
    public static string GetMemberName<ObjectType, MemberType>(this ObjectType @this, Expression<Func<ObjectType, MemberType>> expression)
    {
        return ((MemberExpression)expression.Body).Member.Name;
    }

    // Expression からメンバー名を取得
    public static string GetMemberName<MemberType>(Expression<Func<MemberType>> expression)
    {
        return ((MemberExpression)expression.Body).Member.Name;
    }
}

Item クラス内部に Test 用のメソッドを追加し、そこから呼んでみる。

using System;

class Item
{
    public int Id { get; set; }

    public void Test()
    {
        var memberName = ObjectExtensions.GetMemberName(() => Id);
        Console.WriteLine(memberName);
    }
}

class Program
{
    static void Test()
    {
        var memberName = new Item().GetMemberName(item => item.Id);
        Console.WriteLine(memberName);
    }

    public static void Main()
    {
        Test();
        new Item().Test();
    }
}

実行結果は下の通り。クラス内部からもラムダ式からプロパティ名が取得できた。

Id
Id

■ 今回のまとめ

Expression を利用してラムダ式のメンバー名を取得してみた。

しかし、こんなことが何の役に立つのだろうか?

次回から、これを利用した応用例について書こうと思う。

2012年12月05日

[C#][dynamic] DynamicObject を使ってみよう

Dynamic

C# 4 から dynamic が使えるようになった。

動的言語のように、動的にプロパティを参照したり、メソッドを呼んだり出来るようになった訳だ。

そして、.NET Framework では 4 から System.Dynamic と云う名前空間ができた。

今回は、この名前空間の中の DynamicObject を使ってみたい。

■ DynamicObject を使った例

・DynamicObject

DynamicObject は、メソッド呼び出し、プロパティへのアクセス、インデックスによるアクセス等のそれぞれに対応する virtual メソッドを持つ。

例.

virtual メソッド名 説明
TrySetMember オーバーライドして、プロパティに値が設定されるときの動作を定義できる
TryGetMember オーバーライドして、プロパティから値が取得されるときの動作を定義できる
TrySetIndex オーバーライドして、インデックスを用いて値が設定されるときの動作を定義できる
TryGetIndex オーバーライドして、インデックスを用いて値が取得されるときの動作を定義できる
TryInvokeMember オーバーライドして、メソッドが呼び出されるときの動作を定義できる

DynamicObject から派生したクラスを作り、これらの virtual メソッド をオーバーライドすることで、動的な動作を定義することができる。

・DynamicObjectを使った例 1

実際に試してみよう。

今回は、TrySetMember と TryGetMember をオーバーライドしてプロパティにアクセスしてみる。

using System;
using System.Dynamic;

class Dynamic : DynamicObject
{
    // プロパティに値を設定しようとしたときに呼ばれる
    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        return base.TrySetMember(binder, value);
    }

    // プロパティから値を取得しようとしたときに呼ばれる
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        return base.TryGetMember(binder, out result);
    }
}

class Program
{
    static void Main()
    {
        dynamic item = new Dynamic();
        item.Id = 100; // 実行時エラー (Id の定義がない)
        Console.WriteLine(item.Id); // 実行時エラー (Id の定義がない)
        item.Name = "田中一郎"; // 実行時エラー (Name の定義がない)
        Console.WriteLine(item.Name); // 実行時エラー (Name の定義がない)
    }
}

Visual Studio を使って、オーバーライドした TrySetMember と TryGetMember にブレークポイントを置いてデバッグ実行してみると、それぞれプロパティの設定時と取得時にちゃんと呼ばれていることが判る。

DynamicObjectを使った例 1 - ブレークポイントで止めたところ
DynamicObjectを使った例 1 - ブレークポイントで止めたところ

base.TrySetMember(binder, value) と base.TryGetMember(binder, out result) は false を返している。

TrySetMember が false を返すとプロパティの設定に失敗し、TryGetMember が false を返すとプロパティの取得に失敗する。

従って、このプログラムを実行してみると、以下のような実行時エラーになる。

DynamicObjectを使った例 1 - 実行時エラー
DynamicObjectを使った例 1 - 実行時エラー
・DynamicObjectを使った例 2

TrySetMember と TryGetMember の中をもう少しちゃんと実装して、動くようにしてみよう。

TrySetMember の中で、Dictionary を使って設定した値を覚えるようにしてみる。

そして、同じプロパティが再設定されるときは、型が同じか派生クラスのときだけ許すようにしてみる
(この実装はやや妥当ではないが、今回はこうしてみる)。

そして、TryGetMember の中では、Dictionary から値を取り出して返すようにしてみよう。

こんな感じだ。

using System;
using System.Collections.Generic;
using System.Dynamic;

class Dynamic : DynamicObject
{
    // observableTarget の該当するプロパティ名毎に型と値を格納
    Dictionary<string, Tuple<Type, object>> values = new Dictionary<string, Tuple<Type, object>>();

    // プロパティに値を設定しようとしたときに呼ばれる
    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        Tuple<Type, object> tuple;
        // 該当するプロパティの型と値を values から取得
        if (values.TryGetValue(binder.Name, out tuple)) {
            var valueType = value.GetType();
            // もしプロパティに設定しようとしている値の型が、そのプロパティの型もしくはそのサブクラスでなかったら
            if (!valueType.Equals(tuple.Item1) && !valueType.IsSubclassOf(tuple.Item1))
                return false;
            // 元の型の儘で値を再設定
            values[binder.Name] = new Tuple<Type, object>(tuple.Item1, value);
            return true;
        }
        // 型と値を新規に格納
        values[binder.Name] = new Tuple<Type, object>(value.GetType(), value);
        return true;
    }

    // プロパティから値を取得しようとしたときに呼ばれる
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        Tuple<Type, object> tuple;
        // 該当するプロパティの型と値を values から取得
        if (values.TryGetValue(binder.Name, out tuple)) {
            result = tuple.Item2;
            return true;
        }
        result = null;
        return false;
    }
}

class Program
{
    static void Main()
    {
        dynamic item = new Dynamic();
        item.Id = 100; // OK
        Console.WriteLine(item.Id); // OK
        //item.Id = "田中一郎"; // 実行時エラー (異なった型の値を設定しようとしている)
        item.Name = "田中一郎"; // OK
        Console.WriteLine(item.Name); // OK
        //Console.WriteLine(item.Address); // 実行時エラー (設定していないプロパティの値を取得しようとしている)
    }
}

今度の実行結果は次の通り。

100
田中一郎

item.Id に異なった型の値を設定しようとしたり、設定していないプロパティの値を取得しようとしたりしている箇所は、実行時エラーになる。
それ以外は正常に動く。

プロパティの再設定時に、再設定する値の型が派生クラスなら設定できるが派生クラスでなければ設定できないことも、確認しておこう。

class Super {}

class Sub : Super {}

class Program
{
    static void Main()
    {
        dynamic item = new Dynamic();

        item.Property1 = new Super(); // OK
        item.Property1 = new Sub(); // OK

        item.Property2 = new Sub(); // OK
        item.Property2 = new Super(); // 実行時エラー (型が違うし、派生クラスでもない)
    }
}

最後の、型が同じでも派生クラスでもなかったときだけ実行時エラーとなる。

■ ExpandoObject を使った例

次に、同じ名前空間 System.Dynamic の中の ExpandoObject も試してみよう。

DynamicObject は派生して使うが、ExpandoObject は派生せずにその儘使う。

こんな感じだ。

using System;
using System.Dynamic;

class Program
{
    static void Main()
    {
        dynamic item = new ExpandoObject();

        item.Id = 100; // OK
        Console.WriteLine(item.Id); // OK
        item.Id = "田中一郎"; // OK
        Console.WriteLine(item.Id); // OK
        item.Name = "田中一郎"; // OK
        Console.WriteLine(item.Name); // OK
        //Console.WriteLine(item.Address); // 実行時エラー(設定していないプロパティの値を取得しようとしている)
    }
}

実行結果は次の通り。

100
田中一郎
田中一郎

ExpandoObject はその儘で、動的にプロパティを設定したり、取得したりできる。

こちらは、最後の設定してないプロパティを取得しようとしたとき以外は、実行時エラーにならない。

■ 今回のまとめ

今回は、DynamicObject を使ってみた。

近く、これの応用例として、DynamicObject をオブザーバー パターンの実装に利用してみる予定だ。

お楽しみに。

2012年12月06日

[C#][dynamic] DynamicObject を使ってみよう その 2

Dynamic

前回の「DynamicObject を使ってみよう」の続き。

前回は、DynamicObject と ExpandoObject を使ってみた。

DynamicObject の派生クラスや ExpandoObject は連想配列のように機能した。
但し、通常の連想配列とはインタフェイスが異なる。

今回も、引き続き DynamicObject と ExpandoObject を使ってみよう。

■ DynamicObject を使った例

DynamicObject の派生クラスがインタフェイスの異なる連想配列のように使える、と云うことを利用してちょっとしたラッパー クラスを作ってみよう。

例えば、ASP.NET や ASP.NET MVC では、セッション変数を利用する際、以下のように連想配列のように使う。

    Session["Id"] = 100;
    int? id = Session["Id"] as int?;

DynamicObject を利用したラッパー クラスを作ってみよう。

例えば、こんな感じだ。

using System.Dynamic;
using System.Web;

public class SessionObject : DynamicObject
{
    readonly HttpSessionStateBase session;

    public SessionObject(HttpSessionStateBase session)
    {
        this.session = session;
    }

    // プロパティに値を設定しようとしたときに呼ばれる
    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        session[binder.Name] = value;
        return true;
    }

    // プロパティから値を取得しようとしたときに呼ばれる
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = session[binder.Name];
        return true;
    }
}

すると、従来のこんな書き方が、

// ASP.NET MVC の場合の一例 (従来の書き方)
using System.Web;
using System.Web.Mvc;
using System.Web.UI;

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var id = Session["Id"]; // まだ Session["Id"] は無いので、id は null
        Session["Id"] = 100; // Session["Id"] に 100 を設定
        id = Session["Id"]; // id には 100 が返ってくる

        var item = Session["Item"]; // まだ Session["Item"] は無いので、item は null
        Session["Item"] = new { Id = 200, Name = "田中一郎" }; // Session["Item"] に匿名型のオブジェクトを設定
        id = DataBinder.Eval(Session["Item"], "Id"); // id には 200 が返ってくる

        return View();
    }
}

SessionObject クラスを使うことによって、こんな書き方になる。

// ASP.NET MVC の場合の一例 (SessionObject クラスを使った場合)
using System.Web;
using System.Web.Mvc;

public class HomeController : Controller
{
    dynamic session = null;

    public ActionResult Index()
    {
        session = new SessionObject(Session);

        var id = session.Id; // まだ session.Id は無いので、id は null
        session.Id = 100; // session.Id に 100 を設定
        id = session.Id; // id には 100 が返ってくる

        var item = session.Item; // まだ session.Item は無いので、item は null
        session.Item = new { Id = 200 }; // session.Item に匿名型のオブジェクトを設定
        id = session.Item.Id; // id には 200 が返ってくる

        return View();
    }
}

dynamic に書けることで、コードがややシンプルになった。

一応、Visual Studio でデバッグ実行し、デバッガーを使って値をチェックしてみると、次のようになった。

DynamicObjectを使った例 - デバッガーでの値のチェック - 「まだ session.Id は無いので、id は null」
1. 「まだ session.Id は無いので、id は null」
DynamicObjectを使った例 - デバッガーでの値のチェック - 「id には 100 が返ってくる」
2. 「id には 100 が返ってくる」
DynamicObjectを使った例 - デバッガーでの値のチェック - 「まだ session.Item は無いので、item は null」
3. 「まだ session.Item は無いので、item は null」
DynamicObjectを使った例 - デバッガーでの値のチェック - 「id には 200 が返ってくる」
4. 「id には 200 が返ってくる」

まあ、実用的な意味合いは余りないが、連想配列的な部分には、使える可能性がある、と云う一例だ。

■ ExpandoObject を使った例

次は、ExpandoObject だ。

・ExpandoObject にプロパティっぽいものを追加

前回は、ExpandoObject を使って動的なプロパティっぽいものを実現した。

再度やってみよう。

using System;
using System.Dynamic;

class Program
{
    static void Main()
    {
        dynamic item = new ExpandoObject();

        // プロパティっぽいものを追加 その1
        item.Id = 10;
        Console.WriteLine(item.Id);
    }
}

実行結果は次の通り。

10

もうちょっと複雑な例。

using System;
using System.Dynamic;

class Program
{
    static void Main()
    {
        dynamic item = new ExpandoObject();

        // プロパティっぽいものを追加 その2
        item.SubItem = new { Id = 100, Name = "田中一郎" };
        Console.WriteLine(item.SubItem);
        Console.WriteLine(item.SubItem.Id);
        Console.WriteLine(item.SubItem.Name);
    }
}

実行結果は次の通り。

{ Id = 100, Name = 田中一郎 }
100
田中一郎
・ExpandoObject に static メソッドっぽいものを追加

オブジェクトが何でも追加できるのであれば、デリゲートだって追加できる筈だ。

デリゲートが追加できるのであれば、メソッドっぽいものも実現できるのではないか。

試しに、static メソッドっぽいものの追加を試してみよう。

こんな感じ。

using System;
using System.Dynamic;

class Program
{
    static void Main()
    {
        dynamic item = new ExpandoObject();

        // static メソッドっぽいものを追加
        item.SetId = new Action<dynamic, int>((o, value) => { o.Id = value; });
        item.GetId = new Func<dynamic, int>(o => o.Id);

        // 呼んでみる
        item.SetId(item, 30);
        Console.WriteLine(item.GetId(item));
    }
}

実行結果は次の通り。

30
・ExpandoObject に イベントっぽいものを追加

デリゲートが追加できるのであれば、もうちょっとイベントっぽくしてみよう。

こんな感じだ。

using System;
using System.Dynamic;

class Program
{
    static void Main()
    {
        dynamic item = new ExpandoObject();

        // イベントっぽいものを追加

        // 1. 先ず、Update と云う名のイベントっぽいものを用意
        item.Update = null;

        // 2. 次に、Update にイベントハンドラーっぽいものを追加
        item.Update += new Action<dynamic, EventArgs>((sender, _) => Console.WriteLine(sender.Name));

        // 3. そして、SetName と云うメソッドっぽいものを用意して、中で Update と云うイベントっぽいものを起こす
        item.SetName = new Action<dynamic, string>(
            (o, value) => {
                o.Name = value;
                if (o.Update != null)
                    o.Update(o, EventArgs.Empty);
            }
        );

        // 4. では、試してみよう:
        // SetName を呼ぶと Update が起きて Console.WriteLine(sender.Name) されるかな?
        item.SetName(item, "田中次郎");
    }
}

実行結果は次の通り。

田中次郎

イベントっぽい。

・ExpandoObject のメンバーの一覧

ここまでを纏めてみる。最後にメンバーの一覧を取ってみよう。

using System;
using System.Dynamic;

class Program
{
    static void Main()
    {
        dynamic item = new ExpandoObject();

        // プロパティっぽいものを追加 その1
        item.Id = 10;
        Console.WriteLine(item.Id);

        // プロパティっぽいものを追加 その2
        item.SubItem = new { Id = 20, Name = "田中一郎" };
        Console.WriteLine(item.SubItem);
        Console.WriteLine(item.SubItem.Id);
        Console.WriteLine(item.SubItem.Name);

        // static メソッドっぽいものを追加
        item.SetId = new Action<dynamic, int>((o, value) => { o.Id = value; });
        item.GetId = new Func<dynamic, int>(o => o.Id);

        // 呼んでみる
        item.SetId(item, 30);
        Console.WriteLine(item.GetId(item));

        // イベントっぽいものを追加

        // 1. 先ず、Update と云う名のイベントっぽいものを用意
        item.Update = null;

        // 2. 次に、Update にイベントハンドラーっぽいものを追加
        item.Update += new Action<dynamic, EventArgs>((sender, _) => Console.WriteLine(sender.Name));

        // 3. そして、SetName と云うメソッドっぽいものを用意して、中で Update と云うイベントっぽいものを起こす
        item.SetName = new Action<dynamic, string>(
            (o, value) => {
                o.Name = value;
                if (o.Update != null)
                    o.Update(o, EventArgs.Empty);
            }
        );

        // 4. では、試してみよう:
        // SetName を呼ぶと Update が起きて、イベントハンドラーの中で Console.WriteLine(sender.Name) されるかな?
        item.SetName(item, "田中次郎");

        // メンバーの一覧の取得
        Console.WriteLine("\nメンバーの一覧:\n");
        foreach (var member in item)
            Console.WriteLine(member);
    }
}

実行結果は次のようになった。

10
{ Id = 20, Name = 田中一郎 }
20
田中一郎
30
田中次郎

メンバーの一覧:

[Id, 30]
[SubItem, { Id = 20, Name = 田中一郎 }]
[SetId, System.Action`2[System.Object,System.Int32]]
[GetId, System.Func`2[System.Object,System.Int32]]
[Update, System.Action`2[System.Object,System.EventArgs]]
[SetName, System.Action`2[System.Object,System.String]]
[Name, 田中次郎]

追加したプロパティっぽいものや、メソッドっぽいもの、イベントっぽいものが、型が違うだけのオブジェクトとして追加されているのが判る。

因みに、ExpandoObject は、IDictionary<string, Object> を実装しているので、こんな風に一覧を取ることができるし、要素数を取得したり、要素を削除したりすることもできる。

■ 今回のまとめ

今回も、DynamicObject と ExpandoObject を使ってみた。

dynamic な動作は、実行時にオーバーヘッドが大きいので、そこは要注意だが、応用例を考えてみると面白いのではないだろうか。

2012年12月07日

[C#][dynamic] プラグイン処理

Dynamic

動的処理の一例として、今回はプラグイン処理を行ってみる。

プラグイン処理によって、アプリケーションに対して動的に機能を追加できるようにすることができる。

■ プラグイン処理の例

今回のプラグインは、以下のような規約ベースで動くものとする。

  • 実行中のプログラムがあるフォルダーの下の Plugin と云う名前のフォルダーにある dll ファイルをプラグインのアセンブリと看做す。
  • プラグインのアセンブリの中にある最初の public なクラスをプラグインと見做す。
  • プラグインは、必ず Name と云う名称を表す public なプロパティを持っている。
  • プラグインは、必ず void Run() と云う public なメソッドによって動作する。
・プラグイン処理の実装の例

では、実装してみよう。

・プラグイン側の実装の例

先ずプラグイン側だ。

プラグインは、クラスライブラリとして作成する。

public なクラスを持ち、その中には、public な Name プロパティと public な Run() メソッドを持てば良い。

using System;

public class Plugin
{
    public string Name
    {
        get { return "Sample Plugin"; }
    }

    public void Run()
    {
        Console.WriteLine("This is sample plugin!");
    }
}
・プラグインが組み込まれる本体側の実装の例

次はプラグインが組み込まれる本体側だ。

using System;
using System.IO;
using System.Linq;
using System.Reflection;

class Program
{
    // プラグインのフォルダー名
    const string pluginFolderName = "Plugin";
    // 拡張子が dll のものをプラグインと看做す
    const string pluginFileName = "*.dll";
    
    static void Main()
    {
        // プラグインのフォルダーへのフルパス名を取得し、
        var pluginFolderName = GetPluginFolderName();
        // もしプラグインのフォルダーが在ったら、
        if (Directory.Exists(pluginFolderName))
            // プラグインのフォルダーの各のプラグインのパス名を使って、プラグインを実行
            Directory.GetFiles(pluginFolderName, pluginFileName).ToList().ForEach(Run);
    }

    // プラグインのフォルダーへのフルパス名を取得
    static string GetPluginFolderName()
    {
        // 実行中のこのプログラム自体のアセンブリのパスのフォルダーの下の Plugin フォルダーへのフルパスを取得
        return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + Path.DirectorySeparatorChar + pluginFolderName;
    }

    // プラグインを実行
    static void Run(string pluginPath)
    {
        // アセンブリを読み込み、その中から public な最初のクラスを取り出す
        var pluginType = Assembly.LoadFrom(pluginPath).GetExportedTypes().FirstOrDefault(type => type.IsClass);
        if (pluginType != null)
            // インスタンスを生成し、それをプラグインとして実行
            Run(Activator.CreateInstance(pluginType));
    }

    // プラグインを実行
    static void Run(dynamic plugin)
    {
        Console.WriteLine(plugin.Name); // プラグインの Name を表示し、
        plugin.Run(); // プラグインを Run
    }
}

規約ベースなので、特にプラグイン側は interface 等は持っていないし、本体側も interface で探したりしていない。

規約通りのものを実行する。

双方が特定の interface に依存しているのでなく、規約に依存している。

こういう場合は、dynamic による処理が合っている。

・プラグイン処理の実行例

実行してみよう。

先ずは、Plugin フォルダーを準備しない儘で実行してみる。


何も表示されない。

次に、プログラムを実行しているフォルダーの下に Plugin と云う名前のフォルダーを作成して実行してみる。


矢張り、何も表示されない。

次に、クラスライブラリであるプラグインをビルドし、出来上がった dll ファイルを Plugin フォルダーの直下にコピーし、実行してみる。

Sample Plugin
This is sample plugin!

プラグインが実行された。

Plugin フォルダーの dll を二つにしてみると、

Sample Plugin
This is sample plugin!
Sample Plugin
This is sample plugin!

両方のプラグインが実行される。

・interface を用いたプラグイン処理の実装の例

因みに、interface を用いた場合の例だと次のようになる。

・interface を用いたプラグイン処理の実装の例 - interface

先ず interface をクラス ライブラリとして準備する。

public interface IPlugin
{
    string Name { get; }
    void Run();
}
・interface を用いたプラグイン処理の実装の例 - プラグイン側

プラグイン側で interface のライブラリを参照し、クラスで IPlugin を実装する。

public class Plugin : IPlugin
{
    public string Name
    {
        get { return "Sample Plugin"; }
    }

    public void Run()
    {
        Console.WriteLine("This is sample plugin!");
    }
}
・interface を用いたプラグイン処理の実装の例 - プラグインが組み込まれる本体側

本体側でも interface のライブラリを参照する。

プラグイン側と本体側が同じ interface に依存することになる。

interface を持ったクラスを探して、interface によって Name や Run() にアクセスする。

先の例と違って、dynamic は使わない。

using System;
using System.IO;
using System.Linq;
using System.Reflection;

class Program
{
    // プラグインのフォルダー名
    const string pluginFolderName = "Plugin";
    // 拡張子が dll のものをプラグインと看做す
    const string pluginFileName = "*.dll";
    
    static void Main()
    {
        // プラグインのフォルダーへのフルパス名を取得し、
        var pluginFolderName = GetPluginFolderName();
        // もしプラグインのフォルダーが在ったら、
        if (Directory.Exists(pluginFolderName))
            // プラグインのフォルダーの各のプラグインのパス名を使って、プラグインを実行
            Directory.GetFiles(pluginFolderName, pluginFileName).ToList().ForEach(Run);
    }

    // プラグインのフォルダーへのフルパス名を取得
    static string GetPluginFolderName()
    {
        // 実行中のこのプログラム自体のアセンブリのパスのフォルダーの下の Plugin フォルダーへのフルパスを取得
        return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + Path.DirectorySeparatorChar + pluginFolderName;
    }

    // プラグインを実行
    static void Run(string pluginPath)
    {
        // アセンブリを読み込み、その中から public で IPlugin インタフェイスを持った最初のクラスを取り出す
        var pluginType = Assembly.LoadFrom(pluginPath).GetExportedTypes().FirstOrDefault(type => type.IsClass && typeof(IPlugin).IsAssignableFrom(type));
        if (pluginType != null)
            // インスタンスを生成し、それをプラグインとして実行
            Run((IPlugin)Activator.CreateInstance(pluginType));
    }

    // プラグインを実行
    static void Run(IPlugin plugin)
    {
        Console.WriteLine(plugin.Name); // プラグインの Name を表示し、
        plugin.Run(); // プラグインを Run
    }
}

■ 今回のまとめ

今回は、プラグイン処理をやってみた。

interface を使ってやることもできるが、規約ベースで dynamic を使ってやる方法の方が依存関係がシンプルになる。

dynamic な動作は、実行時にオーバーヘッドが大きいので、そこは要注意だが、応用例を考えてみると面白いのではないだろうか。

2012年12月08日

[C#][Design Pattern] C# による Observer パターンの実装 その1 - 古典的な実装

Observer

Expression を使ってラムダ式のメンバー名を取得する」と云う記事で、ラムダ式の中のメンバーの名前を取得した。

これから数回に渡って、Observer パターンの C# による実装を何種類か例に挙げ、その中で上記を活用していきたい。

今回は、その第一回として、Expression どころか C# の event すら使わない、Observer パターンの最も古典的な実装を見てみよう。

■ Observer パターンとは

先ず Observer パターンの簡単な説明から。

Observer パターンは、GoF による 23 種のデザインパターンの中の一つで、特にポピュラーなものの一つだ。

・参考資料:

■ C# での Observer パターンの実装 1 - 古典的な実装

早速 C# で Observer パターンを実装していこう。

今回は、手始めに上の参考資料で紹介されているような由緒ある方法で実装してみる。

・クラス図

クラス図はこんな感じ。

「C# での Observer パターン 1 - 古典的な実装」のクラス図
「C# での Observer パターン 1 - 古典的な実装」のクラス図
・フレームワーク部 (抽象部)

上半分の青い部分がフレームワーク部分だ。

IObserver が Observable を監視している。

Observable の Add で IObserver を実装したオブジェクト (= オブザーバー) を追加する。

Observable は更新されると、以下の手順で全オブザーバーに更新があったことを通知してオブザーバーの表示が更新されるようにする。

  1. Update() を呼ぶ。
  2. Update() では NotifyObservers() が呼ばれる。
  3. NotifyObservers() は、全オブザーバーの Update() を呼ぶ。
  4. オブザーバーは、Update() の中で表示を更新する。
・アプリケーション部 (具象部)

下半分の赤い部分がアプリケーション部分だ。

Employee は Observable だ。
Model として Number と Name と云う二つのプロパティを持っている。

EmployeeView がオブザーバーだ。
EmployeeView は Employee を表示するための View で、DataSource として Employee が渡されると、Employee のオブザーバーとして自分自身を追加する。

EmployeeView は、表示部として TextControl を二つ持っていて、それぞれに DataSource の Number と Name を表示する。

・実装

それでは、実装していこう。

一部エラー処理を省略する。

・フレームワーク部の実装

先に、フレームワーク部の IObserver と Observable から実装しよう。

こんな感じだ。

// C# による Oberver パターンの実装 その1
using System;
using System.Collections.Generic;

// フレームワーク部

interface IObserver // 更新を監視する側
{
    void Update(Observable observable);
}

abstract class Observable // 更新を監視される側
{
    List<IObserver> observers = new List<IObserver>();

    public void Add(IObserver observer) // オブサーバーの追加
    {
        observers.Add(observer);
    }

    protected void Update() // 更新イベント
    {
        NorifyObservers();
    }

    void NorifyObservers() // 全オブザーバーに更新を通知
    {
        observers.ForEach(observer => observer.Update(this));
    }
}
・アプリケーション部の実装

続いて、アプリケーション部。ここには、Employee と EmployeeView、TextControl、そして Main を含んだクラス Program がある。

// C# による Oberver パターンの実装 その1
using System;

// アプリケーション部

// Model
class Employee : Observable
{
    int number = 0;
    string name = string.Empty;

    public int Number
    {
        get { return number; }
        set
        {
            if (value != number) {
                number = value;
                Update();
            }
        }
    }

    public string Name
    {
        get { return name; }
        set
        {
            if (value != name) {
                name = value;
                Update();
            }
        }
    }
}

class TextControl // テキスト表示用のUI部品 (ダミー)
{
    public string Text
    {
        set { Console.WriteLine("TextControl is updated: {0}", value); }
    }
}

class EmployeeView : IObserver // Employee 用の View
{
    TextControl numberTextControl = new TextControl(); // Number 表示用
    TextControl nameTextControl = new TextControl(); // Name 表示用

    public Employee DataSource
    {
        set { value.Add(this); }
    }

    public void Update(Observable observable) // データソースのどのプロパティが更新されてもここに来る
    {
        var employee = observable as Employee;
        if (employee != null) {
            numberTextControl.Text = employee.Number.ToString();
            nameTextControl.Text = employee.Name;
        }
    }
}

class Program
{
    static void Main()
    {
        var employee = new Employee(); // Model, Observable
        var employeeView = new EmployeeView(); // View, Observer

        employeeView.DataSource = employee; // データバインド
        employee.Number = 100; // Number を変更。employeeView に反映されたかな?
        employee.Name = "福井太郎"; // Name を変更。employeeView に反映されたかな?
    }
}
・実行結果

実行してみると、次のようになる。

TextControl is updated: 100
TextControl is updated:
TextControl is updated: 100
TextControl is updated: 福井太郎

Employee の Number や Name が更新される度に、EmployeeView の Update が呼ばれ、二つの TextControl の表示が更新される。

Number の更新でも Name の更新でも同じ Update が呼ばれている。
ここは、出来ればそれぞれ処理したいところだ。

■ 今回のまとめと次回の予告

さて、Observer パターンの C# による実装の第一回として、古典的な実装を行ってみた。

interface と抽象クラスを用いてフレームワーク部を書いている。

次回は、もう少し C# らしい、event を用いた実装をやってみよう。

2012年12月09日

[C#][Design Pattern] C# による Observer パターンの実装 その2 - event による実装

Observer

前回「C# による Observer パターンの実装 その1 - 古典的な実装」と云う記事で、Observer パターンの C# による実装の第一回として、古典的な実装を行ってみた。

interface と抽象クラスを用いた実装だった訳だが、こうした場合、C# では event を用いるのが普通だろう。

今回は、「C# による Observer パターンの実装 その2」として、event による実装を行ってみる。

■ C# での Observer パターンの実装 2 - event による実装 1

・クラス図 1

クラス図はこんな感じ。

「C# での Observer パターンの実装 2 - event による実装 1」のクラス図
「C# での Observer パターンの実装 2 - event による実装 1」のクラス図

前回 と比べるとフレームワーク部 (抽象部) が無くなっているのが判る。

C# が持っている event が強力なので、それだけで更新によって呼ばれるべき処理が呼べてしまうのだ。

Employee は Model として Number と Name と云う二つのプロパティを持っている。

また、Employee は Update というイベントを持っている。
Employee は更新されると Update イベントを起こす。

Update イベントが起きたときに、もしそのイベント ハンドラーがあれば、それが呼ばれる。

EmployeeView は Employee を表示するための View で、DataSource として Employee が渡されると、Employee の Update イベントにイベント ハンドラーとして Update メソッドを設定する。

EmployeeView は、表示部として TextControl を二つ持っていて、それぞれに DataSource の Number と Name を表示する。

・実装 1

それでは、実装していこう (一部エラー処理を省略)。

// C# による Oberver パターンの実装 - event による実装 1
using System;
using System.Collections.Generic;

// アプリケーション部

// Model: 更新を監視される側
class Employee
{
    public event Action<Employee> Update; // 更新イベント

    int number = 0;
    string name = string.Empty;

    public int Number
    {
        get { return number; }
        set {
            if (value != number) {
                number = value;
                if (Update != null)
                    Update(this);
            }
        }
    }

    public string Name
    {
        get { return name; }
        set {
            if (value != name) {
                name = value;
                if (Update != null)
                    Update(this);
            }
        }
    }
}

class TextControl // テキスト表示用のUI部品 (ダミー)
{
    public string Text {
        set { Console.WriteLine("TextControl is updated: {0}", value); }
    }
}

class EmployeeView // Employee 用の View: 更新を監視する側
{
    TextControl numberTextControl = new TextControl(); // Number 表示用
    TextControl nameTextControl = new TextControl(); // Name 表示用

    public Employee DataSource
    {
        set {
            value.Update += Update; // データソースの更新イベントにハンドラを設定
        }
    }

    void Update(Employee employee) // データソースのどのプロパティが更新されてもここに来る
    {
        numberTextControl.Text = employee.Number.ToString();
        nameTextControl.Text = employee.Name;
    }
}

class Program
{
    static void Main()
    {
        var employee = new Employee(); // Model, Observable
        var employeeView = new EmployeeView(); // View, Observer

        employeeView.DataSource = employee; // データバインド
        employee.Number = 100; // Number を変更。employeeView に反映されたかな?
        employee.Name = "福井太郎"; // Name を変更。employeeView に反映されたかな?
    }
}
・実行結果

実行してみると、次のようになる。

前回と同じ結果だ。

TextControl is updated: 100
TextControl is updated:
TextControl is updated: 100
TextControl is updated: 福井太郎

今回も矢張り、Employee の Number や Name が更新される度に、EmployeeView の Update が呼ばれ、二つの TextControl の表示が更新される。

■ C# での Observer パターンの実装 2 - event による実装 2

上では、Number と Name のどちらが更新されても両方の TextControl が更新されている。

これは、Number と Name のどちらが更新されても同じイベントが起きるようになっている為だ。

更新イベントを分けて、それぞれの TextControl が更新されるように改良してみよう 。

・クラス図 2

クラス図はこうなる。

「C# での Observer パターンの実装 2 - event による実装 2」のクラス図
「C# での Observer パターンの実装 2 - event による実装 2」のクラス図

一つだった Employee 側の Update イベントが、UpdateNumber と UpdateName に分かれている。

・実装 2

それでは、実装してみよう (一部エラー処理を省略)。

// C# による Oberver パターンの実装 - event による実装 2
using System;
using System.Collections.Generic;

// アプリケーション部

// Model: 更新を監視される側
class Employee
{
    public event Action<Employee> UpdateNumber; // Number 更新イベント
    public event Action<Employee> UpdateName; // Name 更新イベント 

    int number = 0;
    string name = string.Empty;

    public int Number
    {
        get { return number; }
        set {
            if (value != number) {
                number = value;
                if (UpdateNumber != null)
                    UpdateNumber(this);
            }
        }
    }

    public string Name
    {
        get { return name; }
        set {
            if (value != name) {
                name = value;
                if (UpdateName != null)
                    UpdateName(this);
            }
        }
    }
}

class TextControl // テキスト表示用のUI部品 (ダミー)
{
    public string Text {
        set { Console.WriteLine("TextControl is updated: {0}", value); }
    }
}

class EmployeeView // Employee 用の View: 更新を監視する側
{
    TextControl numberTextControl = new TextControl(); // Number 表示用
    TextControl nameTextControl = new TextControl(); // Name 表示用

    public Employee DataSource
    {
        set {
            // それぞれの更新イベントでそれぞれ TextControl を更新
            value.UpdateNumber += employee => numberTextControl.Text = employee.Number.ToString();
            value.UpdateName += employee => nameTextControl.Text = employee.Name;
        }
    }
}

class Program
{
    static void Main()
    {
        var employee = new Employee(); // Model, Observable
        var employeeView = new EmployeeView(); // View, Observer
    
        employeeView.DataSource = employee; // データバインド
        employee.Number = 100; // Number を変更。employeeView に反映されたかな?
        employee.Name = "福井太郎"; // Name を変更。employeeView に反映されたかな?
    }
}
・実行結果

実行してみると、次のようになる。

Number と Name それぞれの更新で、それぞれの TextControl が更新されている。

TextControl is updated: 100
TextControl is updated: 福井太郎

それぞれで更新されるようになったのは良いが、Employee 側でプロパティ毎にイベントを用意しなければいけない。

これではプロパティの数が増えてくると、コードが複雑になりそうだ。

■ 今回のまとめと次回の予告

さて、Observer パターンの C# による実装の第二回として、C# の event を用いた実装を行ってみた。

前回のようなフレームワーク部がない。

次回は、フレームワーク部を復活させてみる。

2012年12月17日

[C#][Design Pattern] C# による Observer パターンの実装 その3 - 複数のプロパティの更新イベントをフレームワーク側で振り分け

Observer

前回「C# による Observer パターンの実装 その2 - event による実装」と云う記事で、Observer パターンの C# による実装の第二回として、C# の event を用いた実装を行ってみた。

event を用いることで、より C# らしい実装となった。

C# での Observer パターンの実装 1 - 古典的な実装」のフレームワーク部にあたる部分を C# の event 機構で行ってしまっている訳だ。

すっきりとした実装となったが、監視される側 (Observer 側) でプロパティ毎にイベントを用意しなければいけない、という面があった。

「プロパティの数が増えてくると、コードが複雑になりそう」と云う点が気になった。

そこで、今回は、「C# による Observer パターンの実装 その3 - 複数のプロパティの更新イベントをフレームワーク側で振り分け」として、その2で無くなったフレームワーク部を復活させ、そちらに複数のイベント処理を任せてみる。

■ C# での Observer パターンの実装 3

・クラス図 1

今回のクラス図はこんな感じだ。

「C# での Observer パターンの実装 3」のクラス図
「C# での Observer パターンの実装 3」のクラス図

C# による Observer パターンの実装 その1」のときと同様、上半分の青い部分がフレームワーク部、下半分の赤い部分がアプリケーション部だ。

・フレームワーク部 (抽象部)

先ずは、フレームワーク部の方から。

Observable (更新を監視される側) は、今回は、一つだけの Update というイベントを持つ。
どのプロパティが更新されてもこのイベントを起こす。

但し、プロパティ名を引数で受け取るようになっている。

RaiseUpdate と云うメソッドに文字列でプロパティ名を渡すことで、Update イベントが起きる。

また、Observer (更新を監視する側) は、 DataSource として Observable が渡されると、Observable の Update イベントにイベント ハンドラーとして Update メソッドを設定する。

AddUpdateAction は、プロパティの名称毎に処理を登録するメソッドだ。

Observable 側で更新イベントが起きてイベント ハンドラーの Update が呼ばれると、プロパティの名毎の処理が呼ばれる仕組みだ。

最後に、ObjectExtensions だが、このクラスは、オブジェクトのプロパティ名からプロパティの値を取り出すために使われるメソッド Eval を持つ。
このメソッド Eval は、Observer の中で、プロパティ名からプロパティの値を取り出すために使われる。

・アプリケーション部 (具象部)

Employee は Observable だ。
今回も Model として Number と Name と云う二つのプロパティを持っている。

Update イベントは、ここには無い。

各プロパティが更新されたときに、RaiseUpdate をプロパティ名を引数にして呼ぶことで、ベース クラス Observable の持つ Update イベントを起こす。

EmployeeView はオブザーバーだ。

今回も、EmployeeView は Employee を表示するための View で、AddUpdateAction メソッドを使って、プロパティ毎の更新処理を登録する。

・実装

それでは、実装していこう (一部エラー処理を省略)。

・フレームワーク部の実装

フレームワーク部の ObjectExtensions と Observable、Observer から実装しよう。

// C# による Oberver パターンの実装 その3
using System;
using System.Collections.Generic;

// フレームワーク部

public static class ObjectExtensions
{
    // オブジェクトの指定された名前のプロパティの値を取得
    public static object Eval(this object item, string propertyName)
    {
        var propertyInfo = item.GetType().GetProperty(propertyName);
        return propertyInfo == null ? null : propertyInfo.GetValue(item, null);
    }
}

abstract class Observable // 更新を監視される側
{
    public event Action<string> Update;

    protected void RaiseUpdate(string propertyName)
    {
        if (Update != null)
            Update(propertyName);
    }
}

abstract class Observer // 更新を監視する側
{
    Dictionary<string, Action<object>> updateExpressions = new Dictionary<string, Action<object>>();
    Observable dataSource = null;

    public Observable DataSource
    {
        set {
            dataSource = value;
            value.Update += Update;
        }
    }

    protected void AddUpdateAction(string propertyName, Action<object> updateAction)
    {
        updateExpressions[propertyName] = updateAction;
    }

    void Update(string propertyName)
    {
        Action<object> updateAction;
        if (updateExpressions.TryGetValue(propertyName, out updateAction))
            updateAction(dataSource.Eval(propertyName));
    }
}
・アプリケーション部の実装

続いて、アプリケーション部。Employee と EmployeeView、TextControl、そして Main を含んだクラス Program だ。

// C# による Oberver パターンの実装 その3
using System;

// アプリケーション部

// Model
class Employee : Observable
{
    int number = 0;
    string name = string.Empty;

    public int Number
    {
        get { return number; }
        set {
            if (value != number) {
                number = value;
                RaiseUpdate("Number");
            }
        }
    }

    public string Name
    {
        get { return name; }
        set {
            if (value != name) {
                name = value;
                RaiseUpdate("Name");
            }
        }
    }
}

class TextControl // テキスト表示用のUI部品 (ダミー)
{
    public string Text
    {
        set { Console.WriteLine("TextControl is updated: {0}", value); }
    }
}

class EmployeeView : Observer // Employee 用の View
{
    TextControl numberTextControl = new TextControl(); // Number 表示用
    TextControl nameTextControl = new TextControl(); // Name 表示用

    public EmployeeView()
    {
        AddUpdateAction("Number", number => numberTextControl.Text = ((int)number).ToString());
        AddUpdateAction("Name", name => nameTextControl.Text = (string)name);
    }
}

class Program
{
    static void Main()
    {
        var employee = new Employee(); // Model, Observable
        var employeeView = new EmployeeView(); // View, Observer

        employeeView.DataSource = employee; // データバインド
        employee.Number = 100; // Number を変更。employeeView に反映されたかな?
        employee.Name = "福井太郎"; // Name を変更。employeeView に反映されたかな?
    }
}
・クラス図 2

先のクラス図に、引数と戻り値の型を書き加えたものを示す。

「C# での Observer パターンの実装 3」のクラス図
「C# での Observer パターンの実装 3」のクラス図
・実行結果

実行してみると、次のようになる。

前回の二つ目と同じ結果だ。

TextControl is updated: 100
TextControl is updated: 福井太郎

Number と Name それぞれの更新で、それぞれの TextControl が更新されている。

■ 今回のまとめと次回の予告

今回は、フレームワーク部で、プロパティ毎の更新処理が呼ばれるようにした。

結果として、アプリケーション部の Employee でプロパティ毎に更新イベントを用意する必要がなくなった。

しかし、アプリケーション部の一部に文字列でプロパティを指定する部分が出来てしまった。

Employee の中の、RaiseUpdate("Number") と RaiseUpdate("Name")、EmployeeView の中の AddUpdateAction("Number", ...) と AddUpdateAction("Name", ...) だ。

次回は、この問題を解決しよう。

2012年12月18日

[C#][Design Pattern][式木] C# による Observer パターンの実装 その4 - Expression を使ってプロパティの指定をタイプセーフに

Observer

前回「C# による Observer パターンの実装 その3 - 複数のプロパティの更新イベントをフレームワーク側で振り分け」と云う記事で、Observer パターンの C# による実装の第三回として、フレームワーク部で、プロパティ毎の更新処理が呼ばれるようにした。

結果として、アプリケーション部の Employee でプロパティ毎に更新イベントを用意する必要がなくなった。

しかし、アプリケーション部の一部に文字列でプロパティを指定する部分が出来てしまった。

そこで、今回は、「C# による Observer パターンの実装 その4 - Expression を使ってプロパティの指定をタイプセーフに」として、文字列でプロパティを指定する部分を解消してみよう。

■ C# での Observer パターンの実装 4

以前、「Expression を使ってラムダ式のメンバー名を取得する」と云う記事で、Expression を使ってラムダ式からプロパティ名を取得したことがある。

これを用いることが出来る。

・フレームワーク部 - ObjectExtensions への追加

前回の ObjectExtensions に、件の記事から、Expression からメンバー名を取得する部分を持ってこよう。

using System;
using System.Linq.Expressions;

public static class ObjectExtensions
{
    // オブジェクトの指定された名前のプロパティの値を取得 (前回作成)
    public static object Eval(this object item, string propertyName)
    {
        var propertyInfo = item.GetType().GetProperty(propertyName);
        return propertyInfo == null ? null : propertyInfo.GetValue(item, null);
    }

    // Expression からメンバー名を取得 (「Expression を使ってラムダ式のメンバー名を取得する」で作成)
    public static string GetMemberName<ObjectType, MemberType>(this ObjectType @this, Expression<Func<ObjectType, MemberType>> expression)
    {
        return ((MemberExpression)expression.Body).Member.Name;
    }

    // Expression からメンバー名を取得 (「Expression を使ってラムダ式のメンバー名を取得する」で作成)
    public static string GetMemberName<MemberType>(Expression<Func<MemberType>> expression)
    {
        return ((MemberExpression)expression.Body).Member.Name;
    }
}
・フレームワーク部 - Observable と Observer の変更

そして、前回のフレームワーク部の Observable と Observer のソースコード:

// C# による Oberver パターンの実装 その3 (前回)
using System;
using System.Collections.Generic;

// フレームワーク部

abstract class Observable // 更新を監視される側
{
    public event Action<string> Update;

    protected void RaiseUpdate(string propertyName)
    {
        if (Update != null)
            Update(propertyName);
    }
}

abstract class Observer // 更新を監視する側
{
    Dictionary<string, Action<object>> updateExpressions = new Dictionary<string, Action<object>>();
    Observable dataSource = null;

    public Observable DataSource
    {
        set {
            dataSource = value;
            value.Update += Update;
        }
    }

    protected void AddUpdateAction(string propertyName, Action<object> updateAction)
    {
        updateExpressions[propertyName] = updateAction;
    }

    void Update(string propertyName)
    {
        Action<object> updateAction;
        if (updateExpressions.TryGetValue(propertyName, out updateAction))
            updateAction(dataSource.Eval(propertyName));
    }
}

これの、文字列でプロパティ名を受けている箇所に、Expression で受けるメソッドを追加する。

こうだ。

// C# による Oberver パターンの実装 その4
using System;
using System.Collections.Generic;
using System.Linq.Expressions;

abstract class Observable // 更新を監視される側
{
    public event Action<string> Update;

    protected void RaiseUpdate<PropertyType>(Expression<Func<PropertyType>> propertyExpression)
    {
        RaiseUpdate(ObjectExtensions.GetMemberName(propertyExpression));
    }

    void RaiseUpdate(string propertyName)
    {
        if (Update != null)
            Update(propertyName);
    }
}

abstract class Observer<ObservableType> where ObservableType : Observable // 更新を監視する側
{
    Dictionary<string, Action<object>> updateExpressions = new Dictionary<string, Action<object>>();
    ObservableType dataSource = null;

    public ObservableType DataSource
    {
        set {
            dataSource = value;
            value.Update += Update;
        }
    }

    protected void AddUpdateAction<PropertyType>(Expression<Func<ObservableType, PropertyType>> propertyExpression, Action<object> updateAction)
    {
        AddUpdateAction(dataSource.GetMemberName(propertyExpression), updateAction);
    }

    void AddUpdateAction(string propertyName, Action<object> updateAction)
    {
        updateExpressions[propertyName] = updateAction;
    }

    void Update(string propertyName)
    {
        Action<object> updateAction;
        if (updateExpressions.TryGetValue(propertyName, out updateAction))
            updateAction(dataSource.Eval(propertyName));
    }
}
・アプリケーション部 - Employee と EmployeeView の変更

すると、前回のアプリケーション部の Employee と EmployeeView のソースコード:

// C# による Oberver パターンの実装 その3 (前回)
using System;

// アプリケーション部

// Model
class Employee : Observable
{
    int number = 0;
    string name = string.Empty;

    public int Number
    {
        get { return number; }
        set {
            if (value != number) {
                number = value;
                RaiseUpdate("Number"); // ☆ 文字列でプロパティを指定
            }
        }
    }

    public string Name
    {
        get { return name; }
        set {
            if (value != name) {
                name = value;
                RaiseUpdate("Name"); // ☆ 文字列でプロパティを指定
            }
        }
    }
}

class TextControl // テキスト表示用のUI部品 (ダミー)
{
    public string Text
    {
        set { Console.WriteLine("TextControl is updated: {0}", value); }
    }
}

class EmployeeView : Observer // Employee 用の View
{
    TextControl numberTextControl = new TextControl(); // Number 表示用
    TextControl nameTextControl = new TextControl(); // Name 表示用

    public EmployeeView()
    {
        // ☆ AddUpdateAction の第一引数で文字列でプロパティを指定
        AddUpdateAction("Number", number => numberTextControl.Text = ((int)number).ToString());
        AddUpdateAction("Name", name => nameTextControl.Text = (string)name);
    }
}

これが、こうなる。

// C# による Oberver パターンの実装 その4
using System;

// アプリケーション部

// Model
class Employee : Observable
{
    int number = 0;
    string name = string.Empty;

    public int Number
    {
        get { return number; }
        set {
            if (value != number) {
                number = value;
                RaiseUpdate(() => Number); // ☆ RaiseUpdate がタイプセーフに
            }
        }
    }

    public string Name
    {
        get { return name; }
        set {
            if (value != name) {
                name = value;
                RaiseUpdate(() => Name); // ☆ RaiseUpdate がタイプセーフに
            }
        }
    }
}

class TextControl // テキスト表示用のUI部品 (ダミー)
{
    public string Text
    {
        set { Console.WriteLine("TextControl is updated: {0}", value); }
    }
}

class EmployeeView : Observer<Employee> // Employee 用の View
{
    TextControl numberTextControl = new TextControl(); // Number 表示用
    TextControl nameTextControl = new TextControl(); // Name 表示用

    public EmployeeView()
    {
        // ☆ AddUpdateAction の第一引数がタイプセーフに
        AddUpdateAction(employee => employee.Number, number => numberTextControl.Text = ((int)number).ToString());
        AddUpdateAction(employee => employee.Name, name => nameTextControl.Text = (string)name);
    }
}

文字列でプロパティを指定している箇所が消えた。

・Main を含んだクラス Program

Main を含んだクラス Program は、変更なし。

class Program
{
    static void Main()
    {
        var employee = new Employee(); // Model, Observable
        var employeeView = new EmployeeView(); // View, Observer

        employeeView.DataSource = employee; // データバインド
        employee.Number = 100; // Number を変更。employeeView に反映されたかな?
        employee.Name = "福井太郎"; // Name を変更。employeeView に反映されたかな?
    }
}
・実行結果

実行結果も変わらない。

TextControl is updated: 100
TextControl is updated: 福井太郎

矢張り、Number と Name それぞれの更新で、それぞれの TextControl が更新されている。

・クラス図

さてクラス図はこうなった。
前回と余り変わらない。

「C# での Observer パターンの実装 4」のクラス図
「C# での Observer パターンの実装 4」のクラス図
「C# での Observer パターンの実装 4」のクラス図 (引数と戻り値の型を書き加えたもの)
「C# での Observer パターンの実装 4」のクラス図 (引数と戻り値の型を書き加えたもの)

例によって、青い部分がフレームワーク部、赤い部分がアプリケーション部だ。

■ 今回のまとめ

今回は、前回のものに対して、プロパティを文字列で指定している部分を改良した。

そうすることで、アプリケーション部でプロパティを指定する箇所がタイプセーフになった。

アプリケーション部が随分すっきりと書けるようになったのが判る。

■ 次回予告

さて次回だが、今回 Expression を使って行ったのと同様のことを別の方法で実現してみたい。

お楽しみに。

2012年12月19日

[Windows 8][Windows ストア アプリ] Windows 8 ストア アプリ開発資料リンク集

Windows 8

Windows 8 の ストア アプリ開発を始めるにあたり、参考にさせていただいたサイトをご紹介する。

2012年12月20日

[Windows 8][Windows ストア アプリ][C#] ポータブル クラス ライブラリに関する検証

Windows 8 Store apps Advent Calendar の 20日目のエントリー。

以前、「Windows Store アプリと Windows Phone アプリ、Silverlight アプリ、WPF アプリでソースコードを共通化する方法に関する記事」と云う記事でポータブル クラス ライブラリに関して少しだけご紹介した。

今回は、ポータブル クラス ライブラリについて、更に調べてみよう。

■ アプリケーションの種類と .NET Framework の違い

Windows ストア アプリで参照している .NET は、他のアプリケーションで参照しているものと少し異なる。

その様子を先ず確認してみよう。

現時点で最新の環境で何種類かのアプリケーションをデフォルトで追加してみて、参照している .NET を見てみる。

・コンソール アプリケーションとクラス ライブラリの場合 (.NET Framework 4.5)
コンソール アプリケーションとクラス ライブラリで参照している .NET
コンソール アプリケーションとクラス ライブラリで参照している .NET
・WPF の場合 (.NET Framework 4.5)
WPF アプリケーションで参照している .NET
WPF アプリケーションで参照している .NET
・Silverlight の場合 (Silverlight 5)
Silverlight アプリケーションと Silverlight クラス ライブラリで参照している .NET
Silverlight アプリケーションと Silverlight クラス ライブラリで参照している .NET
・Windows Phone の場合 (Windows Phone OS 8.0)
Windows Phone アプリと Windows Phone クラス ライブラリで参照している .NET
Windows Phone アプリと Windows Phone クラス ライブラリで参照している .NET

「.NET for Windows Phone」となっている。

・Windows ストア アプリの場合
Windows ストア アプリとWindows ストア クラス ライブラリで参照している .NET
Windows ストア アプリとWindows ストア クラス ライブラリで参照している .NET

こちらでは、「.NET for Windows Store apps」となっているのが判る。

■ IsSubclassOf 等による検証

つまり、これらで参照している .NET は、全てが共通な訳ではない。

コア部分は共通なのだろうか?

私が試してみたところ、System 名前空間の付近でも微妙な違いがあるようだ。

今回は、以下のようなコードを用いて、この辺りを検証してみたい。

// コンソール アプリケーション
// .NET Framework 4.5

using System.Reflection; // ※ GetRuntimeProperties に必要

class Program
{
    class Super { }
    class Sub : Super
    {
        public int Number { get; set; }
        public string Name { get; set; }
    }

    static void Test()
    {
        var sub = new Sub();

        bool result1 = sub.GetType().IsSubclassOf(typeof(Super));
        bool result2 = sub.GetType().IsAssignableFrom(typeof(Super));
        bool result3 = sub is Super;

        var properties1 = typeof(Sub).GetProperties();
        var properties2 = typeof(Sub).GetRuntimeProperties();
    }
        
    static void Main()
    {
        Test();
    }
}

これはコンソール アプリケーションのソースコードだが、正常にコンパイルでき、正常に動作する。

Visual Studio のデバッガーで値を確認してみると、次のようになった。

コンソール アプリケーションの場合の実行結果 1
コンソール アプリケーションの場合の実行結果 1
コンソール アプリケーションの場合の実行結果 2
コンソール アプリケーションの場合の実行結果 2
コンソール アプリケーションの場合の実行結果 3
コンソール アプリケーションの場合の実行結果 3
・WPF と Silverlight の場合

同様のことを、WPF と Silverlight の場合で試してみると次のようになる。

先ず WPF から。

// WPF アプリケーション
// .NET Framework 4.5

using System.Windows;

namespace WpfApplication
{
    using System.Reflection; // ※ GetRuntimeProperties に必要
    
    public partial class App : Application
    {
        class Super { }
        class Sub : Super
        {
            public int Number { get; set; }
            public string Name { get; set; }
        }

        static void Test()
        {
            var sub = new Sub();

            bool result1 = sub.GetType().IsSubclassOf(typeof(Super)); // ○ OK
            bool result2 = sub.GetType().IsAssignableFrom(typeof(Super)); // ○ OK
            bool result3 = sub is Super; // ○ OK

            var properties1 = typeof(Sub).GetProperties(); // ○ OK
            var properties2 = typeof(Sub).GetRuntimeProperties(); // ○ OK
        }
        
        public App()
        {
            Test();
        }
    }
}

WPF では問題なく、全てコンパイルでき、正常に動作する。

参照している .NET が同じ .NET Framework 4.5 なので、当たり前と云えば当たり前だ。

次に Silverlight。

// Silverlight 5

using System;
using System.Windows;

namespace SilverlightApplication
{
    using System.Reflection; // ※ GetRuntimeProperties に必要

    public partial class App : Application
    {
        class Super { }
        class Sub : Super
        {
            public int Number { get; set; }
            public string Name { get; set; }
        }

        static void Test()
        {
            var sub = new Sub();

            bool result1 = sub.GetType().IsSubclassOf(typeof(Super)); // ○ OK
            bool result2 = sub.GetType().IsAssignableFrom(typeof(Super)); // ○ OK
            bool result3 = sub is Super; // ○ OK

            var properties1 = typeof(Sub).GetProperties(); // ○ OK
            var properties2 = typeof(Sub).GetRuntimeProperties(); // × コンパイル エラー
        }

        public App()
        {
            Test();

            // ... 以下省略 ...
        }

        // ... 以下省略 ...
    }
}

Silverlight の方は、次のようなコンパイル エラーになる。

  • エラー 1 'System.Type' に 'GetRuntimeProperties' の定義が含まれておらず、型 'System.Type' の最初の引数を受け付ける拡張メソッドが見つかりませんでした。using ディレクティブまたはアセンブリ参照が不足しています。

この GetRuntimeProperties は、 System.Reflection 名前空間の RuntimeReflectionExtensions クラスが持つ拡張メソッドだ。

.NET Framework 4.5 で使えるようになったものだが、Silverlight では使えないようだ。

・Windows Phone の場合

ちなみに、Windows Phone では次のようになる。

// Windows Phone OS 8.0

using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using PhoneApp.Resources;
using System.Diagnostics;
using System.Windows;
using System.Windows.Markup;
using System.Windows.Navigation;

namespace PhoneApp
{
    using System;
    using System.Reflection; // ※ GetRuntimeProperties に必要
    
    public partial class App : Application
    {
        class Super { }
        class Sub : Super
        {
            public int Number { get; set; }
            public string Name { get; set; }
        }

        static void Test()
        {
            var sub = new Sub();

            bool result1 = sub.GetType().IsSubclassOf(typeof(Super)); // ○ OK
            bool result2 = sub.GetType().IsAssignableFrom(typeof(Super)); // ○ OK
            bool result3 = sub is Super; // ○ OK

            var properties1 = typeof(Sub).GetProperties(); // ○ OK
            var properties2 = typeof(Sub).GetRuntimeProperties(); // ○ OK
        }

        public App()
        {
            Test();

            // ... 以下省略 ...
        }

        // ... 以下省略 ...
    }
}

全て問題なくコンパイル・実行できる。

・Windows ストア アプリの場合

さて、Windows ストア アプリではどうなるだろうか。

こうなるのだ。

// Windows ストア アプリ

using System;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace WindowsStoreApp
{
    using System.Reflection; // ※ GetRuntimeProperties に必要
    
    sealed partial class App : Application
    {
        class Super { }
        class Sub : Super
        {
            public int Number { get; set; }
            public string Name { get; set; }
        }

        static void Test()
        {
            var sub = new Sub();

            bool result1 = sub.GetType().IsSubclassOf(typeof(Super)); // × コンパイル エラー
            bool result2 = sub.GetType().IsAssignableFrom(typeof(Super)); // × コンパイル エラー
            bool result3 = sub is Super; // ○ OK

            var properties1 = typeof(Sub).GetProperties(); // × コンパイル エラー
            var properties2 = typeof(Sub).GetRuntimeProperties(); // ○ OK
        }

        public App()
        {
            Test();

            // ... 以下省略 ...

            this.InitializeComponent();
            this.Suspending += OnSuspending;
        }

        // ... 以下省略 ...
    }
}

次のようなコンパイル エラーとなる。

  • エラー 1 'System.Type' に 'IsSubclassOf' の定義が含まれておらず、型 'System.Type' の最初の引数を受け付ける拡張メソッドが見つかりませんでした。using ディレクティブまたはアセンブリ参照が不足しています。
  • エラー 2 'System.Type' に 'IsAssignableFrom' の定義が含まれておらず、型 'System.Type' の最初の引数を受け付ける拡張メソッドが見つかりませんでした。using ディレクティブまたはアセンブリ参照が不足しています。
  • エラー 3 'System.Type' に 'GetProperties' の定義が含まれておらず、型 'System.Type' の最初の引数を受け付ける拡張メソッドが見つかりませんでした。using ディレクティブまたはアセンブリ参照が不足しています。

なんと、System 名前空間の Type 型が IsSubclassOf や IsAssignableFrom を持っていないようだ。

実際に調べてみると、Windows ストア アプリが参照している System 名前空間の Type 型は以下の public メンバーを持っている。

// Windows ストア アプリが参照している System 名前空間の Type 型
// アセンブリ System.Runtime.dll, v4.0.0.0
// Framework\.NETCore\v4.5\System.Runtime.dll
namespace System
{
    public abstract class Type
    {
        public static readonly object Missing;

        public abstract string AssemblyQualifiedName { get; }
        public abstract Type DeclaringType { get; }
        public abstract string FullName { get; }
        public abstract int GenericParameterPosition { get; }
        public abstract Type[] GenericTypeArguments { get; }
        public bool HasElementType { get; }
        public bool IsArray { get; }
        public bool IsByRef { get; }
        public abstract bool IsConstructedGenericType { get; }
        public abstract bool IsGenericParameter { get; }
        public bool IsNested { get; }
        public bool IsPointer { get; }

        public abstract string Name { get; }
        public abstract string Namespace { get; }
        public virtual RuntimeTypeHandle TypeHandle { get; }

        public override bool Equals(object o);
        public bool Equals(Type o);
        public abstract int GetArrayRank();
        public abstract Type GetElementType();
        public abstract Type GetGenericTypeDefinition();
        public override int GetHashCode();
        public static Type GetType(string typeName);
        public static Type GetType(string typeName, bool throwOnError);
        public static Type GetTypeFromHandle(RuntimeTypeHandle handle);
        public abstract Type MakeArrayType();
        public abstract Type MakeArrayType(int rank);
        public abstract Type MakeByRefType();
        public abstract Type MakeGenericType(params Type[] typeArguments);
        public abstract Type MakePointerType();
        public override string ToString();
    }
}

IsSubclassOf、IsAssignableFrom、GetProperties 等が見当たらない。

一方、通常の .NET Framework 4.5 の名前空間の Type 型は遥かに多くの public メンバーを持っている。

// 通常の .NET Framework 4.5 の名前空間の Type 型
// アセンブリ mscorlib.dll, v4.0.0.0
// Framework\.NETFramework\v4.5\mscorlib.dll
namespace System
{
    public abstract class Type : MemberInfo, _Type, IReflect
    {
        bool IsSubclassOf(Type c);
        bool IsAssignableFrom(Type c);
        PropertyInfo[] GetProperties();
        
        // ...その他遥かに多くのメンバー...
    }
}

先ず、アセンブリが異なる。Windows ストア アプリの方は、 mscorlib.dll ではなく System.Runtime.dll だった。

また、こちらは Windows ストア アプリの方の Type と異なり、「class Type : MemberInfo, _Type, IReflect」となっているのが判る。

この中の _Type は実は interface で、以下のように IsSubclassOf、IsAssignableFrom、GetProperties を含む多くのメンバーを持っているのだ。

// 通常の .NET Framework 4.5 の名前空間の Type 型が実装している interface _Type
namespace System.Runtime.InteropServices
{
    public interface _Type
    {
        bool IsSubclassOf(Type c);
        bool IsAssignableFrom(Type c);
        PropertyInfo[] GetProperties();
        
        // ...その他多くのメンバー...
    }
}

■ ポータブル クラス ライブラリによる解決

では、ポータブル クラス ライブラリを利用した場合はどうなるだろうか。

・ポータブル クラス ライブラリの作成

先ず、ポータブル クラス ライブラリを作成する。

ポータブル クラス ライブラリの作成
ポータブル クラス ライブラリの作成

今回は、ターゲット フレームワークとしてデフォルトの儘、.NET Framework 4.5、Silverlight 4 以上、Windows Phone 7 以上、.NET for Windows Store apps を選ぶ。

ポータブル クラス ライブラリの作成
ポータブル クラス ライブラリの作成

ソースコードは以下の通り。

Type 型の IsSubclassOf、IsAssignableFrom、GetProperties をそれぞれ呼ぶだけのメソッドを用意することにする。

// ポータブル クラス ライブラリ
//
// ターゲット フレームワーク:
// ・.NET Framework 4.5
// ・Silverlight 4 以上
// ・Windows Phone 7 以上
// ・.NET for Windows Store apps

namespace PortableClassLibrary
{
    using System;
    using System.Reflection;

    public static class TestClass
    {
        public static bool IsSubclassOf(Type sub, Type super)
        {
            return sub.IsSubclassOf(super);
        }

        public static bool IsAssignableFrom(Type type1, Type type2)
        {
            return type1.IsAssignableFrom(type2);
        }

        public static PropertyInfo[] GetProperties(Type type)
        {
            return type.GetProperties();
        }
    }
}

これは問題なくコンパイルできる。

ちなみに、このポータブル クラス ライブラリで参照している Type 型は次のようなものだ。

// このポータブル クラス ライブラリが参照している Type 型
// アセンブリ mscorlib.dll, v2.0.5.0
// Framework\.NETPortable\v4.0\Profile\Profile4\mscorlib.dll
namespace System
{
    public abstract class Type : MemberInfo
    {
        bool IsSubclassOf(Type c);
        bool IsAssignableFrom(Type c);
        PropertyInfo[] GetProperties();
        
        // ...その他多くのメンバー...
    }
}

こちらは、IsSubclassOf、IsAssignableFrom、GetProperties 等を持っている。_Type interface は持っていない。

また、このポータブル クラス ライブラリが参照している .NET はこうなっている。

このポータブル クラス ライブラリが参照している .NET
このポータブル クラス ライブラリが参照している .NET

「.NET Portable Subset」となっている。

・作成したポータブル クラス ライブラリの参照

では、早速このポータブル クラス ライブラリを各アプリケーション (コンソール アプリケーション、WPF アプリケーション、Silverlight アプリケーション、Windows Phone アプリ、Windows ストア アプリ) でそれぞれ参照してみよう。

ポータブル クラス ライブラリの参照
ポータブル クラス ライブラリの参照

そして、各アプリケーションで、以下のようにこのポータブル クラス ライブラリの三つのメソッド、IsSubclassOf、IsAssignableFrom、GetProperties を呼んでみる。

    // 参照したポータブル クラス ライブラリの利用
    bool result4 = PortableClassLibrary.TestClass.IsSubclassOf(sub.GetType(), typeof(Super));
    bool result5 = PortableClassLibrary.TestClass.IsAssignableFrom(sub.GetType(), typeof(Super));
    var properties3 = PortableClassLibrary.TestClass.GetProperties(sub.GetType());

すると、コンソール アプリケーション、WPF アプリケーション、Silverlight アプリケーション、Windows Phone アプリ、Windows ストア アプリの何れでも、コンパイルでき、正常に動作する。

つまり、ポータブル クラス ライブラリにであれば、この部分のコードは共通化でき、且つそれぞれのアプリケーションから問題なく呼べるのだ。

■ 今回のまとめ

今回は、ポータブル クラス ライブラリに関して、調査をしてみた。

アプリケーションの種類によって、参照している .NET が異なるので、.NET のコア部分を使ったソースコードでも共通のものが使えないことがあることが判った。

ポータブル クラス ライブラリを使うことで、そのような部分のソースコードをより共通化することができるだろう。

2012年12月22日

[C#][Design Pattern] C# による Observer パターンの実装 その5 - Caller Info を使ってプロパティの指定をよりシンプルに

Observer

前回「C# による Observer パターンの実装 その4 - Expression を使ってプロパティの指定をタイプセーフに」と云う記事で、Observer パターンの C# による実装の第四回として、Expression を用いることで、前々回文字列でプロパティを指定していた部分をタイプセーフにしてみた。

今回は、それに対する補足だ。
前回の Expression による遣り方とは別の方法で更にアプリケーション部をシンプルにしてみたい。
C# 5 の新機能の Caller Info を使った方法だ。

■ 前回のソースコード

先ず前回のフレームワーク部の Observable のソースコードを再掲する。

・フレームワーク部 - Observable (前回)
// C# による Oberver パターンの実装 その4
using System;
using System.Collections.Generic;
using System.Linq.Expressions;

abstract class Observable // 更新を監視される側
{
    public event Action<string> Update;

    protected void RaiseUpdate<PropertyType>(Expression<Func<PropertyType>> propertyExpression)
    {
        RaiseUpdate(ObjectExtensions.GetMemberName(propertyExpression));
    }

    void RaiseUpdate(string propertyName)
    {
        if (Update != null)
            Update(propertyName);
    }
}

Observable の RaiseUpdate において、Expression で受けることでプロパティ名を動的に取得している。

この部分を C#5.0 の CallerInfo を使う形で書き換えてみよう。

Caller Info 属性の一つである [CallerMemberName] を付けておくと、呼び出し元のメンバー名 (この場合はプロパティ名) がコンパイラーによって渡されてくる。
これを利用してみよう。

・フレームワーク部 - Observable (今回)
// C# による Oberver パターンの実装 その5
using System;
using System.Runtime.CompilerServices; // ☆ [CallerMemberName] の為に必要

abstract class Observable // 更新を監視される側
{
    public event Action<string> Update;

    protected void RaiseUpdate([CallerMemberName] string propertyName = "") // ☆ Caller Info 属性を利用
    {
        if (Update != null)
            Update(propertyName);
    }
}

すると、Observable の派生クラスで、プロパティ名を渡さなくて良くなる。

前回の Employee はこうだった。

・アプリケーション部 - Employee と EmployeeView (前回)
// C# による Oberver パターンの実装 その4
using System;

// アプリケーション部

// Model
class Employee : Observable
{
    int number = 0;
    string name = string.Empty;

    public int Number
    {
        get { return number; }
        set {
            if (value != number) {
                number = value;
                RaiseUpdate(() => Number); // ☆ RaiseUpdate がタイプセーフに
            }
        }
    }

    public string Name
    {
        get { return name; }
        set {
            if (value != name) {
                name = value;
                RaiseUpdate(() => Name); // ☆ RaiseUpdate がタイプセーフに
            }
        }
    }
}

これがこうなる。

・アプリケーション部 - Employee と EmployeeView (今回)
// C# による Oberver パターンの実装 その5
using System;

// アプリケーション部

// Model
class Employee : Observable
{
    int number = 0;
    string name = string.Empty;

    public int Number
    {
        get { return number; }
        set {
            if (value != number) {
                number = value;
                RaiseUpdate(); // ☆ RaiseUpdate の中で文字列でプロパティを渡す必要がなくなった
            }
        }
    }

    public string Name
    {
        get { return name; }
        set {
            if (value != name) {
                name = value;
                RaiseUpdate(); // ☆ RaiseUpdate の中で文字列でプロパティを渡す必要がなくなった
            }
        }
    }
}

前回のような動的にプロパティ名を取得する方法と異なり、コンパイラーが渡してくれるので、オーバーヘッドも小さくなる筈だ。

・全体のソースコード (今回)

全体のソースコードはこうなった。

// C# による Oberver パターンの実装 その5
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Runtime.CompilerServices; // ☆ [CallerMemberName] の為に必要

// フレームワーク部

public static class ObjectExtensions
{
    // オブジェクトの指定された名前のプロパティの値を取得
    public static object Eval(this object item, string propertyName)
    {
        var propertyInfo = item.GetType().GetProperty(propertyName);
        return propertyInfo == null ? null : propertyInfo.GetValue(item, null);
    }

    // Expression からメンバー名を取得
    public static string GetMemberName<ObjectType, MemberType>(this ObjectType @this, Expression<Func<ObjectType, MemberType>> expression)
    {
        return ((MemberExpression)expression.Body).Member.Name;
    }
}

abstract class Observable // 更新を監視される側
{
    public event Action<string> Update;

    protected void RaiseUpdate([CallerMemberName] string propertyName = "") // ☆ Caller Info 属性を利用
    {
        if (Update != null)
            Update(propertyName);
    }
}

abstract class Observer<ObservableType> where ObservableType : Observable // 更新を監視する側
{
    Dictionary<string, Action<object>> updateExpressions = new Dictionary<string, Action<object>>();
    ObservableType dataSource = null;

    public ObservableType DataSource
    {
        set {
            dataSource = value;
            value.Update += Update;
        }
    }
    
    protected void AddUpdateAction<PropertyType>(Expression<Func<ObservableType, PropertyType>> propertyExpression, Action<object> updateAction)
    {
        AddUpdateAction(dataSource.GetMemberName(propertyExpression), updateAction);
    }

    void AddUpdateAction(string propertyName, Action<object> updateAction)
    {
        updateExpressions[propertyName] = updateAction;
    }

    void Update(string propertyName)
    {
        Action<object> updateAction;
        if (updateExpressions.TryGetValue(propertyName, out updateAction))
            updateAction(dataSource.Eval(propertyName));
    }
}
// C# による Oberver パターンの実装 その5
using System;

// アプリケーション部

// Model
class Employee : Observable
{
    int number = 0;
    string name = string.Empty;

    public int Number
    {
        get { return number; }
        set {
            if (value != number) {
                number = value;
                RaiseUpdate(); // ☆ RaiseUpdate の中で文字列でプロパティを渡す必要がなくなった
            }
        }
    }

    public string Name
    {
        get { return name; }
        set {
            if (value != name) {
                name = value;
                RaiseUpdate(); // ☆ RaiseUpdate の中で文字列でプロパティを渡す必要がなくなった
            }
        }
    }
}

class TextControl // テキスト表示用のUI部品 (ダミー)
{
    public string Text
    {
        set { Console.WriteLine("TextControl is updated: {0}", value); }
    }
}

class EmployeeView : Observer<Employee> // Employee 用の View
{
    TextControl numberTextControl = new TextControl(); // Number 表示用
    TextControl nameTextControl = new TextControl(); // Name 表示用

    public EmployeeView()
    {
        AddUpdateAction(employee => employee.Number, number => numberTextControl.Text = ((int)number).ToString());
        AddUpdateAction(employee => employee.Name, name => nameTextControl.Text = (string)name);
    }
}

class Program
{
    static void Main()
    {
        var employee = new Employee(); // Model, Observable
        var employeeView = new EmployeeView(); // View, Observer

        employeeView.DataSource = employee; // データバインド
        employee.Number = 100; // Number を変更。employeeView に反映されたかな?
        employee.Name = "福井太郎"; // Name を変更。employeeView に反映されたかな?
    }
}
・実行結果

勿論、実行結果に変わりはない。

TextControl is updated: 100
TextControl is updated: 福井太郎

■ 今回のまとめと次回予告

今回は、前回のものに対して補足を行った。

Caller Info を使うことで、アプリケーション部でプロパティを指定する箇所が更にシンプルになった。

次回は、DynamicObject を使った全然別のアプローチを試してみたい。

お楽しみに。

2012年12月24日

[C#][dynamic] プラグイン処理 2 (DLL/C#/Python に対応させてみる)

Dynamic

前回の「プラグイン処理」の続き。

今回は、前回のコードに少し付け足して、様々な種類のプラグインに対応してみよう。

前回は、DLL だけをプラグインとして使えるようにしたが、今回は、それに加えて、C# と Python のプラグインも使えるようにしてみたい。

■ 今回のプラグインの規約

今回のプラグインも、前回同様、以下のような規約ベースで動くものとする。

  • 実行中のプログラムがあるフォルダーの下の Plugin と云う名前のフォルダーにある dll ファイルをプラグインのアセンブリ、cs ファイルを C# のプラグイン、py ファイルを Python のプラグインと看做す。
  • DLL プラグインと C# プラグインでは、最初の public なクラスをプラグインと見做す。
  • プラグインは、必ず string Name() と云う名称を返す public なメソッドを持っている。
  • プラグインは、必ず void Run() と云う public なメソッドによって動作する。

■ プラグイン側の実装の例

では、プラグイン側から実装してみよう。

今回用意するのは、以下の三種類だ。

・DLL プラグインの実装の例

DLL プラグインは、クラスライブラリとして作成し、ビルドして "Test.dll" とする。

public なクラスを持ち、その中には、public な Name() メソッドと public な Run() メソッドを持つ。

// DLL プラグイン (クラスライブラリとして作成し、ビルドして "Test.dll" に)
using System;

public class Plugin
{
    public string Name()
    {
        return "DLL Plugin";
    }

    public void Run()
    {
        Console.WriteLine("DLL Plugin is running!");
    }
}
・C# プラグインの実装の例

C# プラグインは、一つの cs ファイルだ。"Test.cs" として保存する。

public なクラスを持ち、その中には、public な Name() メソッドと public な Run() メソッドを持つ。

// C# プラグイン ("Test.cs" として保存)
using System;

public class Plugin
{
    public string Name()
    {
        return "C# Plugin";
    }

    public void Run()
    {
        Console.WriteLine("C# Plugin is running!");
    }
}
・Python プラグインの実装の例

Python プラグインは、一つの py ファイルだ。"Test.py" として保存する。

Name() メソッドと Run() メソッドを持つ。

# Python Plugnin (Save as "Test.py".)
def Name():
  return "Python plugin"

def Run():
  print "Python plugin is running!\n"

■ プラグインが組み込まれる本体側の実装の例

次に、プラグインが組み込まれる本体側の実装だ。

・IronPython を利用する為の準備

先ず、Python をプラグインとして使えるようにするために、IronPython をインストールしよう。

IronPython は、Visual Studio で NuGet からインストール出来る。

IronPython のインストール
IronPython のインストール

IronPython のインストールが終わると、プロジェクトの参照設定は、次のように IronPython を使う為の参照が追加されている。

IronPython のインストール後の参照設定
IronPython のインストール後の参照設定
・プラグインが組み込まれる本体側の実装の例

では、本体側を実装しよう。

using IronPython.Hosting; // Python プラグインの処理に必要
using Microsoft.CSharp; // C# プラグインの処理に必要
using System;
using System.CodeDom.Compiler; // C# プラグインの処理に必要
using System.IO;
using System.Linq;
using System.Reflection;

class Program
{
    // プラグインのフォルダー名
    const string pluginFolderName = "Plugin";
    
    static void Main()
    {
        // プラグインのフォルダーへのフルパス名を取得し、
        var pluginFolderName = GetPluginFolderName();
        // もしプラグインのフォルダーが在ったら、
        if (Directory.Exists(pluginFolderName))
            // プラグインのフォルダーの各のプラグインのパス名を使って、プラグインを実行
            Directory.GetFiles(pluginFolderName, "*.*").ToList().ForEach(Run);
    }

    // プラグインのフォルダーへのフルパス名を取得
    static string GetPluginFolderName()
    {
        // 実行中のこのプログラム自体のアセンブリのパスのフォルダーの下の Plugin フォルダーへのフルパスを取得
        return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + Path.DirectorySeparatorChar + pluginFolderName;
    }

    // プラグインを実行
    static void Run(string pluginPath)
    {
        switch (Path.GetExtension(pluginPath).ToLower()) {
            case ".dll": /* DLL の場合       */ RunDll(pluginPath); break;
            case ".cs" : /* C# のコードの場合 */ RunCSharp(pluginPath); break;
            case ".py": /* Python のコードの場合 */ RunPython(pluginPath); break;
        }
    }

    // DLL プラグインを実行
    static void RunDll(string path)
    {
        // DLL をアセンブリとして読み込む
        var assembly = Assembly.LoadFrom(path);
        if (assembly != null)
            // アセンブリをプラグインとして実行
            Run(assembly);
    }

    // C# プラグインを実行
    static void RunCSharp(string pathName)
    {
        // C# のコードをアセンブリに変換
        var assembly = CodeToAssembly(pathName);
        if (assembly != null)
            // アセンブリに変換されたプラグインを実行
            Run(assembly);
    }

    // Python プラグインを実行
    static void RunPython(string pathName)
    {
        dynamic plugin = Python.CreateRuntime().UseFile(pathName); // Python のコードからランタイムを作成し、
        Run(plugin); // それを実行
    }

    // プラグインを実行
    static void Run(Assembly pluginAssembly)
    {
        // アセンブリを読み込み、その中から public な最初のクラスを取り出す
        var pluginType = pluginAssembly.GetExportedTypes().FirstOrDefault(type => type.IsClass);
        if (pluginType != null)
            // インスタンスを生成し、それをプラグインとして実行
            Run(Activator.CreateInstance(pluginType));
    }

    // プラグインを実行
    static void Run(dynamic plugin)
    {
        Console.WriteLine(plugin.Name()); // プラグインの Name を表示し、
        plugin.Run(); // プラグインを Run
    }

    // C# のコードをコンパイルしてアセンブリに変換
    public static Assembly CodeToAssembly(string csharpCode)
    {
        using (var cscp = new CSharpCodeProvider()) {
            // コンパイルした結果のアセンブリを返す
            return cscp.CompileAssemblyFromFile(new CompilerParameters { GenerateInMemory = true }, csharpCode).CompiledAssembly;
        }
    }
}
  • DLL プラグインは前回と同じ。
    アセンブリとして読み込んで、その中から public な最初のクラスを取り出し、中のメソッドを dynamic に実行する。
  • C# プラグインはコンパイルし、メモリ上でアセンブリに変換する。後は DLL プラグインと同じ。
  • Python プラグインは、コンパイルせずに、動的にスクリプトとして実行することでメソッドを呼び出す。

dynamic を使うことで、Python のプラグインも同じように実行することができる。

■ プラグイン処理の実行例

では、実行してみよう。

プログラムを実行しているフォルダーの下に Plugin と云う名前のフォルダーを作成し、そこに三つのプラグイン "Test.dll"、"Test.cs"、"Test.py" を置く。

Plugin フォルダーの中に置かれた三つのプラグイン
Plugin フォルダーの中に置かれた三つのプラグイン

本体プログラムの実行結果は次の通りだ。

DLL Plugin
DLL Plugin is running!
C# Plugin
C# Plugin is running!
Python plugin
Python plugin is running!

各プラグインが実行された。

■ 今回のまとめ

今回は、前回のプラグイン処理に少し補足を行った。

Visual Basic.NET や F#、IronRuby 等も同様に扱えるのでないだろうか。

2012年12月25日

[C#][Design Pattern][DynamicObject][dynamic] C# による Observer パターンの実装 その6 - DynamicObject を使ってオブザーバーを作る

C# Advent Calender 2012 の 25日目のエントリー。
Observer

本ブログでは、これ迄五回に渡り、C# による Observer パターンの実装をご紹介してきた。

前前回の「C# による Observer パターンの実装 その4 - Expression を使ってプロパティの指定をタイプセーフに」と前回の「C# による Observer パターンの実装 その5 - Caller Info を使ってプロパティの指定をよりシンプルに」と云う記事では、Expression や Caller Info を用いることで、第三回で文字列でプロパティを指定していた部分をシンプルな記述にしてみた。

今回は、同じく第三回からの改良を行ってみたい。
第四回第五回の遣り方とは別のアプローチでアプリケーション部をシンプルにしてみたい。

■ DynamicObject の応用

以前、「DynamicObject を使ってみよう」や「DynamicObject を使ってみよう その 2」と云う記事で DynamicObject をご紹介した。

これらの記事で、DynamicObject を用いることで、プロパティの設定時の処理を定義出来ることを示した。

今回は、この仕組みを用いて、フレームワーク部の Observable で動的にプロパティの更新を捕まえ、更新イベントを発行してみよう。

■ C# での Observer パターンの実装 5

・クラス図

全体像をざっと把握していただくために、先ずクラス図を示そう。

「C# による Observer パターンの実装 その6」のクラス図
「C# による Observer パターンの実装 その6」のクラス図

引数と戻り値の型を書き加えたクラス図だとこうなる。

「C# による Observer パターンの実装 その6」のクラス図 (引数と戻り値の型を書き加えたもの)
「C# による Observer パターンの実装 その6」のクラス図 (引数と戻り値の型を書き加えたもの)

例によって、青い部分がフレームワーク部、赤い部分がアプリケーション部だ。

・フレームワーク部の実装

では、フレームワーク部から実装していこう。

・フレームワーク部の実装 - ObjectExtensions

第三回の ObjectExtensions を少し書き換えて、今回の DynamicObject を用いた例に対応できるようにする。

using System.Dynamic;

// フレームワーク部

public static class ObjectExtensions
{
    // オブジェクトの指定された名前のプロパティの値を取得
    public static object Eval(this object item, string propertyName, out bool isSucceeded)
    {
        var propertyInfo = item.GetType().GetProperty(propertyName);
        if (propertyInfo == null) {
            isSucceeded = false;
            return null;
        }
        isSucceeded = true;
        return propertyInfo.GetValue(item, null);
    }

    // オブジェクトの指定された名前のプロパティの値を設定
    public static void SetPropertyValue(this object item, string propertyName, object value)
    {
        var propertyInfo = item.GetType().GetProperty(propertyName);
        if (propertyInfo != null)
            propertyInfo.SetValue(item, value, null);
    }

    // DynamicObject の指定された名前のプロパティの値を取得
    public static object GetPropertyValue(this DynamicObject item, string propertyName)
    {
        object result;
        return item.TryGetMember(new MyGetMemberBinder(propertyName), out result) ? result : null;
    }
    
    // GetPropertyValue 用
    class MyGetMemberBinder : GetMemberBinder
    {
        public MyGetMemberBinder(string name) : base(name, false)
        { }

        public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion)
        { return null; }
    }
}

object に指定された名前のプロパティの値を設定する拡張メソッドや、DynamicObject から指定された名前のプロパティの値を取得する拡張メソッドを追加した。

・フレームワーク部の実装 - DynamicContainer

DynamicContainer は、「DynamicObject を使ってみよう」に出てきたのと同様、DynamicObject の派生クラスだ。

あの時と同じように、TrySetMember と TryGetMember をオーバーライドする。

あの時との違いは、target としてのオブジェクトを持ち、値を設定したり取得したりする際はそちらに行うことだ。

従って、Dictionary の中には値を保持する必要がない。

最初に target を受け取った際に、その target の全プロパティの型情報のみ格納しておく
(本当は、各プロパティが public な set と public な get の両方を持つかどうかの情報も保持した方が良いが、今回は省略)。

using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Reflection;

// フレームワーク部

// 任意のオブジェクトを格納してプロパティの値の変化を監視
class DynamicContainer : DynamicObject
{
    // 対象とするアイテム
    object target;
    // observableTarget の該当するプロパティ名毎に型を格納
    Dictionary<string, Type> types = new Dictionary<string, Type>();

    public DynamicContainer(object target)
    {
        this.target = target;
        SetProperties(target.GetType()); // 対象とする型の全プロパティを格納
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        Type type;
        // 該当するプロパティの型を values から取得
        if (types.TryGetValue(binder.Name, out type)) {
            var valueType = value.GetType();
            // もしプロパティに設定しようとしている値の型が、そのプロパティの型もしくはそのサブクラスだったら
            if (valueType.Equals(type) || valueType.IsSubclassOf(type)) {
                // target のプロパティに値を設定
                target.SetPropertyValue(binder.Name, value);
                return true;
            }
        }
        return false;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        // target のプロパティから値を取得
        bool isSucceeded;
        result = target.Eval(binder.Name, out isSucceeded);
        return isSucceeded;
    }

    // 対象とする型の全プロパティを格納
    void SetProperties(Type type)
    {
        type.GetProperties().ToList().ForEach(SetProperty);
    }

    // 対象とするアイテムのプロパティを格納
    void SetProperty(PropertyInfo propertyInfo)
    {
        types[propertyInfo.Name] = propertyInfo.PropertyType;
    }
}
・フレームワーク部の実装 - DynamicObservable

次に、DynamicContainer を継承して DynamicObservable を作る。

前回迄の Observable にあたるクラスだ。

Observable と異なり、抽象クラスではない。

これまでは、イベントを起こす為のメソッド RaiseUpdate をアプリケーション側のモデルで、変更されるプロパティ毎に呼ぶ必要があった。

この DynamicObservable では、TrySetMember をオーバーライドし、もし値が更新された場合は、自ら Update イベントを起こす。

これにより、アプリケーション部のモデルがシンプルになる筈だ。

using System;
using System.Dynamic;

// フレームワーク部

class DynamicObservable : DynamicContainer // 更新を監視される側
{
    public event Action<string> Update;

    public DynamicObservable(object target) : base(target)
    { }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        object oldValue = this.GetPropertyValue(binder.Name);
        if (base.TrySetMember(binder, value)) {
            if (!value.Equals(oldValue))
                RaiseUpdate(binder.Name);
            return true;
        }
        return false;
    }

    void RaiseUpdate(string propertyName)
    {
        if (Update != null)
            Update(propertyName);
    }
}
・フレームワーク部の実装 - DynamicObserver

フレームワーク部の実装の最後は、DynamicObserver だ。

前回迄の Observer にあたるクラスだ。

このクラスは、DataSource が Observable から DynamicObservable に変化した以外は、変更はない。

using System;
using System.Collections.Generic;

// フレームワーク部

abstract class DynamicObserver // 更新を監視する側
{
    Dictionary<string, Action<object>> updateExpressions = new Dictionary<string, Action<object>>();
    DynamicObservable dataSource = null;

    public DynamicObservable DataSource
    {
        set {
            dataSource = value;
            value.Update += Update;
        }
    }

    protected void AddUpdateAction(string propertyName, Action<object> updateAction)
    {
        updateExpressions[propertyName] = updateAction;
    }

    void Update(string propertyName)
    {
        Action<object> updateAction;
        if (updateExpressions.TryGetValue(propertyName, out updateAction))
            updateAction(dataSource.GetPropertyValue(propertyName));
    }
}
・アプリケーション部の実装

次に、アプリケーション部だ。
どんな風にシンプルになっただろう。

・アプリケーション部の実装 - Employee

Employee は、こうなる。

プロパティが設定される毎に Update イベントを起こさなくて良くなったため、随分シンプルだ。

特定のクラスからの派生が不要な POCO (Plain Old CLR Object) になった。

// アプリケーション部

// Model
class Employee
{
    public int Number { get; set; }
    public string Name { get; set; }
}
・アプリケーション部の実装 - EmployeeView

EmployeeView は、こうだ。

第三回から全く変化していない。

using System;

// アプリケーション部

class TextControl // テキスト表示用のUI部品 (ダミー)
{
    string text = string.Empty;

    public string Text
    {
        set { text = value; Console.WriteLine("TextControl is updated: {0}", value); }
        get { return text; }
    }
}

class EmployeeView : DynamicObserver // Employee 用の View
{
    TextControl numberTextControl = new TextControl(); // Number 表示用
    TextControl nameTextControl = new TextControl(); // Name 表示用

    public EmployeeView()
    {
        AddUpdateAction("Number", number => numberTextControl.Text = number.ToString());
        AddUpdateAction("Name", name => nameTextControl.Text = (string)name);
    }
}
・アプリケーション部の実装 - Main を含んだクラス Program

Main を含んだクラス Program では、少し変更がある。

EmployeeView のデータソースに DynamicObservable を用いるようにする。

using System;

// アプリケーション部

class Program
{
    static void Main()
    {
        var employee = new Employee(); // Model
        dynamic observableEmployee = new DynamicObservable(employee); // Observable
        var employeeView = new EmployeeView(); // View, Observer

        employeeView.DataSource = observableEmployee; // データバインド
        observableEmployee.Number = 100; // Number を変更。employeeView に反映されたかな?
        observableEmployee.Name = "福井太郎"; // Name を変更。employeeView に反映されたかな?
    }
}
・実行結果

勿論、実行結果に変わりはない。

TextControl is updated: 100
TextControl is updated: 福井太郎

矢張り、Number と Name それぞれの更新で、それぞれの TextControl が更新されている。

今回は、もう少し念入りにチェックしてみよう。

using System;

// アプリケーション部

class Program
{
    static void Main()
    {
        var employee = new Employee(); // Model
        dynamic observableEmployee = new DynamicObservable(employee); // Observable
        var employeeView = new EmployeeView(); // View, Observer

        employeeView.DataSource = observableEmployee; // データバインド

        observableEmployee.Number = 100; // Number を変更。employeeView に反映されたかな?
        observableEmployee.Number = 200; // Number を再度変更。employeeView に反映されたかな?

        observableEmployee.Name = "福井太郎"; // Name を変更。employeeView に反映されたかな?
        observableEmployee.Name = "山田次郎"; // Name を再度変更。employeeView に反映されたかな?

        observableEmployee.Number = 200; // Number を変更していない。employeeView は更新されない筈
        observableEmployee.Name = "山田次郎"; // Name を変更していない。employeeView は更新されない筈

        // 念の為値を確認
        Console.WriteLine("employee - Number:{0}, Name:{1}", employee.Number, employee.Name);
        Console.WriteLine("observableEmployee - Number:{0}, Name:{1}", observableEmployee.Number, observableEmployee.Name);
    }
}
・実行結果

実行結果は予想通り。
この範囲ではうまく行っているようだ。

TextControl is updated: 100
TextControl is updated: 200
TextControl is updated: 福井太郎
TextControl is updated: 山田次郎
employee - Number:200, Name:山田次郎
observableEmployee - Number:200, Name:山田次郎

■ 今回のまとめ

今回は、DynamicObject を使ったアプローチを行った。

モデル部分がシンプルに書けるようになった。

2012年12月27日

[Event][Windows 8][Windows ストア アプリ] VSUG DAY 2012 Winter 「Windows 8 ストア アプリ 開発 Tips」の資料公開

VSUG DAY 2012 Winter 2012.12.15(Sat) マイクロソフト品川ビル31F

2012/12/15(土) VSUG DAY 2012 Winter の時の資料を公開します。

■ 関連リンク

2012年12月28日

[Event][Windows 8][Windows ストア アプリ] 「Community Open Day 2012 北陸」 (6月9日) での「JavaScript+HTML5 と C#+XAML で作る Windows8 アプリ」資料公開

大分前のものだが、「Community Open Day 2012 北陸」 (6月9日, 石川工業高等専門学校) での「JavaScript+HTML5 と C#+XAML で作る Windows8 アプリ」も公開。

■ 関連リンク

2013年02月04日

[WPF][Windows ストア アプリ][Windows Phone][Design Pattern] MVVM パターンを理解するための参考記事

WPF (や Silverlight、XAML による Windows ストア アプリ、Windows Phone アプリ) での View 周りの設計方法には、大きく分けて次の二種類がある。

  • コードビハインド
    • XxxWindow.xaml - XAML で View を宣言的に書く 。
    • XxxWindow.xaml.cs - コードビハインド。View のロジック部分を書く。イベント ハンドラー等。
  • MVVM (Model-View-ViewModel) パターン
    • XxxWindow.xaml - XAML で View を宣言的に書く 。ViewModel とはデータバインドで結び付ける。
    •  XxxViewModel.cs - View の状態や View のロジック部分のモデル。View のロジック。

このうち、MVVM パターンについて参考となる記事をご紹介する。

前提知識

MVVM パターンを理解するための参考記事

2013年02月06日

[C#][関数型] カリー化を C# で説明してみる

今回は、軽めの話題として、関数型プログラミングでお馴染みのカリー化を C# でやってみよう。

■ 関数の作成

先ず、以下のように関数を用意してみる。

// 2つの数の足し算 (引数は2つ)
Func<double, double, double> 足し算 = (x, y) => x + y;

// 2つの数の平均 (引数は2つ)
Func<double, double, double> 平均 = (x, y) => 足し算(x, y) / 2.0;

これらの関数を使ってみると次のようになる。

var 和   = 足し算(1.0, 2.0); // 和の値は 3.0
var 平均値 = 平均(3.0, 4.0); // 平均値は 3.5

■ カリー化

これらは2つの引数を取る関数だ。

このような2つの引数を取る関数は、次のように、1つの引数を取り「1つの引数を取る関数」を返す関数に変換することができる。

Func<double, Func<double, double>> 足し算 = x => (y => x + y);
Func<double, Func<double, double>> 平均 = x => (y => 足し算(x)(y) / 2.0);

このようにして、複数の引数の関数を「1つの引数の関数」で表現することをカリー化という。

カリー化後の関数を使ってみると次のようになる。

var 和 = 足し算(1.0)(2.0); // 和の値は 3.0
var 平均値 = 平均(3.0)(4.0); // 平均値は 3.5

■ 部分適用

カリー化された関数は、引数の一部だけを渡すことで、一部の変数だけが適用された別の関数を作り出すことができる。

これを部分適用と云う。

// 部分適用
Func<double, double> 被加数が1の足し算 = 足し算(1.0);

// 部分適用された関数を使ってみる
var 和 = 被加数が1の足し算(2.0); // 和の値は 3.0

■ 3つ以上の引数の場合の例

3つ以上の引数を取る関数の場合も同様である。

例えば、次のような3つの引数を取る台形の面積を求める関数があってとして、

Func<double, double, double, double> 台形の面積 = (上辺, 下辺, 高さ) => (上辺 + 下辺) * 高さ / 2.0;

// 3つの引数を取る関数を使ってみる
var 面積 = 台形の面積(3.0, 5.0, 2.0); // 面積の値は 8.0

これをカリー化していくと、次のようになる。

Func<double, double, Func<double, double>> 台形の面積 = (上辺, 下辺) => (高さ => (上辺 + 下辺) * 高さ / 2.0);
Func<double, Func<double, Func<double, double>>> 台形の面積 = 上辺 => (下辺 => (高さ => (上辺 + 下辺) * 高さ / 2.0));

つまり、「『関数を返す関数』を返す関数」を返せば良いことになる。

順番に部分適用していくと次のようになる。

Func<double, Func<double, double>> 上辺が3の台形の面積 = 台形の面積(3.0);
Func<double, double> 上辺が3で下辺が5の台形の面積 = 上辺が3の台形の面積(5.0);
var 上辺が3で下辺が5で高さが2の台形の面積 = 上辺が3で下辺が5の台形の面積(2.0);

まとめて書くと次のようになる。

var 面積 = 台形の面積(3.0)(5.0)(2.0); // 面積の値は 8.0

2013年04月04日

[Event] Community Open Day 2013 (2013年5月11日土)

cod2013logo.jpg

今年も全国 7 箇所 (札幌、仙台、名古屋、石川、大阪、広島、沖縄) で一斉に Community Open Day が開催されます。

東京会場はこちらに詳しいスケジュールがあります。

Community Open Day 2013 Supported by Microsoft
日時 2013/05/11(土) 10:00 – 16:50 (9:00 受付開始)
懇親会 17:15 – 18:30
会場 日本マイクロソフト株式会社品川本社 31Fセミナールーム (東京都港区港南2-16-3 品川グランドセントラルタワー)
参加費 無料
主催 マイクロソフト技術コミュニティ supported by Microsoft
詳細/申込み Community Open Day 2013 Supported by Microsoft

北陸会場はこちらです。

Community Open Day 2013 北陸会場
日時 2013/05/11(土) 10:00 - 18:00
会場 石川工業高等専門学校 (石川県河北郡津幡町北中条タ1)
参加費 無料 (懇親会は実費)
主催 Hokuriku.NET福井情報技術者協会[FITEA]
詳細/申込み Community Open Day 2013 北陸会場 : ATND

2013年05月12日

[Event] 『Community Open Day 2013 北陸会場』が開催されました。

cod2013logo.jpg

先週土曜日 (5月11日) に、全国 7 箇所 (札幌、仙台、名古屋、石川、大阪、広島、沖縄) で一斉に Community Open Day 2013 が開催されました。

私は、北陸のエリア リーダーとして参加しました。

聴きに来てくださった皆様、スピーカーの皆様、スタッフの皆様本当にありがとうございました。

『Community Open Day 2013 北陸会場』概要

Community Open Day 2013 北陸会場
日時 2013/05/11(土) 10:00 - 18:00
会場 石川工業高等専門学校 (石川県河北郡津幡町北中条タ1)
主催 Hokuriku.NET福井情報技術者協会[FITEA]
内容
  • オープニング (ライブストリーミングの視聴) 伊藤かつら 氏 高橋忍 氏 砂金 信一郎 氏 日本マイクロソフト 3名
  • 『リモートデスクトップの作り方』 金子 雄一 氏 Remote Desktop Services MVP
  • 『Office 365 チームサイトを美味しく使う10の方法』 中村 和彦 氏 MVP for Office 365
  • 『私がWindows 8を勧める8つの理由』 さくしま たかえ 氏 MVP for Windows Expert-Consumer
  • 『初心者のためのIT講座 ~Office365をつくってみる~』 澤田 賢也 氏 Small Business Server MVP
  • 『C# MVP に聞くC#アレコレ!』 石野 光仁 氏 鈴木 孝明 氏 小島 富治雄 氏 C# MVP 3名
  • 『ライトニング トークス』 石川高専の学生さん
  • プレゼント抽選会
  • 懇親会

7名の Microsoft MVP がスピーカーとして登壇し、全体では 32 名の参加者がありました。

全体の様子 1
全体の様子 その1。
全体の様子 2
全体の様子 その2。
プレゼント
プレゼント抽選会のプレゼント。スポンサーやスピーカーから提供された。ソニーのデジタル カメラ等。
飲食物
主催者から提供された菓子や飲み物。
カレー弁当
昼食は定番のカレー専門店の弁当。とても美味い。
Photo 6
終了後は懇親会も行われた。刺身の盛合せ他、北陸の海の幸。

ライブ コーディング対決! 「C# MVP に聞くC#アレコレ!」の資料公開

私は、鈴木 孝明 氏と共に石野 光仁 氏のセッション「C# MVP に聞くC#アレコレ!」に登壇しました。

両氏と相談して、「折角北信越の C# MVP 3名が集まるのだから、一緒に C# の魅力を語ろう!」ということで、石野さんに企画していただいたものです。

ライブ コーディング対決を行いましたが、お題は本番になるまで本当に何も知らされず、鈴木さんと私はぶっつけ本番でした。

それだけに、ライブ感が出たのではないかと思います。私にとって、とても楽しいセッションでした。

私の分の「C#の好きなところ」の資料を公開します。

関連リンク

2013年05月14日

[C#] 匿名型でのプロパティ名の省略

今回は、ごく軽く余り役に立たない話題。C# の匿名型に関して。

■ 匿名型

匿名型は、以下のようなものだ。

    var 本1 = new { タイトル = "C#入門", 価格 = 3800 };

これで、"本1" は次のような無名のクラスの参照変数となる、と云うものだ。

// 疑似コード (コンパイルできない)
class <>f__AnonymousType1`2 // 内部的なクラス名
{
    public string タイトル { get; set; }
    public int    価格     { get; set; }
}

■ 匿名型のプロパティ名の省略

名前が付いている場合、匿名型のプロパティ名は省略できる。

例えば、変数を使用した場合、以下のように書ける:

    var タイトル = "C#入門";
    var 価格     = 3800;
    var 本2      = new { タイトル, 価格 };

これで、"本2" は、先のクラスと同様の匿名クラスの参照変数となる。

まあ、これは大して役に立たないだろう。変数名とプロパティ名では、通常、命名規則が異なる (CamelCase と PascalCase が普通) からだ。

プロパティ名を使うこともできる:

    var 出力用本 = new { 本1.タイトル, 価格 = 本1.価格.ToString("C") };

"出力用本" は、次の2つのプロパティを持つ匿名クラスの参照変数だ。

    public string タイトル { get; set; }
    public string 価格     { get; set; }

こちらは、「同じプロパティ名としたい (先のプロパティ名が変更になったら変更になるようにしたい)」と云う場合等に使えるかもしれない。

2013年06月12日

[C#][dynamic] 列挙型 (enum) の列挙子の動的な取得など

今回は、列挙型 (enum) の列挙子の取得などについて。

■ 列挙型 (enum) の列挙子を動的なメソッド呼び出しの引数で渡したい

例えば、次のようなクラス ライブラリー (ClassLibrary.dll) があるとする。

// クラス ライブラリー側: ClassLibrary.dll
namespace ClassLibrary
{
    // 列挙型 (enum)
    public enum ItemKind
    {
        Book, Toy, Stationary
    }

    // クラス
    public class Item
    {
        // アイテムの種類 (型は列挙型の ItemKind)
        readonly ItemKind itemType;

        // コンストラクター (引数でアイテムの種類を指定)
        public Item(ItemKind itemType)
        { this.itemType = itemType; }
    }
}

そして、別のプロジェクトで、このクラス ライブラリーを動的に読み込んで使いたいとする。

例.

// クラス ライブラリーを動的に利用する側: Xxx.exe
using System;
using System.Reflection;

class Program
{
    static void Main()
    {
        // アセンブリ名を使ってクラス ライブラリーを動的に読み込み
        var assembly = Assembly.Load("ClassLibrary");
        // アセンブリ内のクラスの型情報を取得
        var itemType = assembly.GetType("ClassLibrary.Item");

        // ... 途中省略 ...
    }
}

ここで次のように、型情報 itemType を使って、インスタンスを生成させようとしてみる。

        // ... 途中省略 ...

        // アセンブリ名を使ってクラス ライブラリーを動的に読み込み
        var assembly = Assembly.Load("ClassLibrary");
        // アセンブリ内のクラスの型情報を取得
        var itemType = assembly.GetType("ClassLibrary.Item");

        // クラスの型情報を使って、クラスのインスタンスを生成
        dynamic item = Activator.CreateInstance(itemType, 0 /* ClassLibrary.ItemKind.Book の値 */);

        // ... 途中省略 ...

これは次のように、実行時エラーになる。
コンストラクターの引数の型が合っていないからだ。

実行時エラー その1

勿論、object にキャストしても実行時エラーの儘だ。

実行時エラー その2

だが、クラス ライブラリーを動的に読み込みたい訳なので、ClassLibrary.ItemKind.Book と書く訳にもいかない。

        // ... 途中省略 ...

        // クラスの型情報を使って、クラスのインスタンスを生成
        dynamic item = Activator.CreateInstance(itemType, ClassLibrary.ItemKind.Book); // コンパイル エラー

        // ... 途中省略 ...

こういう場合はどうすれば良いだろうか?

これは、次のように、列挙型 (enum) の型情報も取得して、そこから ClassLibrary.ItemKind.Book を生成してやれば良い。

        // ... 途中省略 ...

        // アセンブリ内の列挙型 (enum) の型情報をアセンブリから取得
        var itemKindType     = assembly.GetType("ClassLibrary.ItemKind");
        // enum の列挙オブジェクト ClassLibrary.ItemKind.Book を生成
        var itemKindTypeBook = Enum.Parse(itemKindType, "Book");

        // クラスの型情報を使って、クラスのインスタンスを生成
        dynamic item = Activator.CreateInstance(itemType, itemKindTypeBook);

        // ... 途中省略 ...

Enum.Parse メソッドを使って enum の列挙子名から列挙オブジェクトを生成するのがポイントだ。

■ 列挙型 (enum) の列挙子の名称を取得

列挙型 (enum) の型情報から全ての列挙子の名称を取得することもできる。

例えば、列挙型 (enum) ClassLibrary.ItemKind の全ての列挙子の名称を取得するには次のようにすれば良い。

        // ... 途中省略 ...

        // 列挙型 (enum) の全ての列挙子の名称を取得
        string[] itemKindTypeNames = Enum.GetNames(typeof(ClassLibrary.ItemKind));

        // ... 途中省略 ...

列挙子の名称が配列で得られる。即ち、ここから列挙子の数を取得することができることになる。

        // ... 途中省略 ...

        // 列挙型 (enum) の列挙子の数を取得
        int itemKindTypeLength = Enum.GetNames(typeof(ClassLibrary.ItemKind)).Length;

        // ... 途中省略 ...

Enum.GetTypes メソッドで、全ての列挙子オブジェクトを配列 (Array) で取得することもできるので、次のようにしても良い。

        // ... 途中省略 ...

        // 列挙型 (enum) の列挙子の数を取得
        int itemKindTypeLength = Enum.GetTypes(typeof(ClassLibrary.ItemKind)).Length;

        // ... 途中省略 ...

2013年06月13日

[C#][dynamic] 動的にイベント ハンドラーを追加

今回は、動的にイベント ハンドラーの追加を行ってみたい。

■ 動的に読み込まれるクラス ライブラリー側

例えば、次のようなクラス ライブラリー (ClassLibrary.dll) があるとする。

// クラス ライブラリー側: ClassLibrary.dll
namespace ClassLibrary
{
    public class Data : IEnumerable<string>
    {
        // 更新されると起きるイベント
        public event EventHandler Update;

        List<string> itemList = new List<string>();

        // アイテムの追加
        public void Add(string item)
        {
            itemList.Add(item);
            // 更新イベントを起こす
            if (Update != null)
                Update(this, null);
        }

        // IEnumerable<string> の実装
        public IEnumerator<string> GetEnumerator()
        { return itemList.GetEnumerator(); }

        // IEnumerable<string> の実装
        IEnumerator IEnumerable.GetEnumerator()
        { return GetEnumerator(); }
    }
}

クラス Data は、文字列のコンテナー クラスで、文字列が追加 (Add) されると、Update イベントが起きるようになっている。

■ クラス ライブラリーを動的に読み込む側

別のプロジェクトで、このクラス ライブラリーを動的に読み込んで使いたいとする。

こちらでは、先ず先の Data を表示するための View クラスを用意することにする。

・View クラス
using System;
using System.Collections.Generic;

class View
{
    public IEnumerable<string> DataSource { private get; set; }

    // Update のイベント ハンドラー用
    public void OnUpdate(object sender, EventArgs e)
    {
        Show();
    }

    // DataSource の内容を表示
    void Show()
    {
        Console.WriteLine("View:");
        if (DataSource == null)
            return;
        foreach (var item in DataSource)
            Console.WriteLine("\t{0}", item);
    }
}

表示したい IEnumerable<string> な DataSource を予め設定しておくと、OnUpdate が呼ばれたときにそれを表示する、というクラスだ。

・動的にイベント ハンドラーを設定

それでは、クラス ライブラリーを動的に読み込み、その中の Data クラスのインスタンスを生成し、それにイベント ハンドラーを追加してみよう。

クラス ライブラリーを動的に読み込み、その中の Data クラスのインスタンスを生成
using System;
using System.Collections.Generic;
using System.Reflection;

class Program
{
    static void Main()
    {
        // アセンブリ名を使ってクラス ライブラリーを動的に読み込み
        Assembly assembly = Assembly.Load("ClassLibrary");
        // アセンブリ内のクラス Data の型情報を取得
        Type     dataType = assembly.GetType("ClassLibrary.Data");
        // アセンブリ内のクラス Data のインスタンスを生成
        var      data     = Activator.CreateInstance(dataType);
    }
}
続いて、View のインスタンスを生成して DataSource に data を設定
// ...省略...
class Program
{
    static void Main()
    {
        // ...省略...

        // View のインスタンスを生成して DataSource に data を設定
        var view = new View { DataSource = data as IEnumerable<string> };
    }
}
次に、イベント ハンドラー view.OnUpdate からデリゲートを作成し、それを動的にイベント ハンドラーとして追加
// ...省略...
class Program
{
    static void Main()
    {
        // ...省略...

        // Delegate クラスを利用してイベント ハンドラーである view.OnUpdate からデリゲートを作成
        Delegate eventHandlerDelegate = Delegate.CreateDelegate(typeof(EventHandler), view, "OnUpdate");

        // アセンブリ内のクラスの Update イベントの EventInfo を取得
        EventInfo eventInfo = dataType.GetEvent("Update");
        // EventInfo に対してイベント ハンドラーを追加
        eventInfo.AddEventHandler(data, eventHandlerDelegate);
    }
}
・試してみる

では試してみよう。

data に対して、文字列を追加 (Add) する度に view.OnUpdate が呼ばれれば OK だ。

// ...省略...
class Program
{
    static void Main()
    {
        // ...省略...

        // data.Add("Apple"); を動的に呼び出す
        dataType.InvokeMember("Add", BindingFlags.InvokeMethod, null, data, new object[] { "Apple"  });
        // data.Add("Banana"); を動的に呼び出す
        dataType.InvokeMember("Add", BindingFlags.InvokeMethod, null, data, new object[] { "Banana" });
    }
}

結果コンソールには、次のように表示される。

View:
        Apple
View:
        Apple
        Banana

文字列を追加 (Add) する度にイベント ハンドラー view.OnUpdate が呼ばれているのが判る。

2013年06月14日

[C#][ラムダ式][式木] Expression でラムダ式を作成してデリゲートとして実行する

Expression

以前「Expression の構造を調べてみる」と云う記事で、Expression の内部のツリー構造を調べた。

その中で、ラムダ式として足し算を行うだけの (x, y) => x + y と云うシンプルなラムダ式を Expression で受け、その構造を調べた。

using System;
using System.Linq.Expressions;

class Program
{
    static void Main()
    {
        Expression<Func<int, int, int>> expression = (x, y) => x + y;
    }
}

今回は、逆に Expression でラムダ式を作成し、これをデリゲートとして実行してみよう。

■ 今回のサンプルプログラム

今回のサンプルプログラムは次の通りだ。

using System;
using System.Linq.Expressions;

class Program
{
    static void Main()
    {
        // ラムダ式の Parameter の x
        ParameterExpression parameterX = Expression.Parameter(typeof(int), "x");
        // ラムダ式の Parameter の y
        ParameterExpression parameterY = Expression.Parameter(typeof(int), "y");

        // ラムダ式の Body: x + y
        BinaryExpression body = Expression.Add(parameterX, parameterY);

        // ラムダ式: Parameter の x と y、Body で作成
        Expression<Func<int, int, int>> lamda = Expression.Lambda<Func<int, int, int>>(body, parameterX, parameterY);

        // ラムダ式をコンパイルしてデリゲートを生成
        Func<int, int, int> lamdaDelegate = lamda.Compile();

        // 実行してみる: result が 3 になる
        int result = lamdaDelegate(1, 2);
    }
}

このように、Expression でラムダ式をツリー上に組立て、コンパイルすることで、これをデリゲートとして実行することができる。

2013年07月01日

[C#][dynamic] リフレクション Q&A

Dynamic

「Hokuriku.NET C# メタプログラミング ~リフレクション~」に参加してきた。

Hokuriku.NET C# メタプログラミング ~リフレクション~
日時 2013年6月29日
会場 海みらい図書館 (石川県金沢市)
関連記事

その中で話題になったことから、何点かQ&A形式でご紹介したい。

■ Q. ジェネリックの型情報って取れるの?

A. 取れる。

例えば System.Collections.Generic.Dictionary の型情報を取ってみよう。

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        // Dictionary<,> の型情報を取得
        Type typeOfDictionary = typeof(Dictionary<,>);
        Console.WriteLine(typeOfDictionary);
    }
}

実行結果は、次のようになる。

System.Collections.Generic.Dictionary`2[TKey,TValue]

この型情報から、型引数を指定した場合の型情報を得るには、次のように MakeGenericType を使う。

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        // Dictionary<,> の型情報を取得
        Type typeOfDictionary               = typeof(Dictionary<,>);

        // typeOfDictionary に型引数を指定して型情報を取得
        Type typeOfDictionaryOfStringAndInt = typeOfDictionary.MakeGenericType(new Type[] { typeof(string), typeof(int) });
        Console.WriteLine(typeOfDictionaryOfStringAndInt);
    }
}

実行結果:

System.Collections.Generic.Dictionary`2[System.String,System.Int32]

勿論、始めから型引数を指定した場合の型情報と同じになる。

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        // Dictionary<string, int> の型情報を取得
        Type typeOfDictionaryOfStringAndInt = typeof(Dictionary<string, int>);
        Console.WriteLine(typeOfDictionaryOfStringAndInt);
    }
}

実行結果:

System.Collections.Generic.Dictionary`2[System.String,System.Int32]

■ Q. プロパティって内部的にはメソッドなの?

A. その通り。プロパティでもある。

リフレクションを使って、プロパティを持ったクラスのメンバーを調べてみよう。

using System;
using System.Reflection;

// プロパティ Name を持つクラス Person (Object クラスから派生)
class Person // : Object
{
    public string Name { get; set; }
}

class Program
{
    static void Main()
    {
        // Person の型情報を取得
        Type         type        = typeof(Person);

        // Person の型情報から、Person の全メンバーの情報を取得
        MemberInfo[] memberInfos = type.GetMembers();

        // 各メンバー情報の名前と種類を表示
        foreach (MemberInfo member in memberInfos)
            Console.WriteLine("Name: {0}, MemberType: {1}", member.Name, member.MemberType);
    }
}

Type.GetMembers メソッドを使い、全メンバーの情報を取得し、各メンバー情報の名前と種類を表示させてみる。

実行してみると、次のように Person とそのベースクラスである Object の、public なメンバーがリストアップされる:

Name: get_Name, MemberType: Method
Name: set_Name, MemberType: Method
Name: ToString, MemberType: Method
Name: Equals, MemberType: Method
Name: GetHashCode, MemberType: Method
Name: GetType, MemberType: Method
Name: .ctor, MemberType: Constructor
Name: Name, MemberType: Property

プロパティとして Name、そのアクセサー メソッドとして get_Name と set_Name があるのが判る。

次のように、プロパティ Name から、アクセサー メソッドを取り出してみることもできる。

using System;
using System.Reflection;

// プロパティ Name を持つクラス Person
class Person
{
    public string Name { get; set; }
}

class Program
{
    static void Main()
    {
        // Person の型情報を取得
        Type         type         = typeof(Person);

        // Person の型情報から、プロパティ Name の情報を取得
        PropertyInfo propertyInfo = type.GetProperty("Name");
        Console.WriteLine(propertyInfo);

        // プロパティ Name の情報から、アクセサー メソッド (getter と setter) の情報を取得
        MethodInfo[] methodInfos  = propertyInfo.GetAccessors();

        // アクセサー メソッド (getter と setter) の情報を表示
        foreach (MethodInfo methodInfo in methodInfos)
            Console.WriteLine(methodInfo);
    }
}

実行結果:

System.String Name
System.String get_Name()
Void set_Name(System.String)

つまり、プロパティ Name があるときには、内部的にアクセサーとして get_Name と set_Name メソッドを持つことになる。

これは、単に get_Name と set_Name メソッドを持つのとは異なる。Name というプロパティも持つことになる。

class Person
{
    public string Name { get; set; } // プロパティ Name (アクセサーとして get_Name と set_Name メソッドも持つことになる)
}

と異なり、

class Person
{
    public string get_Name(           ) { return null; } 
    public void   set_Name(string name) {              }
}

では、get_Name と set_Name メソッドを持つことは同じだが、Name プロパティを持たない。

■ Q. Name プロパティがあるときに内部的にアクセサーとして get_Name と set_Name メソッドを持つのだとすると、Name プロパティがあるときは、get_Name という名前や set_Name という名前のメソッドは作れない?

A. そう、作れない。コンパイル エラーになる。

次のコードをコンパイルしてみると、コンパイル エラーになる。

class Person
{
    public string Name { get; set; } // プロパティ Name

    public string get_Name(           ) { return null; } 
    public void   set_Name(string name) {              }
}

コンパイル結果:

  • エラー 1 型 'Person' は、'get_Name' と呼ばれるメンバーを同じパラメーターの型で既に予約しています。
  • エラー 2 型 'Person' は、'set_Name' と呼ばれるメンバーを同じパラメーターの型で既に予約しています。

■ Q. プロパティが内部的にメソッドだとすると、リフレクションでその内部的なメソッドを実行してもプロパティの値の設定/取得ができる?

A. できる。

先ずは、リフレクションを使って、プロパティに値を設定/取得してみる。

using System;
using System.Reflection;

// プロパティ Name を持つクラス Person
class Person
{
    public string Name { get; set; }
}

class Program
{
    static void Main()
    {
        // Person の型情報を取得
        Type         type         = typeof(Person);

        var          person       = new Person();
        PropertyInfo propertyInfo = type.GetProperty("Name");

        // リフレクションを使って、person の Name の値を設定
        propertyInfo.SetValue(person, "明智光秀", null); // .NET 4.5 未満の場合
        // .NET 4.5 以上の場合は propertyInfo.SetValue(person, "明智光秀"); でも良い

        // リフレクションを使って、person の Name の値を取得
        string       name         = (string)propertyInfo.GetValue(person, null); // .NET 4.5 未満の場合
        // .NET 4.5 以上の場合は string name = (string)propertyInfo.GetValue(person); でも良い

        Console.WriteLine(name);
    }
}

実行結果:

明智光秀

次に、リフレクションを使い、内部的な set_Name メソッドで値を設定し、Name プロパティで値を取得してみる。

using System;
using System.Reflection;

// プロパティ Name を持つクラス Person
class Person
{
    public string Name { get; set; }
}

class Program
{
    static void Main()
    {
        // Person の型情報を取得
        Type         type         = typeof(Person);

        var          person       = new Person();

        // リフレクションを使って、person の set_Name を呼び出す
        MethodInfo   methodInfo   = type.GetMethod("set_Name");
        methodInfo.Invoke(person, new object[] { "織田信長" });

        // リフレクションを使って、person の Name の値を取得
        PropertyInfo propertyInfo = type.GetProperty("Name");
        string       name         = (string)propertyInfo.GetValue(person, null); // .NET 4.5 未満の場合
        // .NET 4.5 以上の場合は string name = (string)propertyInfo.GetValue(person); でも良い

        Console.WriteLine(name);
    }
}

実行結果:

織田信長

このように、プロパティの内部的なメソッドをリフレクションを使って実行することで、プロパティの値の設定/取得ができる。

勿論、リフレクションを使わずに、直接 set_Name や get_Name を呼び出すことはできない。

using System;
using System.Reflection;

// プロパティ Name を持つクラス Person
class Person
{
    public string Name { get; set; }
}

class Program
{
    static void Main()
    {
        var person = new Person();
        person.set_Name("石田光成"); // コンパイル エラーになる
    }
}

コンパイル結果:

  • エラー 1 'Person.Name.set': 演算子またはアクセサーを明示的に呼び出すことはできません。

■ Q. リフレクションで static なメンバーや private なメンバーにもアクセスできる?

A. できる。

次の static メンバーや private メンバーを持つクラス Singleton で、試してみよう。

using System;
using System.Reflection;

// static メンバーや private メンバーを持つクラス
class Singleton
{
    private static Singleton instance = new Singleton();

    public static Singleton GetInstance()
    { return instance; }

    private Singleton()
    {}
}

class Program
{
    static void Main()
    {
        // Singleton の型情報を取得
        Type            type            = typeof(Singleton);

        // BindingFlags.NonPublic | BindingFlags.Instance を指定することで、public でも static でもないコンストラクターの情報を取得
        ConstructorInfo constructorInfo = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { }, null);

        // リフレクションを使うことで、private なコンストラクターが実行できる
        Singleton       singleton       = (Singleton)constructorInfo.Invoke(null);

        // BindingFlags.NonPublic | BindingFlags.Static を指定することで、public でなく static なフィールドの情報を取得
        FieldInfo       fieldInfo       = type.GetField("instance", BindingFlags.NonPublic | BindingFlags.Static);

        // リフレクションを使うことで、private で static なフィールドにアクセスできる
        fieldInfo.SetValue(null, singleton);
    }
}

■ Q. const なフィールドって static なフィールドとしてリフレクションでアクセスできる? まさかと思うけど値を書き換えられる?

A. const なフィールドは static なフィールドとしてリフレクションで値を取得できるが、書き換えられる訳ではない。

試してみよう:

using System;
using System.Reflection;

class ConstantSample
{
    public const double PI = 3.14159265358979 ; // const なフィールド
}

class Program
{
    static void Main()
    {
        // Constant の型情報を取得
        Type      type        = typeof(ConstantSample);

        // Constant の型情報から、const なフィールド PI の情報を取得
        FieldInfo piFieldInfo = type.GetField("PI");

        // インスタンスを指定せずに static フィールドとして値を取得してみる
        var       pi          = (double)piFieldInfo.GetValue(null);
        Console.WriteLine(pi);

        // インスタンスを指定せずに static フィールドとして値を設定してみる
        piFieldInfo.SetValue(null, 3.0);
    }
}

実行してみると、次のように、PI の値を取得することはできるが、設定ではエラーになる:

3.14159265358979

ハンドルされていない例外: System.FieldAccessException: 定数フィールドを設定できません。

■ Q. じゃあ readonly なフィールドは?

A. readonly なフィールドの場合は、const なフィールドの場合と異なり、static としてなければインスタンス フィールドになる。また、値を取得するだけでなく設定することもできる。

readonly なフィールドの場合について試してみよう。

using System;
using System.Reflection;

class ReadonlySample
{
    public readonly int DayOfYear = DateTime.Now.DayOfYear; // readonly なフィールド
}

class Program
{
    static void Main()
    {
        // Readonly の型情報を取得
        Type      type               = typeof(ReadonlySample);

        // Readonly の型情報から、readonly なフィールド DayOfYear の情報を取得
        FieldInfo dayOfYearFieldInfo = type.GetField("DayOfYear");

        // インスタンスを指定せずに static フィールドとして値を取得してみると、これは実行時エラーとなり取得できない
        //var     dayOfYear          = (int)dayOfYearFieldInfo.GetValue(null); // 実行時エラー

        // Readonly のインスタンスを生成
        var       readonlySample     = new ReadonlySample();
        // インスタンスを指定してインスタンス フィールドとして値を取得してみる
        var       dayOfYear          = (int)dayOfYearFieldInfo.GetValue(readonlySample);
        Console.WriteLine(dayOfYear);

        // インスタンスを指定してインスタンス フィールドとして値を設定してみる
        dayOfYearFieldInfo.SetValue(readonlySample, 0);
        // 設定されたか確認
        Console.WriteLine(readonlySample.DayOfYear); 
    }
}

実行結果は次の通り:

182
0

取得だけでなく、設定も可能だ。

2013年07月02日

Microsoft MVP for Visual C# を再受賞しました

IMG_3513.JPG

Microsoft MVP (Most Valuable Professional) for Visual C# を再受賞しました。2005年7月からの受賞で、9年目になります。

ここでも述べましたが、オンライン・オフラインで多くの素晴らしいエンジニアの皆様と出会えたおかげです。感謝です。

今後ともどうぞよろしくお願いいたします。

2013年07月23日

こみゅぷらす Tech Aid 2013

こみゅぷらす Tech Aid 2013
『こみゅぷらす Tech Aid 2013』
こみゅぷらすの飲み放題イベント2013!!
日時 2013年7月27日(土)
11時~18時
会場 新宿居酒屋
参加費 3100円 (ランチ、飲み放題付き)
内容/申込み http://comuplus.net/CLT2013/

Hokuriku.NET Vol.12

Hokuriku.NET
『Hokuriku.NET Vol.12』
~今年の夏も福井で~
日時 2013年8月3日(土)
10時30分~18時
会場 福井市地域交流プラザ 研修室603 (AOSSA 6階) (福井県福井市手寄1-4-1)
参加費 500円 (会場費)
内容/申込み http://atnd.org/events/40509

2013年07月25日

[C#][Windows ストア アプリ][WPF] INotifyPropertyChanged の実装に便利なクラスとコードスニペット

WPF や Silverlight、Windows 8 や Windows RT の Windows ストア アプリでは、UI の記述に XAML を使うことが多い。

そして、データバインドするために INotifyPropertyChanged をしょっちゅう実装することになる。

これが結構面倒なので、開発者は普通、少しでも楽になるような工夫をしている。

そのような「有り勝ちな工夫」について。

■ 工夫する前のコード

「工夫」する前のコードは、こんな感じだ。

using System.ComponentModel;

class MyViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    int id = 0;

    public int Id
    {
        get { return id; }
        set {
            if (value != id) {
                id = value;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("Id"));
            }
        }
    }

    string name = string.Empty;

    public string Name
    {
        get { return name; }
        set {
            if (value != name) {
                name = value;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("Name"));
            }
        }
    }
}

プロパティ毎の記述が、結構煩雑だ。

上記ではプロパティが2つしかないが、プロパティが多くなれば、この繰り返しはそれに比例して増えることになる。

■ ヘルパー クラス

Expression や Caller Info を使ったヘルパー クラスを用意するともう少し記述が楽になる。

※ 参考:

こんな感じのヘルパー クラスだ。

using System;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;

public static class PropertyChangedEventHandlerExtensions
{
    // Caller Info を使っているので C# 5.0 以降
    public static void Raise(this PropertyChangedEventHandler onPropertyChanged, object sender, [CallerMemberName] string propertyName = "")
    {
        if (onPropertyChanged != null)
            onPropertyChanged(sender, new PropertyChangedEventArgs(propertyName));
    }

    public static void Raise<PropertyType>(this PropertyChangedEventHandler onPropertyChanged, object sender, Expression<Func<PropertyType>> propertyExpression)
    { onPropertyChanged.Raise(sender, propertyExpression.GetMemberName()); }

    static string GetMemberName<MemberType>(this Expression<Func<MemberType>> expression)
    { return ((MemberExpression)expression.Body).Member.Name; }
}

これを使うことで、先のコードは「少しだけ」簡潔になる。また、プロパティ名の変更に対して安全になった。

using System.ComponentModel;

class MyViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    int id = 0;

    public int Id
    {
        get { return id; }
        set {
            if (value != id) {
                id = value;
                PropertyChanged.Raise(this);
            }
        }
    }

    string name = string.Empty;

    public string Name
    {
        get { return name; }
        set {
            if (value != name) {
                name = value;
                PropertyChanged.Raise(this);
            }
        }
    }
}

■ コード スニペット

更に Visual Studio でコード スニペットを利用することで、タイピングの手間を減らすことができる。

このコード スニペットは、例えば次のような XML だ。

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
	<CodeSnippet Format="1.0.0">
	<Header>
		<Title>Property for INotifyPropertyChanged</Title>
		<Shortcut>propin</Shortcut>
		<Description>Code snippet for property for INotifyPropertyChanged.
Use this class (for C# 5.0 or later):
    using System.ComponentModel;
    using System.Runtime.CompilerServices;

    public static class PropertyChangedEventHandlerExtensions 
    {
        public static void Raise(this PropertyChangedEventHandler onPropertyChanged, object sender, [CallerMemberName] string propertyName = "")
        {
            if (onPropertyChanged != null)
                onPropertyChanged(sender, new PropertyChangedEventArgs(propertyName));
        }
    }</Description>
			<Author></Author>
			<SnippetTypes>
				<SnippetType>Expansion</SnippetType>
			</SnippetTypes>
		</Header>
		<Snippet>
			<Declarations>
				<Literal>
					<ID>type</ID>
					<ToolTip>type name</ToolTip>
					<Default>int</Default>
				</Literal>
				<Literal>
					<ID>field</ID>
					<ToolTip>field name</ToolTip>
					<Default>myField</Default>
				</Literal>
				<Literal>
					<ID>property</ID>
					<ToolTip>property name</ToolTip>
					<Default>MyProperty</Default>
				</Literal>
			</Declarations>
			<Code Language="csharp"><![CDATA[ $type$ $field$ = default($type$);

		public $type$ $property$
		{
			get { return $field$; }
			set {
				if (!value.Equals($field$)) {
					$field$ = value;
					PropertyChanged.Raise(this);
				}
			}
		}
$end$]]></Code>
		</Snippet>
	</CodeSnippet>
</CodeSnippets>

これを、"propin.snippet" のような名前で、テキスト ファイルとして任意の場所に保存する。

次に、Visual Studio で、メニューから 「ツール」 - 「コード スニペット マネージャー」を開く。

コード スニペット マネージャー
コード スニペット マネージャー

「インポート」ボタンを押す。先ほどのテキスト ファイル "propin.snippet" を選択する。

コード スニペットのインポート
コード スニペットのインポート

"My Code Snippets" にチェックをいれて、「完了」ボタンを押す。

これでコード スニペットが使えるようになる。

例えば、先程の MyViewModel クラスの Name プロパティの下にカーソルを合わせて、propin とタイプして[TAB]キーを2回押してみよう。

新しいプロパティが簡単に挿入でき、型名、フィールド名、プロパティ名が楽に設定できるだろう。

コード スニペットによるプロパティの追加
コード スニペットによるプロパティの追加

2013年07月27日

[C#][Roslyn] Roslyn による Visual Studio のアドイン

Roslyn Cafe

※ この内容は、『こみゅぷらす Tech Aid 2013』 (2013-07-27 新宿,東京) にて実際のデモと共に発表予定。

■ Roslyn について

Roslyn は、C# や Visual Basic のコンパイラーの内部の API 等を公開したものだ。"Compiler as a Service" と表現されている。

現在、次の場所や NuGet で入手可能だ。

コンパイラーのコード解析部分等の機能が公開されている。

次にあげる Channel 9 のビデオと資料が参考になるだろう。

Channel 9 にある Roslyn 関連ビデオ (古い順)

Roslyn には次にあげるような機能がある。

Roslyn の機能

  • C# や Visual Basic のソースコード解析機能
  • Visual Studio の拡張機能
  • C#スクリプト機能

今回は、この中の「ソースコード解析機能」を用いて、簡単な Visual Studio のアドインを作ってみたい。

C# のソースコードを解析して、識別子等を数えて、それを WPF で作成したウィンドウに表示させてみよう。

■ 「Visual Studio アドイン」プロジェクトの新規作成

先ず、Visual Studio を用いて、「Visual Studio アドイン」プロジェクトを新規作成する。

「Visual Studio アドイン」プロジェクトの新規作成
「Visual Studio アドイン」プロジェクトの新規作成

次に Roslyn をインストールし、このプロジェクトから参照する。

NuGet を使うことで、簡単にこの作業を行うことができる。

「ソリューション エクスプローラー」でプロジェクト名を右クリックし、「NuGet パッケージの管理...」を選択する。

「NuGet パッケージの管理」ダイアログ ボックスが現れるので、次のように「オンラインの検索」で、"Roslyn" を検索し、"Roslyn" を選択して「インストール」する。

NuGet による Roslyn の追加
NuGet による Roslyn の追加

Roslyn の追加後のプロジェクトの参照設定は、次のようになる。

Roslyn の追加後のプロジェクトの参照設定
Roslyn の追加後のプロジェクトの参照設定

■ ViewModel の作成

今回は、WPF を使ってウィンドウを出すことにする。

そのウィンドウ用に、次のような ViewModel を用意した。

ソースコード内の識別子や using、クラス等の数を保持するクラスだ。

※ 実装の参考:

using System;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;

namespace AddinByRoslyn
{
    public static class PropertyChangedEventHandlerExtensions
    {
        public static void Raise(this PropertyChangedEventHandler onPropertyChanged, object sender, [CallerMemberName] string propertyName = "")
        {
            if (onPropertyChanged != null)
                onPropertyChanged(sender, new PropertyChangedEventArgs(propertyName));
        }

        public static void Raise<PropertyType>(this PropertyChangedEventHandler onPropertyChanged, object sender, Expression<Func<PropertyType>> propertyExpression)
        { onPropertyChanged.Raise(sender, propertyExpression.GetMemberName()); }

        static string GetMemberName<MemberType>(this Expression<Func<MemberType>> expression)
        { return ((MemberExpression)expression.Body).Member.Name; }
    }

    public class ViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        int identifierCount = 0;

        public int IdentifierCount
        {
            get { return identifierCount; }
            set {
                if (value != identifierCount) {
                    identifierCount = value;
                    PropertyChanged.Raise(this);
                    RaiseUpdateData();
                }
            }
        }

        int usingCount = 0;

        public int UsingCount
        {
            get { return usingCount; }
            set {
                if (value != usingCount) {
                    usingCount = value;
                    PropertyChanged.Raise(this);
                    RaiseUpdateData();
                }
            }
        }

        int classCount = 0;

        public int ClassCount
        {
            get { return classCount; }
            set {
                if (value != classCount) {
                    classCount = value;
                    PropertyChanged.Raise(this);
                    RaiseUpdateData();
                }
            }
        }

        int fieldCount = 0;

        public int FieldCount
        {
            get { return fieldCount; }
            set {
                if (value != fieldCount) {
                    fieldCount = value;
                    PropertyChanged.Raise(this);
                    RaiseUpdateData();
                }
            }
        }

        int propertyCount = 0;

        public int PropertyCount
        {
            get { return propertyCount; }
            set {
                if (value != propertyCount) {
                    propertyCount = value;
                    PropertyChanged.Raise(this);
                    RaiseUpdateData();
                }
            }
        }

        int methodCount = 0;

        public int MethodCount
        {
            get { return methodCount; }
            set {
                if (value != methodCount) {
                    methodCount = value;
                    PropertyChanged.Raise(this);
                    RaiseUpdateData();
                }
            }
        }

        int variableCount = 0;

        public int VariableCount
        {
            get { return variableCount; }
            set {
                if (value != variableCount) {
                    variableCount = value;
                    PropertyChanged.Raise(this);
                    RaiseUpdateData();
                }
            }
        }

        int ifCount = 0;

        public int IfCount
        {
            get { return ifCount; }
            set {
                if (value != ifCount) {
                    ifCount = value;
                    PropertyChanged.Raise(this);
                    RaiseUpdateData();
                }
            }
        }

        int lambdaCount = 0;

        public int LambdaCount
        {
            get { return lambdaCount; }
            set {
                if (value != lambdaCount) {
                    lambdaCount = value;
                    PropertyChanged.Raise(this);
                    RaiseUpdateData();
                }
            }
        }

        public object Data
        {
            get {
                return new [] {
                    new { 名称 = "識別子の数" , 結果 = IdentifierCount },
                    new { 名称 = "using の数" , 結果 = UsingCount },
                    new { 名称 = "クラスの数" , 結果 = ClassCount },
                    new { 名称 = "フィールドの数", 結果 = FieldCount },
                    new { 名称 = "プロパティの数", 結果 = PropertyCount },
                    new { 名称 = "メソッドの数" , 結果 = MethodCount },
                    new { 名称 = "変数の数" , 結果 = VariableCount },
                    new { 名称 = "if の数" , 結果 = IfCount },
                    new { 名称 = "ラムダ式の数" , 結果 = LambdaCount }
                };
            }
        }

        string result = string.Empty;

        public string Result
        {
            get { return result; }
            set {
                if (value != result) {
                    result = value;
                    PropertyChanged.Raise(this);
                }
            }
        }

        public void Clear()
        {
            IdentifierCount =
            UsingCount =
            ClassCount =
            FieldCount =
            PropertyCount =
            MethodCount =
            VariableCount =
            IfCount =
            LambdaCount = 0;
            Result = string.Empty;
        }

        void RaiseUpdateData()
        { PropertyChanged.Raise(this, () => Data); }
    }
}

■ Roslyn を用いたコード解析部の作成

では、Roslyn を用いたコード解析部を作成してみよう。

ここでは、Roslyn にある Roslyn.Compilers.CSharp.SyntaxWalker というクラスを継承して SyntaxCounter というクラスを作成する。

この SyntaxWalker クラスは、Visitor パターンになっていて、Visit メソッドを呼ぶことで、全ノードを辿り、ノードの種類毎の virtual メソッドを呼んでくれる。

例えば、識別子のノードの場合には、VisitIdentifierName という virtual メソッドが呼ばれる。

それぞれの virtual メソッドをオーバーライドすることで、そこに処理を書くことができる訳だ。

SyntaxCounter では、オーバーライドしたメソッドのそれぞれで呼ばれた回数を数えることで、ソースコード内の、識別子や using、クラス等の数を知ることにする。

using Roslyn.Compilers.CSharp;
using System.IO;
using System.Text;

namespace AddinByRoslyn
{
    class SyntaxCounter : SyntaxWalker
    {
        readonly ViewModel viewModel;
        StringBuilder stringBuilder; // viewModel.Result 用

        public SyntaxCounter(ViewModel viewModel)
        { this.viewModel = viewModel; }

        // ソース ファイルの中を解析
        public void AnalyzeSourceFile(string sourceFileName)
        {
            using (var reader = new StreamReader(sourceFileName, false)) {
                var sourceCode = reader.ReadToEnd();
                Analyze(sourceCode);
            }
        }

        // ソース コードの中を解析
        void Analyze(string sourceCode)
        {
            // Roslyn.Compilers.CSharp.SyntaxTree クラスによるシンタックス ツリーの取得
            var tree = SyntaxTree.ParseText(sourceCode);
            Analyze(tree.GetRoot()); // シンタックス ツリーのルート要素を解析
        }

        // ルート要素の中を解析
        void Analyze(CompilationUnitSyntax root)
        {
            viewModel.Clear();
            stringBuilder = new StringBuilder();
            Visit(root); // Visit メソッドにより、全ノードを辿る (Visitor パターン)
            viewModel.Result = stringBuilder.ToString();
        }

        // 全ノードについて
        public override void DefaultVisit(SyntaxNode node)
        {
            base.DefaultVisit(node);
            // ノードの情報を stringBuilder に追加
            stringBuilder.AppendLine(string.Format("NodeType: {0}, Node: {1}", node.GetType().Name, node.GetText()));
        }

        // 識別子のノードの場合
        public override void VisitIdentifierName(IdentifierNameSyntax node)
        {
            base.VisitIdentifierName(node);
            viewModel.IdentifierCount++; // 識別子を数える
        }

        // using のノードの場合
        public override void VisitUsingDirective(UsingDirectiveSyntax node)
        {
            base.VisitUsingDirective(node);
            viewModel.UsingCount++; // using を数える
        }

        // クラスのノードの場合
        public override void VisitClassDeclaration(ClassDeclarationSyntax node)
        {
            base.VisitClassDeclaration(node);
            viewModel.ClassCount++; // クラスを数える
        }

        // フィールドのノードの場合
        public override void VisitFieldDeclaration(FieldDeclarationSyntax node)
        {
            base.VisitFieldDeclaration(node);
            viewModel.FieldCount++; // フィールドを数える
        }

        // プロパティのノードの場合
        public override void VisitPropertyDeclaration(PropertyDeclarationSyntax node)
        {
            base.VisitPropertyDeclaration(node);
            viewModel.PropertyCount++; // プロパティを数える
        }

        // メソッドのノードの場合
        public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
        {
            base.VisitMethodDeclaration(node);
            viewModel.MethodCount++; // メソッドを数える
        }

        // 変数のノードの場合
        public override void VisitVariableDeclaration(VariableDeclarationSyntax node)
        {
            base.VisitVariableDeclaration(node);
            viewModel.VariableCount++; // 変数を数える
        }

        // if のノードの場合
        public override void VisitIfStatement(IfStatementSyntax node)
        {
            base.VisitIfStatement(node);
            viewModel.IfCount++; // if を数える
        }

        // シンプルなラムダ式のノードの場合
        public override void VisitSimpleLambdaExpression(SimpleLambdaExpressionSyntax node)
        {
            base.VisitSimpleLambdaExpression(node);
            viewModel.LambdaCount++; // ラムダ式を数える
        }

        // 括弧付きのラムダ式のノードの場合
        public override void VisitParenthesizedLambdaExpression(ParenthesizedLambdaExpressionSyntax node)
        {
            base.VisitParenthesizedLambdaExpression(node);
            viewModel.LambdaCount++; // ラムダ式を数える
        }
    }
}

■ View の作成

次に View を追加する。プロジェクトに "View" という名前の WPF のウィンドウを一つ追加する。

この View は、先の ViewModel を表示するためのウィンドウだ。

プロジェクトに System.Xaml への参照設定の追加が必要になる。

View.xaml

XAML の Grid 内に、ViewModel の "Result" を表示するための TextBox と "Data" を表示するための DataGrid を追加しておく。

<Window x:Class="AddinByRoslyn.View"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
        mc:Ignorable="d" 
        d:DesignHeight="500" d:DesignWidth="500">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <TextBox TextWrapping="Wrap" Text="{Binding Path=Result}" Grid.RowSpan="2" FontFamily="Meiryo" FontSize="14" IsReadOnlyCaretVisible="True" VerticalScrollBarVisibility="Auto" />
        <DataGrid Grid.Row="1" ItemsSource="{Binding Path=Data}" FontFamily="Meiryo" FontSize="14" />
    </Grid>
</Window>
View の「デザイン」
View の「デザイン」

View.xaml.cs

View の C# 部分では、ViewModel のインスタンスを保持し、それを DataContext とする。

また、コンストラクターで C# のソース ファイル名を受け取り、それを上で作成した SyntaxCounter クラスを使って解析する。

using System.Windows;

namespace AddinByRoslyn
{
    public partial class View : Window
    {
        ViewModel viewModel = new ViewModel();

        public View(string sourceFileName)
        {
            InitializeComponent();
            DataContext = viewModel; // DataContext に ViewModel のインスタンスを設定

            if (!string.IsNullOrWhiteSpace(sourceFileName))
                // SyntaxCounter クラスを用いたソース ファイルの解析
                new SyntaxCounter(viewModel).AnalyzeSourceFile(sourceFileName);
        }
    }
}

■ Visual Studio アドイン部

最後に、Visual Studio アドイン部のコードをカスタマイズする。

カスタマイズするのは、プロジェクトの新規作成時に自動生成された Connect.cs だ。

Exec というメソッドがあるので、この中で View を作成し、ソースコード ファイル名を渡すようにする。

using EnvDTE;
using EnvDTE80;
using Extensibility;
using Microsoft.VisualStudio.CommandBars;
using System;

namespace AddinByRoslyn
{
    public class Connect : IDTExtensibility2, IDTCommandTarget
    {
        DTE2 _applicationObject = null;
        AddIn _addInInstance = null;

        public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
        {
            _applicationObject = (DTE2)application;
            _addInInstance = (AddIn)addInInst;
            if(connectMode == ext_ConnectMode.ext_cm_UISetup) {
                var contextGUIDS = new object[] { };
                var commands = (Commands2)_applicationObject.Commands;
                var toolsMenuName = "Tools";
                var menuBarCommandBar = ((CommandBars)_applicationObject.CommandBars)["MenuBar"];
                var toolsControl = menuBarCommandBar.Controls[toolsMenuName];
                var toolsPopup = (CommandBarPopup)toolsControl;

                try {
                    var command = commands.AddNamedCommand2(_addInInstance, "AddinByRoslyn", "AddinByRoslyn", "Executes the command for AddinByRoslyn", true, 59, ref contextGUIDS, (int)vsCommandStatus.vsCommandStatusSupported+(int)vsCommandStatus.vsCommandStatusEnabled, (int)vsCommandStyle.vsCommandStylePictAndText, vsCommandControlType.vsCommandControlTypeButton);
                    if (command != null && toolsPopup != null)
                        command.AddControl(toolsPopup.CommandBar, 1);
                } catch(System.ArgumentException) {
                }
            }
        }

        public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom) {}
        public void OnAddInsUpdate (ref Array custom) {}
        public void OnStartupComplete(ref Array custom) {}
        public void OnBeginShutdown (ref Array custom) {}

        public void QueryStatus(string commandName, vsCommandStatusTextWanted neededText, ref vsCommandStatus status, ref object commandText)
        {
            if (neededText == vsCommandStatusTextWanted.vsCommandStatusTextWantedNone &&
                commandName == "AddinByRoslyn.Connect.AddinByRoslyn")
                status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported|vsCommandStatus.vsCommandStatusEnabled;
        }

        // カスタマイズする Exec メソッド
        public void Exec(string commandName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled)
        {
            if (executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault &&
                commandName == "AddinByRoslyn.Connect.AddinByRoslyn") {
                var view = new View(GetSourceFileName()); // View のインスタンスにソースファイル名を渡す
                view.Show();
                handled = true;
            } else {
                handled = false;
            }
        }

        // ソース ファイル名の取得 (追加する private メソッド)
        string GetSourceFileName()
        {
            var items = _applicationObject.SelectedItems;
            if (items.Count == 0)
                return null;
            var item = items.Item(1);
            return item.ProjectItem.get_FileNames(1);
        }
    }
}

■ 実行

それでは、試してみよう。

Visual Studio から実行してみると、別の Visual Studio が起動する。

新たに起動した Visual Studio で、メニュー から「ツール」 - 「アドイン マネージャー」を開く。

アドイン マネージャー
アドイン マネージャー

作成したアドインが追加できるようになっているのが分かる。

実際に、新たに起動した Visual Studio で今回作成したプロジェクトを開き、ViewModel.cs を開いた状態で、アドインを実行してみよう。

新たに起動した方の Visual Studio で、メニュー から「ツール」 で、新しいアドインを実行する。

アドインの実行
アドインの実行

アドインが起動すると、ウィンドウが開き、結果が表示される。

アドインの実行結果
アドインの実行結果

■ まとめ

今回は、Roslyn を使って単に何種類かの文法要素を数えただけだが、同様の方法で、より複雑な解析を行ったり、一部を変更したりすることもできる。

また、先に述べたように、Roslyn の機能は C# や Visual Basic のソースコードの解析だけではない。他の機能についても、いずれ解説して行きたい。

2013年09月04日

[C#] Windows 及び Internet Explorer のバージョンを調べる

■ 概要

C# で Windows 及び Internet Explorer のバージョンを調べる方法を示す。

■ 解説

・C# での Windows のバージョンの取得方法

Windows のバージョンは、System.Environment.OSVersion.Version で取得できる。

例えば、Windows 8 のバージョンは "6.2.9200.0" ですが、メジャー バージョンである最初の 6 だけを得たい場合は、System.Environment.OSVersion.Version.Major で int として取得できる。

ちなみに、マイナー バージョンの 2 の部分の取得には、System.Environment.OSVersion.Version.Minor を使う。

・C# での Internet Explorer のバージョンの取得方法

Internet Explorer のバージョンはレジストリで確認することができる。

レジストリの HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer の Version の値や svcVersion の値で確認できる (Internet Explorer 9 以前は Version、Internet Explorer 10 は svcVersion)。

下のサンプル コードでは、Registry.LocalMachine.OpenSubKey メソッドでキーをオープンし、キーの GetValue メソッドで値を取得している。

ちなみに、Internet Explorer のバージョンは次のサイトで確認することができる。

例えば、Windows 8 の Internet Explorer 10 では、"10.0.9200.16384" だ。

■ サンプル コード

using Microsoft.Win32; 
using System; 

namespace IEVersionChecker 
{ 
    public static class VersionChecker 
    { 
        public static Version WindowsVersion 
        { 
            get { return Environment.OSVersion.Version; } 
        } 

        public static int WindowsMajorVersion 
        { 
            get { return WindowsVersion.Major; } 
        } 

        public static string InternetExplorerVersion // 取得失敗時は null 
        { 
            get 
            { 
                const string internetExplorerKeyName = @"SOFTWARE\Microsoft\Internet Explorer"; // レジストリのこの場所を確認 
                const string svcVersionValueName = "svcVersion"; // Internet Explorer 10 からはこれの値でチェック 
                const string versionValueName = "Version" ; // Internet Explorer 9 以前はこれの値でチェック 

                try { 
                    using (var key = Registry.LocalMachine.OpenSubKey(internetExplorerKeyName)) { 
                        return (string)(key.GetValue(svcVersionValueName) ?? key.GetValue(versionValueName)); 
                    } 
                } catch (Exception) { 
                    return null; 
                } 
            } 
        } 

        public static int InternetExplorerMajorVersion // 取得失敗時は 0 
        { 
            get 
            { 
                var internetExplorerVersion = InternetExplorerVersion; 
                if (!string.IsNullOrWhiteSpace(internetExplorerVersion)) { 
                    var versionTexts = internetExplorerVersion.Split(new Char[] { '.' }); 
                    if (versionTexts.Length > 0) { 
                        int majorVersion; 
                        if (int.TryParse(versionTexts[0], out majorVersion)) 
                            return majorVersion; 
                    } 
                } 
                return 0; 
            } 
        } 
    } 

    class Program 
    { 
        static void Main() 
        { 
            Console.WriteLine("Windows のバージョンは、{0}。", VersionChecker.WindowsVersion); 
            Console.WriteLine("Windows のメジャー バージョンは、{0}。", VersionChecker.WindowsMajorVersion); 
            Console.WriteLine("Internet Explorer のバージョンは、{0}。", VersionChecker.InternetExplorerVersion); 
            Console.WriteLine("Internet Explorer のメジャー バージョンは、{0}。", VersionChecker.InternetExplorerMajorVersion); 
        } 
    } 
}

■ 実行方法

  1. Windows 上の Visual Studio 2012 以降で「コンソール アプリケーション Visual C#」を新規作成し、Program.cs を上のものに書き換える。
  2. メニューの「デバッグ」-「デバッグなしで開始」を選択するか、Ctrl + F5 キーを押して実行する。
  3.  コマンド プロンプトが起動し、例えば次のように表示される。
Windows のバージョンは、6.2.9200.0。
Windows のメジャー バージョンは、6。
Internet Explorer のバージョンは、10.0.9200.16635。
Internet Explorer のメジャー バージョンは、10。
続行するには何かキーを押してください . . .

2013年09月27日

[C#] 文字が数字かどうかを判定する (char.IsNumber メソッドでローマ数字や漢数字は数字と看做される?)

文字が数字かどうかを判定する場合、System.Char.IsNumber メソッドを使うことができる。

では、ローマ数字などは数字と判定されるのだろうか?

char.IsNumber の場合

今回調べた結果、次のようなテストが通ることが分かった。

using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class CommonTest
{
    [TestMethod]
    public void charのIsNumberのテスト()
    {
              // ※ 一部に機種依存文字があるので注意    

        // 指定した文字が数字だと判断されるケース
        Assert.IsTrue(char.IsNumber('0'));
        Assert.IsTrue(char.IsNumber('1'));
        Assert.IsTrue(char.IsNumber('Ⅲ')); // ローマ数字 (大文字) の 3
        Assert.IsTrue(char.IsNumber('ⅲ')); // ローマ数字 (小文字) の 3
        Assert.IsTrue(char.IsNumber('Ⅰ')); // ローマ数字 (大文字) の 1
        Assert.IsTrue(char.IsNumber('①')); // 丸付きの 1
        Assert.IsTrue(char.IsNumber('2')); // 所謂全角の 2

        // 指定した文字が数字だと判断されないケース
        Assert.IsFalse(char.IsNumber('.')); // 小数点
        Assert.IsFalse(char.IsNumber('-')); // マイナス記号
        Assert.IsFalse(char.IsNumber('I')); // アルファベットの I
        Assert.IsFalse(char.IsNumber('一')); // 漢数字
        Assert.IsFalse(char.IsNumber('四')); // 漢数字
        Assert.IsFalse(char.IsNumber('壱')); // 大字の 1
        Assert.IsFalse(char.IsNumber('壹')); // 大字の 1 (壱の舊字體)
    }
} 

ローマ数字や丸付き数字、所謂全角の数字が数字と見做されていることが分かる。

char.IsDigit の場合

char.IsDigit だとどうだろう。

using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestMethod]
public void charのIsDigitのテスト()
{
    // ※ 一部に機種依存文字があるので注意    

    // 指定した文字が数字だと判断されるケース
    Assert.IsTrue(char.IsDigit('0'));
    Assert.IsTrue(char.IsDigit('1'));
    Assert.IsTrue(char.IsDigit('2')); // 所謂全角の 2

    // 指定した文字が数字だと判断されないケース
    Assert.IsFalse(char.IsDigit('.')); // 小数点
    Assert.IsFalse(char.IsDigit('-')); // マイナス
    Assert.IsFalse(char.IsDigit('①')); // 丸付きの 1
    Assert.IsFalse(char.IsDigit('Ⅲ')); // ローマ数字 (大文字) の 3
    Assert.IsFalse(char.IsDigit('ⅲ')); // ローマ数字 (小文字) の 3
    Assert.IsFalse(char.IsDigit('Ⅰ')); // ローマ数字 (大文字) の 1
    Assert.IsFalse(char.IsDigit('I')); // アルファベットの I
    Assert.IsFalse(char.IsDigit('一')); // 漢数字
    Assert.IsFalse(char.IsDigit('四')); // 漢数字
    Assert.IsFalse(char.IsDigit('壱')); // 大字の 1
    Assert.IsFalse(char.IsDigit('壹')); // 大字の 1 (壱の舊字體)
}

こちらでは、ローマ数字や丸付き数字が数字だと見做されない。

int.TryParse の場合

int.TryParse でもやってみた。

using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestMethod]
public void intのTryParseのテスト()
{
    // ※ 一部に機種依存文字があるので注意    

    int number;

    // 指定した文字が数字だと判断されるケース
    Assert.IsTrue(int.TryParse("0", out number));
    Assert.IsTrue(int.TryParse("1", out number));
    Assert.IsTrue(int.TryParse("2", out number)); // 所謂全角の 2

    // 指定した文字が数字だと判断されないケース
    Assert.IsFalse(int.TryParse(".", out number)); // 小数点
    Assert.IsFalse(int.TryParse("-", out number)); // マイナス
    Assert.IsFalse(int.TryParse("①", out number)); // 丸付きの 1
    Assert.IsFalse(int.TryParse("Ⅲ", out number)); // ローマ数字 (大文字) の 3
    Assert.IsFalse(int.TryParse("ⅲ", out number)); // ローマ数字 (小文字) の 3
    Assert.IsFalse(int.TryParse("Ⅰ", out number)); // ローマ数字 (大文字) の 1
    Assert.IsFalse(int.TryParse("I", out number)); // アルファベットの I
    Assert.IsFalse(int.TryParse("一", out number)); // 漢数字
    Assert.IsFalse(int.TryParse("四", out number)); // 漢数字
    Assert.IsFalse(int.TryParse("壱", out number)); // 大字の 1
    Assert.IsFalse(int.TryParse("壹", out number)); // 大字の 1 (壱の舊字體)
}

こちらは、char.IsDigit と同じ結果となった。

2013年10月03日

[C#][.NET] .NET アプリケーション (WPF/Windows フォーム) で多重起動を禁止し、単一のプロセスで動作させる

■ 概要

.NET アプリケーション (WPF/Windows フォーム) で多重起動を禁止し、単一のプロセスで動作するようにする方法を C# で示す。

■ 解説

WPF アプリケーションや Windows フォーム アプリケーションにおいて、多重起動を禁止してみよう。

ここでは、既にアプリケーションが起動していているときに、そのアプリケーションでドキュメントを開こうとした場合 (*1) に、

  • 他のプロセスが起動していなければ、普通にドキュメントを開く。
  • 他のプロセスが既に起動していれば、その起動中のアプリケーションでドキュメントを開く。

という動作を実現する。

(*1) アプリケーションでドキュメントを開くのには、次のようなケースが考えられる。

  • 実行ファイルに、ドキュメント ファイルをドラッグ&ドロップ
    例. 実行ファイルを a.exe とすると、ファイル エクスプローラーで a.png を a.exe にドラッグ&ドロップ
  • コマンド プロンプトで、コマンドライン引数にドキュメント ファイルを指定して実行ファイルを起動
    例. 実行ファイルのパスを c:\demo\a.exe、ドキュメント ファイル c:\demo\a.png とすると、コマンド プロンプトを開き、c:\demo\a.exe c:\demo\a.png[Enter] と入力
  • ファイルをアプリケーションに関連付けし、開く。

■ 実現方法

すでに起動しているかのチェック方法

ここでは Mutex を用いる。詳細はサンプル コードを参照のこと。

すでに起動しているアプリケーションとの通信方法

ここでは Ipc を用いる。詳細はサンプル コードを参照のこと。

■ サンプル コード

WPF と Windows フォームの両方のサンプルを示そうと思う。

■ 共通部 (WPF、Windows フォーム非依存)

先ずは共通部から。

SingleDocument.cs
// 多重起動を禁止する
// - 他のプロセスが起動していなければ、普通にドキュメントを開く。
// - 他のプロセスが既に起動していれば、その起動中のアプリケーションでドキュメントを開く。
// WPF、Windows フォーム非依存
// 参照設定 System.Runtime.Remoting が必要

using System;
using System.Reflection;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Ipc;
using System.Threading;

namespace SingleDocumentSample
{
    // .NET で多重起動しないアプリケーションを作成するためのクラス (WPF、Windows フォーム非依存)
    public class SingleDocumentHelper
    {
        // 複数のプロセスで一つしかないことを保証する
        class Singleton : IDisposable
        {
            readonly Mutex mutex = null; // 複数のプロセスから参照するために Mutex を使用

            public bool IsExist // 既に存在しているかどうか
            {
                get
                {
                    return !mutex.WaitOne(0, false); // Mutex が既に作成されていたら true
                }
            }

            public Singleton(string uniqueName)
            {
                mutex = new Mutex(false, uniqueName); // コンストラクターで、このアプリケーション独自の名前で Mutex を作成
            }

            public void Dispose()
            { mutex.Dispose(); }
        }

        // Ipc によって複数のプロセスで共有するアイテムを格納する
        class IpcRemoteObject : MarshalByRefObject
        {
            public object Item { get; set; }

            // IpcRemoteObject に複数のプロセスで共有するアイテムを格納
            public static void Send(string channelName, string itemName, object item)
            {
                var channel = new IpcClientChannel(); // IPC (プロセス間通信) クライアント チャンネルの生成
                ChannelServices.RegisterChannel(channel, true); // チャンネルを登録
                var remoteObject = Activator.GetObject(typeof(IpcRemoteObject), string.Format("ipc://{0}/{1}", channelName, itemName)) as IpcRemoteObject; // リモートオブジェクトを取得
                if (remoteObject != null)
                    remoteObject.Item = item;
            }
        }

        // 指定された時間毎に Tick イベントが起きるオブジェクトのインタフェイス
        // アプリケーション側で実装する
        public interface ITicker : IDisposable
        {
            event Action Tick;

            void Start();
        }

        // 指定された時間毎に IpcRemoteObject をチェックし、空でなければ Receive イベントが起きる
        class Receiver : IDisposable
        {
            public event Action<object> Receive;

            IpcRemoteObject remoteObject;
            readonly ITicker ticker;

            public Receiver(string uniqueName, string itemName, ITicker ticker)
            {
                Initialize(uniqueName, itemName);
                this.ticker = ticker;
                ticker.Tick += OnTick;
                ticker.Start();
            }

            public void Dispose()
            { ticker.Dispose(); }

            void Initialize(string uniqueName, string itemName)
            {
                var channel = new IpcServerChannel(uniqueName); // このアプリケーション独自の名前で IPC (プロセス間通信) サーバー チャンネルを作成
                ChannelServices.RegisterChannel(channel, true); // チャンネルを登録
                remoteObject = new IpcRemoteObject(); // リモートオブジェクトを生成
                RemotingServices.Marshal(remoteObject, itemName, typeof(IpcRemoteObject)); // リモートオブジェクトを公開
            }

            void OnTick()
            {
                if (remoteObject.Item != null) { // リモートオブジェクトがあれば
                    if (Receive != null)
                        Receive(remoteObject.Item); // Receive イベントを起こす
                    remoteObject.Item = null;
                }
            }
        }

        // 多重起動しないアプリケーション (他のプロセスが起動済みの場合は、その起動済みのプロセスにドキュメントを開かせ、終了する)
        public class Application : IDisposable
        {
            const string itemName = "DocumentPathName";

            Singleton singleton = null;
            Receiver receiver = null;

            // 初期化 (戻り値が偽の場合は終了)
            public bool Initialize(ITicker ticker, string documentPath = "", Action<string> open = null)
            {
                var applicationProductName = ((AssemblyProductAttribute)Attribute.GetCustomAttribute(Assembly.GetExecutingAssembly(), typeof(AssemblyProductAttribute))).Product;

                singleton = new Singleton(applicationProductName);
                if (singleton.IsExist) { // 既に他のプロセスが起動していたら
                    IpcRemoteObject.Send(applicationProductName, itemName, documentPath); // その起動済みのプロセスにドキュメントのパスを送信して
                    return false; // 終了する
                }
                receiver = new Receiver(applicationProductName, itemName, ticker);
                if (open != null) {
                    receiver.Receive += item => open((string)item); // Receive イベントが起きたらアイテムを open するように設定
                    if (!string.IsNullOrWhiteSpace(documentPath)) // documentPath が空でなければ
                        open(documentPath); // documentPath を開く
                }
                return true; // 終了しない
            }

            public void Dispose()
            {
                if (receiver != null) {
                    receiver.Dispose();
                    receiver = null;
                }
                if (singleton != null) {
                    singleton.Dispose();
                    singleton = null;
                }
            }
        }
    }
}
■ WPF の場合

上記 SingleDocumentHelper クラスを WPF で使う場合は次のようになる。

SingleDocumentWpfApplication.cs

App.xaml.cs のアプリケーション クラスのベース クラス。

タイマーに System.Windows.Threading.DispatcherTimer クラスを使う。

using System;
using System.Windows;
using System.Windows.Threading;

namespace SingleDocumentSample.Wpf
{
    // WPF 多重起動防止アプリケーション
    public class SingleDocumentApplication : Application
    {
        // WPF 用の ITicker の実装
        // 指定された時間毎に Tick イベントが起きる
        class Ticker : SingleDocumentHelper.ITicker
        {
            public event Action Tick;

            public const int DefaultInterval = 1000;
            readonly DispatcherTimer timer;

            public Ticker(int interval = DefaultInterval)
            { timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(interval) }; }

            public void Start()
            {
                // DispatcherTimer を使用して定期的に Tick イベントを起こす
                timer.Tick += (sender, e) => RaiseTick();
                timer.Start();
            }

            public void Dispose()
            { timer.Stop(); }

            void RaiseTick()
            {
                if (Tick != null)
                    Tick();
            }
        }

        public event Action<string> OpenDocument;

        readonly SingleDocumentHelper.Application singleDocumentApplication = new SingleDocumentHelper.Application();
        string documentPath = null;

        protected string DocumentPath
        {
            get { return documentPath; }
            set {
                documentPath = value;
                if (OpenDocument != null)
                    OpenDocument(value); // ドキュメントを開く
            }
        }

        public SingleDocumentApplication(Window mainWindow)
        { MainWindow = mainWindow; }

        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            if (e.Args.Length > 0) // コマンドライン引数があれば
                DocumentPath = e.Args[0]; // ドキュメント ファイルのパスとする

            if (singleDocumentApplication.Initialize(new Ticker(), DocumentPath, sourcePath => DocumentPath = sourcePath))
                MainWindow.Show();
            else
                Shutdown(); // 他のプロセスが既に起動していれば終了
        }

        protected override void OnExit(ExitEventArgs e)
        {
            singleDocumentApplication.Dispose();
            base.OnExit(e);
        }
    }
}
App.xaml
<l:SingleDocumentApplication x:Class="SingleDocumentSample.Wpf.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:l="clr-namespace:SingleDocumentSample.Wpf" />
App.xaml.cs

上記 SingleDocumentApplication クラスから派生。

using System.Windows;

namespace SingleDocumentSample.Wpf
{
    partial class App : SingleDocumentApplication
    {
        public App() : base(new MainWindow())
        { OpenDocument += sourcePath => ((MainWindow)MainWindow).DocumentPath = sourcePath; }
    }
}
MainWindow.xaml

WebBrowser コントロールが貼ってあるだけ。

<Window x:Class="SingleDocumentSample.Wpf.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="SingleDocumentSample.Wpf" Height="500" Width="500">
    <Grid>
        <WebBrowser x:Name="browser" />
    </Grid>
</Window>
MainWindow.xaml.cs

指定されたパスや URL を WebBrowser コントロールで表示する。

using System;
using System.Windows;

namespace SingleDocumentSample.Wpf
{
    partial class MainWindow : Window
    {
        public string DocumentPath
        {
            get { return browser.Source == null ? null : browser.Source.AbsolutePath; }
            set {
                browser.Source = new Uri(value, UriKind.RelativeOrAbsolute);
                Title          = value;
                ToTop();
            }
        }

        public MainWindow()
        { InitializeComponent(); }

        // ウィンドウを最前面に表示
        void ToTop()
        {
            if (WindowState == WindowState.Minimized)
                WindowState = WindowState.Normal;
            Activate();
        }
    }
}
■ Windows フォームの場合

そして、SingleDocumentHelper クラスを Windows フォームで使う場合は次のようになる。

SingleDocumentWindowsFormApplication.cs

Main メソッドで使用するためのクラス。

タイマーに System.Windows.Forms.Timer クラスを使う。

using System;
using System.Windows.Forms;

namespace SingleDocumentSample.WinForm
{
    class SingleDocumentApplication
    {
        // ウィンドウズ フォーム用の ITicker の実装
        // 指定された時間毎に Tick イベントが起きる
        class Ticker : SingleDocumentHelper.ITicker
        {
            public event Action Tick;

            public const int DefaultInterval = 1000;
            readonly Timer timer;

            public Ticker(int interval = DefaultInterval)
            { timer = new Timer { Interval = interval }; }

            public void Start()
            {
                // Timer を使用して定期的に Tick イベントを起こす
                timer.Tick += (sender, e) => RaiseTick();
                timer.Start();
            }

            public void Dispose()
            { timer.Dispose(); }

            void RaiseTick()
            {
                if (Tick != null)
                    Tick();
            }
        }

        public event Action<string> OpenDocument;

        string documentPath = null;

        protected string DocumentPath
        {
            get { return documentPath; }
            set {
                documentPath = value;
                if (OpenDocument != null)
                    OpenDocument(value); // ドキュメントを開く
            }
        }

        public bool Run(Form mainForm)
        {
            var commandLineArgs = Environment.GetCommandLineArgs();
            if (commandLineArgs.Length > 1) // コマンドライン引数があれば
                DocumentPath = commandLineArgs[1]; // ドキュメント ファイルのパスとする

            using (var singleDocumentApplication = new SingleDocumentHelper.Application()) {
                if (singleDocumentApplication.Initialize(new Ticker(), DocumentPath, sourcePath => DocumentPath = sourcePath)) {
                    Application.Run(mainForm);
                    return true;
                }
                return false; // 他のプロセスが既に起動していれば終了
            }
        }
    }
}
Program.cs

Main メソッド。

using System;

namespace SingleDocumentSample.WinForm
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            var application = new SingleDocumentApplication();
            var mainForm    = new MainForm();
            application.OpenDocument += documentPath => mainForm.DocumentPath = documentPath;
            application.Run(mainForm);
        }
    }
}
MainForm.cs (MainForm.Designer.cs は不要)

WebBrowser コントロールが貼ってある。

指定されたパスや URL を WebBrowser コントロールで表示する。

using System;
using System.Drawing;
using System.Windows.Forms;

namespace SingleDocumentSample.WinForm
{
    class MainForm : Form
    {
        readonly WebBrowser browser;

        public string DocumentPath
        {
            set {
                browser.Navigate(new Uri(value, UriKind.RelativeOrAbsolute));
                Text = value;
                ToTop();
            }
        }

        public MainForm()
        {
            SuspendLayout();
            ClientSize = new Size(500, 500);
            Text       = "SingleDocumentSampleWinForm";
            browser    = new WebBrowser { Dock = DockStyle.Fill };
            Controls.Add(browser);
            ResumeLayout(false);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
                browser.Dispose();
            base.Dispose(disposing);
        }

        // ウィンドウを最前面に表示
        void ToTop()
        {
            if (WindowState == FormWindowState.Minimized)
                WindowState = FormWindowState.Normal;
            Activate();
        }
    }
}

■ 実行方法

  1. WPF 版または Windows フォーム版をビルドする。
  2. ファイル エクスプローラーで実行ファイル (例えば、SingleDocumentSample.Wpf.exe や SingleDocumentSample.WinForm.exe) のあるフォルダーを開き、実行ファイルのアイコンに画像ファイル (JPEG ファイルや PNG ファイル) 等をドラッグ&ドロップする。 (または、コマンドプロンプトで実行ファイルのバスにコマンドライン引数で画像ファイルのパスやURLを渡す)
  3. サンプル プログラムが起動し、その画像ファイルが表示される。
  4. サンプル プログラムを最小化し、再度ファイル エクスプローラーで別の画像ファイルをドラッグ&ドロップする。 (または、コマンドプロンプトで実行ファイルのバスにコマンドライン引数で別の画像ファイルのパスやURLを渡す)
  5. 今度はプログラムは起動せず、最小化されていた先程のサンプル プログラムが最前面に戻り、新たな画像ファイルが表示される。
実行イメージ
実行イメージ

2013年11月01日

[C#][.NET] メタプログラミング入門 - はじめに

Metasequoia

数回に渡って、C#/.NET によるメタプログラミングを紹介して行きたい。

先ずは概要から。

■ メタプログラミング

・メタとは

メタ (meta) は、「高次な-」「超-」等の意味の接頭語で、ギリシャ語から来ている。

プログラミングでは、メタプログラミングの他、メタクラス (=クラスがインスタンスとなるクラス)、メタデータ (データが持つそのデータ自身についての付加的なデータ) 等で用いられている。

・メタプログラミングとは

メタプログラミングは、「高次なプログラミング」ということで、「プログラムを操作したり出力したりするプログラムを書くこと」だ。

プログラムでプログラムを出力する (メタプログラミング) 方が、手でプログラムを書くよりも効率的な場合がある。

・DRY の原則とメタプログラミング

プログラミングでは、「DRY (Don't repeat yourself) の原則」というものが重視される。「繰り返しを避けるべし」というものだ。

オブジェクト指向プログラミングジェネリック プログラミング等では、抽象化を行うことで、或る程度コードの重複を避けることができる。

だが、「銀の弾などない」という言葉が示すように、オブジェクト指向を採用すれば、或いは、ジェネリック プログラミングを用いれば、それで常にうまく行く、という訳には行かない。何か一つのパラダイムで全てが解決する、というような特効薬などなく、状況に合った幾つかの方法を組み合わせて用いる必要がある。

メタプログラミングもそうしたものの一つだ。

例えば、似たようなプログラムが繰り返し必要な場合に、メタプログラミング、即ち、「プログラムを操作したり出力したりするプログラムを書くこと」で、プログラミングの手書きソースコードの重複や手書き作業の重複を避けられる場合がある。

・メタプログラミングが有効な例

メタプログラミングが有効な例として、次のようなものがある。

  • コンパイラー/インタープリター
    ホスト言語のソースコードから動的に対象言語のプログラムを生成
  • O/R マッパー
    クラスやオブジェクトから動的に SQL を生成
  • XML や JSON の入出力
    クラスやオブジェクト等から動的に XMLJSON を生成/XML や JSON から動的にクラスやオブジェクト等を生成
    (生成するプログラムをプログラムで生成)
  • モック (mock) オブジェクト
    モック (ユニットテストで用いられる代用のオブジェクト) を動的に生成
    (生成するプログラムをプログラムで生成)
  • Web アプリケーション
    クライアント側で動作するプログラム (HTMLJavaScript 等) をサーバー側で動的に生成

■ C#/.NET におけるメタプログラミング

C#/.NET で、「プログラムを操作したり出力したりするプログラムを書く」為には、次のような技術が用意されている。

  • リフレクション
    .NET の System.Reflection 名前空間の中の型を用いて、アセンブリやクラス、クラスのメンバーやインスタンスに関する情報 (メタデータ) を取得したり、メンバーを呼び出したりすることができる。
    リフレクションについては、以前に何度か紹介した。
  • リフレクションの Emit
    System.Reflection.Emit 名前空間の中の型を用いると、CIL (Common Intermediate Language: 共通中間言語) を生成することで、クラスやクラスのメンバーを動的に生成することができる。
  • 式木
    System.Linq.Expressions 名前空間の型を用いて、式木を生成し、動的にプログラムを生成することができる。
  • Roslyn
    以前、「Roslyn による Visual Studio のアドイン」で紹介した Roslyn は、C# や Visual Basic のコンパイラーの内部の API 等を公開したものだ。
    本稿執筆時点では CTP (Community Technology Preview) と呼ばれる評価版だが、これを用いて、C# のソースコードから、プログラムを生成することができる。

これらの他に、Visual StudioT4 (Text Template Transformation Toolkit) Template という機能を使って、コードを生成させる方法もある。

次回から、これらの技術について、紹介して行こうと思う。

2013年11月02日

[C#][.NET] メタプログラミング入門 - Reflection.Emit による Add メソッドの動的生成

Metasequoia

※ 「[C#][.NET] メタプログラミング入門 - はじめに」の続き。

Reflection.Emit によるメタプログラミング

前回は、C#/.NET でメタプログラミングを行う方法について述べた。

これから数回に渡って、それぞれの方法について紹介していきたい。

今回は、Reflection.Emit によるメソッドの動的生成だ。

動的に生成するメソッド

簡単な例として int の足し算を行うだけのメソッドを作ってみたい。

次のようなものだ。

    // 普通の静的な Add メソッド
    static int Add(int x, int y)
    {
        return x + y;
    }

Reflection.Emit では、CIL (Common Intermediate Language: 共通中間言語) を生成して、メソッド等を作成することができる。

先ずは、この Add メソッドの IL (Intermediate Language) がどのようなものかをツールを使って見てみよう。

ILSpy を使って IL を見る

アセンブリの IL は、.NET ReflectorILSpy といったツールを使うことで見ることができる。

ILSpy は、無償で SourceForge.net の ILSpy - SharpDevelop からダウンロードして使うことができる。

例えば、次のようなコンソール アプリをビルドし、出来上がったアセンブリを ILSpy.exe で開いてみよう。

static class Program
{
    // 普通の静的な Add メソッド
    static int Add(int x, int y)
    {
        return x + y;
    }
    
    static void Main()
    {}
}
ILSpy で Add メソッドの IL を見る
ILSpy で Add メソッドの IL を見る

これを Reflection.Emit を用いて生成してみよう。

Reflection.Emit による Add メソッドの動的生成

実際にやってみると次のようになる。

using System;
using System.Reflection;
using System.Reflection.Emit;

static class Program
{
    // Reflection.Emit の DynamicMethod による Add メソッドの生成
    static Func<int, int, int> AddByEmit()
    {
        // DynamicMethod
        var method = new DynamicMethod(
            name          : "add"                            ,
            returnType    : typeof(int)                      ,
            parameterTypes: new[] { typeof(int), typeof(int)}
        );

        // 引数 x 生成用
        var x = method.DefineParameter(position: 1, attributes: ParameterAttributes.In, parameterName: "x");
        // 引数 y 生成用
        var y = method.DefineParameter(position: 2, attributes: ParameterAttributes.In, parameterName: "y");
        // ILGenerator
        var generator = method.GetILGenerator();

        // 生成したい IL
        // IL_0000: ldarg.0
        // IL_0001: ldarg.1
        // IL_0002: add
        // IL_0003: ret

        // 「最初の引数をスタックにプッシュする」コードを生成
        generator.Emit(opcode: OpCodes.Ldarg_0);
        // 「二つ目の引数をスタックにプッシュ」コードを生成
        generator.Emit(opcode: OpCodes.Ldarg_1);
        // 「二つの値を加算する」コードを生成
        generator.Emit(opcode: OpCodes.Add    );
        // 「リターンする」コードを生成
        generator.Emit(opcode: OpCodes.Ret    );

        // 動的にデリゲートを生成
        return (Func<int, int, int>)method.CreateDelegate(delegateType: typeof(Func<int, int, int>));
    }

    static void Main()
    {
        var addByEmit    = AddByEmit();     // デリゲートを動的に生成
        var answerByEmit = addByEmit(1, 2); // 生成したデリゲートの呼び出し
        Console.WriteLine("answerByEmit: {0}", answerByEmit);
    }
}

実行してみると、次のように正しく動作するのが分かるだろう。

answerByEmit: 3

まとめ

今回は、Reflection.Emit を用いて、動的にメソッドを生成するプログラムを作成した。

次回は、他の方法も試してみよう。

2013年11月03日

[C#][.NET][式木] メタプログラミング入門 - 式木による Add メソッドの動的生成

Metasequoia

※ 「[C#][.NET] メタプログラミング入門 - Reflection.Emit による Add メソッドの動的生成」の続き。

式木によるメタプログラミング

前回は、Reflection.Emit を用いて Add メソッドを動的生成するプログラムを作成した。

今回は、式木によるメソッドの動的生成だ。

動的に生成するメソッド

今回も次の Add メソッドを生成する。

    // 普通の静的な Add メソッド
    static int Add(int x, int y)
    {
        return x + y;
    }

前回は、ILSpy で IL を調べ、それを参考にしたた。

今回は式木として生成するため、先ずは Add メソッドにあたる式を作り、その構造を見て参考にしよう。

以前、「Expression の構造を調べてみる」で行ったように、

using System;
using System.Linq.Expressions;

class Program
{
    static void Main()
    {
        Expression<Func<int, int, int>> add = (x, y) => x + y;
    }
}

のようなプログラムを作成し、デバッグ実行で、add の構造を調べてみよう。

Visual Studio のデバッガーの「クイックウォッチ」の表示から一部抜粋

Visual Studio のデバッガーで add 式の構造を見る (一部抜粋)
Visual Studio のデバッガーで add 式の構造を見る (一部抜粋)

これを見ると、次のような構造をしていることが分かる。

add 式の構造
add 式の構造

これを式木を用いて生成してみよう。

式木による Add メソッドの動的生成

実際にやってみると次のようになる。

using System;
using System.Linq.Expressions;

static class Program
{
    // Expression (式) による Add メソッドの生成
    static Func<int, int, int> AddByExpression()
    {
        // 生成したい式
        // (int x, int y) => x + y

        var x      = Expression.Parameter(type: typeof(int)); // 引数 x の式
        var y      = Expression.Parameter(type: typeof(int)); // 引数 y の式
        var add    = Expression.Add      (left: x, right: y); // x + y の式
        var lambda = Expression.Lambda   (add, x, y        ); // (x, y) => x + y の式
        // ラムダ式をコンパイルしてデリゲートとして返す
        return (Func<int, int, int>)lambda.Compile();
    }

    static void Main()
    {
        var addByExpression    = AddByExpression();     // デリゲートを動的に生成
        var answerByExpression = addByExpression(1, 2); // 生成したデリゲートの呼び出し
        Console.WriteLine("answerByExpression: {0}", answerByExpression);
    }
}

Reflection.Emit を使った場合と比較すると、やや簡潔に書けるのが分かるだろう。

実行してみると、次のように正しく動作する。

answerByExpression: 3

まとめ

今回は、式木を用いて、動的にメソッドを生成するプログラムを作成した。

次回は、更に他の方法も試してみよう。

2013年11月04日

[C#][.NET][Roslyn] メタプログラミング入門 - Roslyn による Add メソッドの動的生成

Metasequoia

※ 「[C#][.NET] メタプログラミング入門 - 式木による Add メソッドの動的生成」の続き。

Roslyn によるメタプログラミング

前回は、式木を用いて Add メソッドを動的生成するプログラムを作成した。

今回は、Roslyn によるメソッドの動的生成だ。

Roslyn は、「Roslyn による Visual Studio のアドイン」で紹介したが、C# や Visual Basic のコンパイラーの内部の API 等を公開したものだ。

本稿執筆時点では CTP (Community Technology Preview) と呼ばれる評価版だが、これを用いて、C# のソースコードから、プログラムを生成することができる。

Roslyn のインストール

先ずは、Roslyn をインストールしよう。

Visual Studio の「ソリューション エクスプローラー」でプロジェクト名を右クリックし、「Nuget パッケージの管理...」を選ぶ。

Visual Studio の「Nuget パッケージの管理...」メニュー
Visual Studio の「Nuget パッケージの管理...」メニュー

「Nuget パッケージの管理」ダイアログボックスが開くので、右上の「オンラインの検索」エディット ボックスに「Roslyn」と入力し、検索する。

Visual Studio の「Nuget パッケージの管理」ダイアログボックス
Visual Studio の「Nuget パッケージの管理」ダイアログボックス

暫く待って表示されたリストの中から「Roslyn」を選び、「インストール」する。

動的に生成するメソッド

今回も次の Add メソッドを生成する。

    // 普通の静的な Add メソッド
    static int Add(int x, int y)
    {
        return x + y;
    }

Roslyn を使うソースコードは次のようになる。

using Roslyn.Scripting.CSharp;
using System;

static class Program
{
    // Roslyn による Add メソッドの生成
    static Func<int, int, int> AddByRoslyn()
    {
        var engine  = new ScriptEngine(); // C# のスクリプトエンジン
        var session = engine.CreateSession();
        session.ImportNamespace("System"); // System 名前空間のインポート

        return (Func<int, int, int>)session.Execute(code: "(Func<int, int, int>)((x, y) => x + y)");
    }
    
    static void Main()
    {
        var addByRoslyn    = AddByRoslyn();     // デリゲートを動的に生成
        var answerByRoslyn = addByRoslyn(1, 2); // 生成したメソッドの呼び出し
        Console.WriteLine("answerByRoslyn: {0}", answerByRoslyn);
    }
}

C# のソースコードを直接扱えるので、Reflection.Emit を使った場合式木を使った場合と 比較して、とても簡潔に書ける。

実行してみると、これも次のように正しく動作する。

answerByRoslyn: 3

まとめ

今回は、Roslyn を用いて、動的にメソッドを生成するプログラムを作成した。

これで三通りの方法を試したことになる。次回は、これらのパフォーマンスを比較してみよう。

2013年11月05日

[C#][.NET] メタプログラミング入門 - Add メソッドのパフォーマンスの比較

Metasequoia

※ 「[C#][.NET] メタプログラミング入門 - Roslyn による Add メソッドの動的生成」の続き。

C# によるメタプログラミングでのパフォーマンスの比較

前回まで、C# によるメタプログラミングで Add メソッドを動的生成するプログラムを作成してきた。

今回は、それぞれの手法における実行速度を測ってみよう。

実行に掛かった時間の測定用のクラス

それぞれの実行に掛かった時間を測る為、次のようなクラスを用意することにした。

using System;
using System.Diagnostics;
using System.Linq.Expressions;

public static class パフォーマンステスター
{
    public static void テスト(Expression<Action> 処理式, int 回数, Action<string> output)
    {
        // 処理でなく処理式として受け取っているのは、文字列として出力する為
        var 処理 = 処理式.Compile();
        var 時間 = 計測(処理, 回数).TotalMilliseconds; // 回数分の処理に掛かったミリ秒数
        // 一回当たり何秒掛かったかを出力
        output(string.Format("{0,70}: {1,10:F}/{2} 秒", 処理式.Body.ToString(), 時間, 回数 * 1000));
    }

    static TimeSpan 計測(Action 処理, int 回数)
    {
        var stopwatch = new Stopwatch(); // 時間計測用
        stopwatch.Start();
        回数.回(処理);
        stopwatch.Stop();
        return stopwatch.Elapsed;
    }

    static void 回(this int @this, Action 処理)
    {
        for (var カウンター = 0; カウンター < @this; カウンター++)
            処理();
    }
}

それでは実際に測ってみよう。

デリゲートの動的生成のパフォーマンスのテスト

先ずは、デリゲートの動的生成に掛かる時間。

これまでの三種類のコードを呼び出し、それぞれがデリゲートを作成するまでに掛かる時間を測ってみる。

using Roslyn.Scripting.CSharp;
using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;

static class Program
{
    // Reflection.Emit の DynamicMethod による Add メソッドの生成
    static Func<int, int, int> AddByEmit()
    {
        // DynamicMethod
        var method = new DynamicMethod(
            name          : "add",
            returnType    : typeof(int),
            parameterTypes: new[] { typeof(int), typeof(int) }
        );

        // 引数 x 生成用
        var x         = method.DefineParameter(position: 1, attributes: ParameterAttributes.In, parameterName: "x");
        // 引数 y 生成用
        var y         = method.DefineParameter(position: 2, attributes: ParameterAttributes.In, parameterName: "y");
        // ILGenerator
        var generator = method.GetILGenerator();

        // 生成したい IL
        // IL_0000: ldarg.0
        // IL_0001: ldarg.1
        // IL_0002: add
        // IL_0003: ret

        // 「最初の引数をスタックにプッシュする」コードを生成
        generator.Emit(opcode: OpCodes.Ldarg_0);
        // 「二つ目の引数をスタックにプッシュ」コードを生成
        generator.Emit(opcode: OpCodes.Ldarg_1);
        // 「二つの値を加算する」コードを生成
        generator.Emit(opcode: OpCodes.Add    );
        // 「リターンする」コードを生成
        generator.Emit(opcode: OpCodes.Ret    );

        // 動的にデリゲートを生成
        return (Func<int, int, int>)method.CreateDelegate(delegateType: typeof(Func<int, int, int>));
    }

    // Expression (式) による Add メソッドの生成
    static Func<int, int, int> AddByExpression()
    {
        // 生成したい式
        // (int x, int y) => x + y

        var x      = Expression.Parameter(type: typeof(int)); // 引数 x の式
        var y      = Expression.Parameter(type: typeof(int)); // 引数 y の式
        var add    = Expression.Add      (left: x, right: y); // x + y の式
        var lambda = Expression.Lambda   (add, x, y        ); // (x, y) => x + y の式
        // ラムダ式をコンパイルしてデリゲートとして返す
        return (Func<int, int, int>)lambda.Compile();
    }

    // Roslyn による Add メソッドの生成
    static Func<int, int, int> AddByRoslyn()
    {
        var engine  = new ScriptEngine(); // C# のスクリプトエンジン
        var session = engine.CreateSession();
        session.ImportNamespace("System"); // System 名前空間のインポート

        return (Func<int, int, int>)session.Execute(code: "(Func<int, int, int>)((x, y) => x + y)");
    }

    static void Main()
    {
        生成のパフォーマンステスト();
    }

    static void 生成のパフォーマンステスト()
    {
        Console.WriteLine("【{0}】", MethodBase.GetCurrentMethod().Name); // メソッド名を表示

        const int 回数 = 1000;

        パフォーマンステスト(() => AddByEmit      (), 回数); // Reflectin.Emit による生成
        パフォーマンステスト(() => AddByExpression(), 回数); // 式木による生成
        パフォーマンステスト(() => AddByRoslyn    (), 回数); // Roslyn による生成
    }

    static void パフォーマンステスト(Expression<Action> 処理式, int 回数)
    {
        パフォーマンステスター.テスト(処理式, 回数, Console.WriteLine);
    }
}

実行してみよう。

【生成のパフォーマンステスト】
                                                           AddByEmit():       7.43/1000000 秒
                                                     AddByExpression():      77.82/1000000 秒
                                                         AddByRoslyn():    3088.51/1000000 秒

速い順に並べてみよう。

順位 方法 時間 (マイクロ秒)
1 Reflection.Emit を使って動的にメソッドを生成した場合 7.43
2 式木を使って動的にメソッドを生成した場合 77.82
3 Roslyn を使って動的にメソッドを生成した場合 3088.51

手間が掛からない方法程時間が掛かっているのが分かる。Roslyn は特に時間が掛かる。3088.51/1000000 秒ということは、約 0.003 秒も掛かっていることになる。

生成済みデリゲートの実行のパフォーマンスのテスト

次は、それぞれの動的生成済みのデリゲートを実行する時間だ。

今度のコードでは、デリゲートを動的生成する迄の時間は測らず、生成後のデリゲートの実行時間を測る。

using Roslyn.Scripting.CSharp;
using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;

static class Program
{
    // 普通の静的な Add メソッド
    static int Add(int x, int y)
    {
        return x + y;
    }

    // Reflection.Emit の DynamicMethod による Add メソッドの生成
    static Func<int, int, int> AddByEmit()
    {
        // DynamicMethod
        var method = new DynamicMethod(
            name          : "add",
            returnType    : typeof(int),
            parameterTypes: new[] { typeof(int), typeof(int) }
        );

        // 引数 x 生成用
        var x         = method.DefineParameter(position: 1, attributes: ParameterAttributes.In, parameterName: "x");
        // 引数 y 生成用
        var y         = method.DefineParameter(position: 2, attributes: ParameterAttributes.In, parameterName: "y");
        // ILGenerator
        var generator = method.GetILGenerator();

        // 生成したい IL
        // IL_0000: ldarg.0
        // IL_0001: ldarg.1
        // IL_0002: add
        // IL_0003: ret

        // 「最初の引数をスタックにプッシュする」コードを生成
        generator.Emit(opcode: OpCodes.Ldarg_0);
        // 「二つ目の引数をスタックにプッシュ」コードを生成
        generator.Emit(opcode: OpCodes.Ldarg_1);
        // 「二つの値を加算する」コードを生成
        generator.Emit(opcode: OpCodes.Add    );
        // 「リターンする」コードを生成
        generator.Emit(opcode: OpCodes.Ret    );

        // 動的にデリゲートを生成
        return (Func<int, int, int>)method.CreateDelegate(delegateType: typeof(Func<int, int, int>));
    }

    // Expression (式) による Add メソッドの生成
    static Func<int, int, int> AddByExpression()
    {
        var x      = Expression.Parameter(type: typeof(int)); // 引数 x の式
        var y      = Expression.Parameter(type: typeof(int)); // 引数 y の式
        var add    = Expression.Add      (left: x, right: y); // x + y の式
        var lambda = Expression.Lambda   (add, x, y        ); // (x, y) => x + y の式
        // ラムダ式をコンパイルしてデリゲートとして返す
        return (Func<int, int, int>)lambda.Compile();
    }

    // Roslyn による Add メソッドの生成
    static Func<int, int, int> AddByRoslyn()
    {
        var engine  = new ScriptEngine();
        var session = engine.CreateSession();
        session.ImportNamespace("System"); // System 名前空間のインポート

        return (Func<int, int, int>)session.Execute(code: "(Func<int, int, int>)((x, y) => x + y)");
    }

    static void Main()
    {
        実行のパフォーマンステスト();
    }

    static void 実行のパフォーマンステスト()
    {
        Console.WriteLine("【{0}】", MethodBase.GetCurrentMethod().Name); // メソッド名を表示

        // それぞれのデリゲートを準備
        Func<int, int, int> add             = Add              ;
        var                 addByEmit       = AddByEmit      (); // Reflectin.Emit による生成
        var                 addByExpression = AddByExpression(); // 式木による生成
        var                 addByRoslyn     = AddByRoslyn    (); // Roslyn による生成

        const int           回数          = 1000000;

        パフォーマンステスト(() => Add            (1, 2), 回数); // 静的な Add を直接呼ぶ
        パフォーマンステスト(() => add            (1, 2), 回数); // 静的な Add をデリゲートに入れて呼ぶ
        パフォーマンステスト(() => addByEmit      (1, 2), 回数); // Reflectin.Emit によって生成済みのデリゲートを呼ぶ
        パフォーマンステスト(() => addByExpression(1, 2), 回数); // 式木によって生成済みのデリゲートを呼ぶ
        パフォーマンステスト(() => addByRoslyn    (1, 2), 回数); // Roslyn によって生成済みのデリゲートを呼ぶ
    }

    static void パフォーマンステスト(Expression<Action> 処理式, int 回数)
    {
        パフォーマンステスター.テスト(処理式, 回数, Console.WriteLine);
    }
}

実行してみよう。

【実行のパフォーマンステスト】
                                                             Add(1, 2):      12.64/1000000000 秒
                   Invoke(value(Program+<>c__DisplayClass0).add, 1, 2):       6.72/1000000000 秒
               Invoke(value(Program+<>c__DisplayClass0).addEmit, 1, 2):       6.68/1000000000 秒
         Invoke(value(Program+<>c__DisplayClass0).addExpression, 1, 2):       4.45/1000000000 秒
             Invoke(value(Program+<>c__DisplayClass0).addRoslyn, 1, 2):       6.55/1000000000 秒

速い順に並べてみよう。

順位 方法 時間 (ナノ秒)
1 式木によって生成済みのデリゲートを呼ぶ 4.45
2 Roslyn によって生成済みのデリゲートを呼ぶ 6.55
3 Reflectin.Emit によって生成済みのデリゲートを呼ぶ 6.68
4 静的な Add をデリゲートに入れて呼ぶ 6.72
5 静的な Add を直接呼ぶ 12.64

今度は、どの結果も大差ない。毎回メソッドを直接呼ぶよりは寧ろ速いことが分かる。

まとめ

方法にも因るが、動的生成自体はそこそこハイコストであることが分かった。

従って、実行の都度、動的生成を行うのではなく、一度生成したデリゲートはキャッシュしておくのが有効だと思われる。

一度生成すれば、リフレクション等を用いた動的なコードとは異なり、通常のデリゲートなので、実行自体に時間が掛かる訳ではない。

次回からは、別のケースでキャッシュを用いた場合について検証していく。

2013年11月06日

[C#][.NET] メタプログラミング入門 - メソッド呼び出しのパフォーマンスの比較

Metasequoia

※ 「[C#][.NET] メタプログラミング入門 - Add メソッドのパフォーマンスの比較」の続き。

C# によるメタプログラミングでのパフォーマンスの比較

前回は、C# によるメタプログラミングで Add メソッドを動的生成した場合の実行速度を測定した。

今回も同様に、メソッドを動的生成した場合の実行速度を測定しよう。今回は、メソッド呼び出しのパフォーマンスを検証する。

次の順番で進めていく。

  • それぞれのメソッド呼び出し方法の紹介
    • 通常の静的なメソッド呼び出し
    • リフレクションを使った動的なメソッド呼び出し
    • dynamic を使った動的なメソッド呼び出し
    • Reflection.Emit を使って動的にメソッドを生成した場合
    • 式木を使って動的にメソッドを生成した場合
    • Roslyn を使って動的にメソッドを生成した場合
  • 実行に掛かった時間の測定用のクラスの準備
  • デリゲートの動的生成のパフォーマンスのテスト
    • Reflectin.Emit による生成
    • 式木による生成
    • Roslyn による生成
  • 実行のパフォーマンステスト
    • デリゲートの動的生成を行わない場合
      • デリゲートでなくメソッドを直接呼ぶ場合
      • メソッドをデリゲートに入れてから呼ぶ場合
      • リフレクションによる動的呼び出し
      • dynamic による動的呼び出し
    • 生成済みのデリゲートを使って呼ぶ場合
      • Reflectin.Emit による生成
      • 式木による生成
      • Roslyn による生成
  • キャッシュによる実行のパフォーマンステスト
    • コード生成しない場合と、コード生成しつつ呼ぶ場合 コード生成にキャッシュを効かせた場合

それぞれのメソッド呼び出し方法

先ずは、それぞれの実際の呼び出しの例を示そう。

  • 通常の静的なメソッド呼び出し
  • リフレクションを使った動的なメソッド呼び出し
  • dynamic を使った動的なメソッド呼び出し
  • Reflection.Emit を使って動的にメソッドを生成した場合
  • 式木を使って動的にメソッドを生成した場合
  • Roslyn を使って動的にメソッドを生成した場合

先ず、試しに呼び出すメソッドを準備しよう。

// 検証用のクラス
public class Something
{
    // 検証用のメソッド
    public string DoSomething()
    {
        return "Hello!";
    }
}

この Something クラスのインスタンスの DoSomething() メソッドを呼び出すこととする。

順次、それぞれの方法での呼び出しを書いて行こう。

普通の静的なメソッド呼び出し
    // 普通の静的なメソッド呼び出し
    static string Call(Something item)
    {
        // 静的なメソッド呼び出し
        return item.DoSomething();
    }
リフレクションを使ったメソッド呼び出し

詳しくは以前の記事等を参考にしてほしい。

    // リフレクションを使ったメソッド呼び出し
    static object CallByReflection(object item)
    {
        // リフレクションを使って動的にメソッド情報を取得
        var doSomething = item.GetType().GetMethod("DoSomething");
        // メソッドの動的呼び出し
        return doSomething.Invoke(item, null);
    }
dynamic を使ったメソッド呼び出し

詳しくは以前の記事等を参考にしてほしい。

    // dynamic を使ったメソッド呼び出し
    static dynamic CallByDynamic(dynamic item)
    {
        // dynamic による動的なメソッド呼び出し
        return item.DoSomething();
    }
Reflection.Emit を使って動的にメソッドを生成した場合

[C#][.NET] メタプログラミング入門 - Reflection.Emit による Add メソッドの動的生成」でやったようにやってみよう。

生成したいコードを先ず C# で書いてビルドし、ILSpyIL (Intermediate Language) を調べてみよう。

次のような C# のソースコードをビルドし、

// 検証用のクラス
public class Something
{
    // 検証用のメソッド
    public string DoSomething()
    {
        return "Hello!";
    }
}

static class Program
{
    // 普通の静的なメソッド呼び出し
    static string Call(Something item)
    {
        return item.DoSomething();
    }

    static void Main()
    {}
}

Call メソッドの部分を ILSpy で見る。

ILSpy で Call メソッドの IL を見る
ILSpy で Call メソッドの IL を見る

参考にして、Reflection.Emit でメソッド生成を行ってみよう。

    // using namespace 省略

    // Reflection.Emit の DynamicMethod によるメソッド呼び出しメソッドの生成
    static Func<T, TResult> CallByEmit<T, TResult>(string methodName)
    {
        // DynamicMethod
        var method = new DynamicMethod(
            name          : "call"             ,
            returnType    : typeof(string)     ,
            parameterTypes: new[] { typeof(T) }
        );

        // 引数 item 生成用
        var item = method.DefineParameter(position: 1, attributes: ParameterAttributes.In, parameterName: "item");
        // ILGenerator
        var generator = method.GetILGenerator();

        // 生成したい IL
        // IL_0000: ldarg.0
        // IL_0001: callvirt instance void Something::DoSomething()
        // IL_0006: ret

        // 「引数をスタックにプッシュする」コードを生成
        generator.Emit(opcode: OpCodes.Ldarg_0);
        // 「指定された名前のメソッドを呼ぶ」コードを生成
        generator.Emit(opcode: OpCodes.Callvirt, meth: typeof(T).GetMethod(name: methodName, types: Type.EmptyTypes));
        // 「リターンする」コードを生成
        generator.Emit(opcode: OpCodes.Ret);

        // 動的にデリゲートを生成
        return (Func<T, TResult>)method.CreateDelegate(delegateType: typeof(Func<T, TResult>));
    }
式木を使って動的にメソッドを生成した場合

[C#][.NET][式木] メタプログラミング入門 - 式木による Add メソッドの動的生成」でやったようにやってみよう。

    var item = new Something();

    Expression<Func<Something, string>> call = item => item.DoSomething();

のようなコードを実行して、Visual Studio のデバッガーの「クイックウォッチ」等で call の構造を調べてみる。

call 式の構造
call 式の構造

これを参考にして、式木によるメソッド生成を行ってみよう。

    // using namespace 省略

    // Expression (式) によるメソッド呼び出しメソッドの生成
    static Func<T, TResult> CallByExpression<T, TResult>(string methodName)
    {
        // 生成したい式の例:
        // (T item) => item.methodName()

        // 引数 item の式
        var parameterExpression = Expression.Parameter(type: typeof(T), name: "item");
        // item.methodName() の式
        var callExpression      = Expression.Call(
                                      instance: parameterExpression,
                                      method  : typeof(T).GetMethod(methodName, Type.EmptyTypes)
                                  );
        // item => item.methodName() の式
        var lambda              = Expression.Lambda(callExpression, parameterExpression);
        // ラムダ式をコンパイルしてデリゲートとして返す
        return (Func<T, TResult>)lambda.Compile();
    }
Roslyn を使って動的にメソッドを生成した場合

[C#][.NET][Roslyn] メタプログラミング入門 - Roslyn による Add メソッドの動的生成」でやったようにやってみよう。

    // using namespace 省略

    // Roslyn によるメソッド呼び出しメソッドの生成
    static Func<T, TResult> CallByRoslyn<T, TResult>(string methodName)
    {
        var engine  = new ScriptEngine(); // C# のスクリプトエンジン
        engine.AddReference(typeof(T).Assembly); // 型 T のメソッドを参照するのに必要
        engine.ImportNamespace("System");        // System 名前空間のインポート
        var session = engine.CreateSession();

        // 作りたいソースコードの例
        // (Func<Something, string>)(item => item.DoSomething())

        // ソースコードを文字列として作成
        var code    = string.Format("(Func<{0}, {1}>)(item => item.{2}())", typeof(T).FullName, typeof(TResult).FullName, methodName);
        // ソースコードをコンパイルしてデリゲートを生成して返す
        return (Func<T, TResult>)session.Execute(code: code);
    }
実行のテスト

まとめると次のようになる。

using Roslyn.Scripting.CSharp;
using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;

// 検証用のクラス
public class Something
{
    // 検証用のメソッド
    public string DoSomething()
    {
        return "Hello!";
    }
}

static class Program
{
    // 普通の静的なメソッド呼び出し
    static string Call(Something item)
    {
        // 静的なメソッド呼び出し
        return item.DoSomething();
    }

    // リフレクションを使ったメソッド呼び出し
    static object CallByReflection(object item)
    {
        // リフレクションを使って動的にメソッド情報を取得
        var doSomething = item.GetType().GetMethod("DoSomething");
        // メソッドの動的呼び出し
        return doSomething.Invoke(item, null);
    }

    // dynamic を使ったメソッド呼び出し
    static dynamic CallByDynamic(dynamic item)
    {
        // dynamic による動的なメソッド呼び出し
        return item.DoSomething();
    }

    // Reflection.Emit の DynamicMethod によるメソッド呼び出しメソッドの生成
    static Func<T, TResult> CallByEmit<T, TResult>(string methodName)
    {
        // DynamicMethod
        var method = new DynamicMethod(
            name          : "call"             ,
            returnType    : typeof(string)     ,
            parameterTypes: new[] { typeof(T) }
        );

        // 引数 item 生成用
        var item = method.DefineParameter(position: 1, attributes: ParameterAttributes.In, parameterName: "item");
        // ILGenerator
        var generator = method.GetILGenerator();

        // 生成したい IL
        // IL_0000: ldarg.0
        // IL_0001: callvirt instance void Something::DoSomething()
        // IL_0006: ret

        // 「引数をスタックにプッシュする」コードを生成
        generator.Emit(opcode: OpCodes.Ldarg_0);
        // 「指定された名前のメソッドを呼ぶ」コードを生成
        generator.Emit(opcode: OpCodes.Callvirt, meth: typeof(T).GetMethod(name: methodName, types: Type.EmptyTypes));
        // 「リターンする」コードを生成
        generator.Emit(opcode: OpCodes.Ret);

        // 動的にデリゲートを生成
        return (Func<T, TResult>)method.CreateDelegate(delegateType: typeof(Func<T, TResult>));
    }

    // Expression (式) によるメソッド呼び出しメソッドの生成
    static Func<T, TResult> CallByExpression<T, TResult>(string methodName)
    {
        // 生成したい式の例:
        // (T item) => item.methodName()

        // 引数 item の式
        var parameterExpression = Expression.Parameter(type: typeof(T), name: "item");
        // item.methodName() の式
        var callExpression      = Expression.Call(
                                      instance: parameterExpression,
                                      method  : typeof(T).GetMethod(methodName, Type.EmptyTypes)
                                  );
        // item => item.methodName() の式
        var lambda              = Expression.Lambda(callExpression, parameterExpression);
        // ラムダ式をコンパイルしてデリゲートとして返す
        return (Func<T, TResult>)lambda.Compile();
    }

    // Roslyn によるメソッド呼び出しメソッドの生成
    static Func<T, TResult> CallByRoslyn<T, TResult>(string methodName)
    {
        var engine  = new ScriptEngine(); // C# のスクリプトエンジン
        engine.AddReference(typeof(T).Assembly); // 型 T のメソッドを参照するのに必要
        engine.ImportNamespace("System");        // System 名前空間のインポート
        var session = engine.CreateSession();

        // 作りたいソースコードの例
        // (Func<Something, string>)(item => item.DoSomething())

        // ソースコードを文字列として作成
        var code    = string.Format("(Func<{0}, {1}>)(item => item.{2}())", typeof(T).FullName, typeof(TResult).FullName, methodName);
        // ソースコードをコンパイルしてデリゲートを生成して返す
        return (Func<T, TResult>)session.Execute(code: code);
    }

    static void Main()
    {
        実行のテスト();
    }

    static void 実行のテスト()
    {
        Console.WriteLine("【{0}】", MethodBase.GetCurrentMethod().Name); // メソッド名を表示

        // テスト用のアイテム
        var item               = new Something();

        // 普通の静的なメソッド呼び出し
        var answer             = Call            (item);
        Console.WriteLine("answer            : {0}", answer            );

        // リフレクションによる動的なメソッド呼び出し
        var answerByReflection = CallByReflection(item);
        Console.WriteLine("answerByReflection: {0}", answerByReflection);

        // dynamic による動的なメソッド呼び出し
        var answerByDynamic    = CallByDynamic   (item);
        Console.WriteLine("answerByDynamic   : {0}", answerByDynamic   );

        // Reflection.Emit の DynamicMethod によるメソッド呼び出しメソッドの生成
        var callByEmit         = CallByEmit      <Something, string>("DoSomething");
        var answerByEmit       = callByEmit      (item); // 生成したメソッドの呼び出し
        Console.WriteLine("answerByEmit      : {0}", answerByEmit      );

        // Expression (式) によるメソッド呼び出しメソッドの生成
        var callByExpression   = CallByExpression<Something, string>("DoSomething");
        var answerByExpression = callByExpression(item); // 生成したメソッドの呼び出し
        Console.WriteLine("answerByExpression: {0}", answerByExpression);

        // Roslyn によるメソッド呼び出しメソッドの生成
        var callByRoslyn       = CallByRoslyn    <Something, string>("DoSomething");
        var answerByRoslyn     = callByRoslyn    (item); // 生成したメソッドの呼び出し
        Console.WriteLine("answerByRoslyn    : {0}", answerByRoslyn    );
    }
}

実行してみると、次のようになる。

【実行のテスト】
answer            : Hello!
answerByReflection: Hello!
answerByDynamic   : Hello!
answerByEmit      : Hello!
answerByExpression: Hello!
answerByRoslyn    : Hello!

いずれの場合も、ちゃんとテスト用のアイテム item のメソッド DoSomething() を呼び出せていることが分かる。

実行に掛かった時間の測定用のクラス

実行に掛かった時間を測る為、今回も、次のクラスを用意する。

using System;
using System.Diagnostics;
using System.Linq.Expressions;

public static class パフォーマンステスター
{
    public static void テスト(Expression<Action> 処理式, int 回数, Action<string> output)
    {
        // 処理でなく処理式として受け取っているのは、文字列として出力する為
        var 処理 = 処理式.Compile();
        var 時間 = 計測(処理, 回数).TotalMilliseconds; // 回数分の処理に掛かったミリ秒数
        // 一回当たり何秒掛かったかを出力
        output(string.Format("{0,70}: {1,8:F}/{2} 秒", 処理式.Body.ToString(), 時間, 回数 * 1000));
    }

    static TimeSpan 計測(Action 処理, int 回数)
    {
        var stopwatch = new Stopwatch(); // 時間計測用
        stopwatch.Start();
        回数.回(処理);
        stopwatch.Stop();
        return stopwatch.Elapsed;
    }

    static void 回(this int @this, Action 処理)
    {
        for (var カウンター = 0; カウンター < @this; カウンター++)
            処理();
    }
}

それでは測っていこう。

デリゲートの動的生成のパフォーマンスのテスト

では、デリゲートの動的生成に掛かる時間を測ってみよう。

前回同様、デリゲートを動的生成する三種類のコードを呼び出し、それぞれがデリゲートを作成するまでに掛かる時間を測ってみる。

using Roslyn.Scripting.CSharp;
using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;

// 検証用のクラス
public class Something
{
    // 検証用のメソッド
    public string DoSomething()
    {
        return "Hello!";
    }
}

static class Program
{
    // Reflection.Emit の DynamicMethod によるメソッド呼び出しメソッドの生成
    static Func<T, TResult> CallByEmit<T, TResult>(string methodName)
    {
        // DynamicMethod
        var method = new DynamicMethod(
            name          : "call"             ,
            returnType    : typeof(string)     ,
            parameterTypes: new[] { typeof(T) }
        );

        // 引数 item 生成用
        var item = method.DefineParameter(position: 1, attributes: ParameterAttributes.In, parameterName: "item");
        // ILGenerator
        var generator = method.GetILGenerator();

        // 生成したい IL
        // IL_0000: ldarg.0
        // IL_0001: callvirt instance void Something::DoSomething()
        // IL_0006: ret

        // 「引数をスタックにプッシュする」コードを生成
        generator.Emit(opcode: OpCodes.Ldarg_0);
        // 「指定された名前のメソッドを呼ぶ」コードを生成
        generator.Emit(opcode: OpCodes.Callvirt, meth: typeof(T).GetMethod(name: methodName, types: Type.EmptyTypes));
        // 「リターンする」コードを生成
        generator.Emit(opcode: OpCodes.Ret);

        // 動的にデリゲートを生成
        return (Func<T, TResult>)method.CreateDelegate(delegateType: typeof(Func<T, TResult>));
    }

    // Expression (式) によるメソッド呼び出しメソッドの生成
    static Func<T, TResult> CallByExpression<T, TResult>(string methodName)
    {
        // 引数 item の式
        var parameterExpression = Expression.Parameter(type: typeof(T), name: "item");
        // item.methodName() の式
        var callExpression      = Expression.Call(
                                      instance: parameterExpression,
                                      method  : typeof(T).GetMethod(methodName, Type.EmptyTypes)
                                  );
        // item => item.methodName() の式
        var lambda              = Expression.Lambda(callExpression, parameterExpression);
        // ラムダ式をコンパイルしてデリゲートとして返す
        return (Func<T, TResult>)lambda.Compile();
    }

    // Roslyn によるメソッド呼び出しメソッドの生成
    static Func<T, TResult> CallByRoslyn<T, TResult>(string methodName)
    {
        var engine  = new ScriptEngine();
        engine.AddReference(typeof(T).Assembly); // 型 T のメソッドを参照するのに必要
        engine.ImportNamespace("System");        // System 名前空間のインポート
        var session = engine.CreateSession();
        // ソースコードを文字列として作成
        var code    = string.Format("(Func<{0}, {1}>)(item => item.{2}())", typeof(T).FullName, typeof(TResult).FullName, methodName);
        // ソースコードをコンパイルしてデリゲートを生成して返す
        return (Func<T, TResult>)session.Execute(code: code);
    }

    static void Main()
    {
        生成のパフォーマンステスト();
    }

    static void 生成のパフォーマンステスト()
    {
        Console.WriteLine("【{0}】", MethodBase.GetCurrentMethod().Name); // メソッド名を表示

        const int 回数 = 1000;

        // Reflectin.Emit による生成
        パフォーマンステスト(() => CallByEmit      <Something, string>("DoSomething"), 回数);
        // 式木による生成
        パフォーマンステスト(() => CallByExpression<Something, string>("DoSomething"), 回数);
        // Roslyn による生成
        パフォーマンステスト(() => CallByRoslyn    <Something, string>("DoSomething"), 回数);
    }

    static void パフォーマンステスト(Expression<Action> 処理式, int 回数)
    {
        パフォーマンステスター.テスト(処理式, 回数, Console.WriteLine);
    }
}

実行してみよう。

【生成のパフォーマンステスト】
                                                                         CallByEmit("DoSomething"):     5.66/1000000 秒
                                                                   CallByExpression("DoSomething"):   106.06/1000000 秒
                                                                       CallByRoslyn("DoSomething"):  3474.70/1000000 秒

速い順に並べてみよう。

順位 方法 時間 (マイクロ秒)
1 Reflection.Emit を使って動的にメソッドを生成した場合 5.66
2 式木を使って動的にメソッドを生成した場合 106.06
3 Roslyn を使って動的にメソッドを生成した場合 3474.70

前回同様、手間が掛からない方法程生成に時間が掛かっている。

生成済みデリゲートの実行のパフォーマンスのテスト

次は、それぞれの方法によるデリゲートを実行する時間を測ろう。

デリゲートを動的生成場合は、動的生成する迄の時間は測らず、生成後のデリゲートの実行時間を測る。

using Roslyn.Scripting.CSharp;
using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;

// 検証用のクラス
public class Something
{
    // 検証用のメソッド
    public string DoSomething()
    {
        return "Hello!";
    }
}

static class Program
{
    // 普通の静的なメソッド呼び出し
    static string Call(Something item)
    {
        // 静的なメソッド呼び出し
        return item.DoSomething();
    }

    // リフレクションを使ったメソッド呼び出し
    static object CallByReflection(object item)
    {
        // リフレクションを使って動的にメソッド情報を取得
        var doSomething = item.GetType().GetMethod("DoSomething");
        // メソッドの動的呼び出し
        return doSomething.Invoke(item, null);
    }

    // dynamic を使ったメソッド呼び出し
    static dynamic CallByDynamic(dynamic item)
    {
        // dynamic による動的なメソッド呼び出し
        return item.DoSomething();
    }

    // Reflection.Emit の DynamicMethod によるメソッド呼び出しメソッドの生成
    static Func<T, TResult> CallByEmit<T, TResult>(string methodName)
    {
        // DynamicMethod
        var method = new DynamicMethod(
            name          : "call"             ,
            returnType    : typeof(string)     ,
            parameterTypes: new[] { typeof(T) }
        );

        // 引数 item 生成用
        var item = method.DefineParameter(position: 1, attributes: ParameterAttributes.In, parameterName: "item");
        // ILGenerator
        var generator = method.GetILGenerator();

        // 生成したい IL
        // IL_0000: ldarg.0
        // IL_0001: callvirt instance void Something::DoSomething()
        // IL_0006: ret

        // 「引数をスタックにプッシュする」コードを生成
        generator.Emit(opcode: OpCodes.Ldarg_0);
        // 「指定された名前のメソッドを呼ぶ」コードを生成
        generator.Emit(opcode: OpCodes.Callvirt, meth: typeof(T).GetMethod(name: methodName, types: Type.EmptyTypes));
        // 「リターンする」コードを生成
        generator.Emit(opcode: OpCodes.Ret);

        // 動的にデリゲートを生成
        return (Func<T, TResult>)method.CreateDelegate(delegateType: typeof(Func<T, TResult>));
    }

    // Expression (式) によるメソッド呼び出しメソッドの生成
    static Func<T, TResult> CallByExpression<T, TResult>(string methodName)
    {
        // 引数 item の式
        var parameterExpression = Expression.Parameter(type: typeof(T), name: "item");
        // item.methodName() の式
        var callExpression      = Expression.Call(
                                      instance: parameterExpression,
                                      method  : typeof(T).GetMethod(methodName, Type.EmptyTypes)
                                  );
        // item => item.methodName() の式
        var lambda              = Expression.Lambda(callExpression, parameterExpression);
        // ラムダ式をコンパイルしてデリゲートとして返す
        return (Func<T, TResult>)lambda.Compile();
    }

    // Roslyn によるメソッド呼び出しメソッドの生成
    static Func<T, TResult> CallByRoslyn<T, TResult>(string methodName)
    {
        var engine  = new ScriptEngine();
        engine.AddReference(typeof(T).Assembly); // 型 T のメソッドを参照するのに必要
        engine.ImportNamespace("System");        // System 名前空間のインポート
        var session = engine.CreateSession();
        // ソースコードを文字列として作成
        var code    = string.Format("(Func<{0}, {1}>)(item => item.{2}())", typeof(T).FullName, typeof(TResult).FullName, methodName);
        // ソースコードをコンパイルしてデリゲートを生成して返す
        return (Func<T, TResult>)session.Execute(code: code);
    }

    static void Main()
    {
        実行のパフォーマンステスト();
    }

    static void 実行のパフォーマンステスト()
    {
        Console.WriteLine("【{0}】", MethodBase.GetCurrentMethod().Name); // メソッド名を表示

        // 検証用のアイテム
        var                      item             = new Something();

        // それぞれのデリゲートを準備
        Func<Something, string > call             = Call                                              ;
        Func<object   , object > callByReflection = CallByReflection                                  ;
        Func<dynamic  , dynamic> callByDynamic    = CallByDynamic                                     ;
        var                      callByEmit       = CallByEmit      <Something, string>("DoSomething");
        var                      callByExpression = CallByExpression<Something, string>("DoSomething");
        var                      callByRoslyn     = CallByRoslyn    <Something, string>("DoSomething");

        const int                回数             = 1000000;

        // デリゲートの動的生成を行わない場合
        パフォーマンステスト(() => Call            (item), 回数); // 静的メソッドを直接呼ぶ場合
        パフォーマンステスト(() => call            (item), 回数); // 静的メソッドをデリゲートに入れてから呼ぶ場合
        パフォーマンステスト(() => callByReflection(item), 回数); // リフレクションによる動的呼び出し
        パフォーマンステスト(() => callByDynamic   (item), 回数); // dynamic による動的呼び出し

        // 生成済みのデリゲートを使って呼ぶ場合
        パフォーマンステスト(() => callByEmit      (item), 回数); // Reflectin.Emit による生成
        パフォーマンステスト(() => callByExpression(item), 回数); // 式木による生成
        パフォーマンステスト(() => callByRoslyn    (item), 回数); // Roslyn による生成
    }

    static void パフォーマンステスト(Expression<Action> 処理式, int 回数)
    {
        パフォーマンステスター.テスト(処理式, 回数, Console.WriteLine);
    }
}

実行してみよう。

【実行のパフォーマンステスト】
                                                      Call(value(Program+<>c__DisplayClass2).item):    16.03/1000000000 秒
            Invoke(value(Program+<>c__DisplayClass2).call, value(Program+<>c__DisplayClass2).item):    10.74/1000000000 秒
Invoke(value(Program+<>c__DisplayClass2).callByReflection, value(Program+<>c__DisplayClass2).item):   316.62/1000000000 秒
   Invoke(value(Program+<>c__DisplayClass2).callByDynamic, value(Program+<>c__DisplayClass2).item):    57.93/1000000000 秒
      Invoke(value(Program+<>c__DisplayClass2).callByEmit, value(Program+<>c__DisplayClass2).item):    20.66/1000000000 秒
Invoke(value(Program+<>c__DisplayClass2).callByExpression, value(Program+<>c__DisplayClass2).item):    18.06/1000000000 秒
    Invoke(value(Program+<>c__DisplayClass2).callByRoslyn, value(Program+<>c__DisplayClass2).item):     8.01/1000000000 秒

速い順に並べてみよう。

順位 方法 時間 (ナノ秒)
1 Roslyn を使って動的にメソッドを生成した場合 8.01
2 静的メソッドをデリゲートに入れてから呼ぶ場合 10.74
3 静的メソッドを直接呼ぶ場合 16.03
4 式木を使って動的にメソッドを生成した場合 18.06
5 Reflection.Emit を使って動的にメソッドを生成した場合 20.66
6 dynamic を使った動的なメソッド呼び出し 57.93
7 リフレクションを使った動的なメソッド呼び出し 316.62

動的生成が終わってしまえば、その後のデリゲートの実行は通常のデリゲートと変わらない。

動的なメソッド呼び出しは呼び出す度に遅い (特にリフレクション) ので、場合によっては動的生成にパフォーマンス上のメリットがある。

キャッシュによる実行のパフォーマンステスト

つまり、動的にメソッドを生成する場合でも、生成済みのメソッドをうまくキャッシュしてやれば、パフォーマンスが大きく向上することになる。

キャッシュを使った場合と使わないで実行の度にメソッドを生成した場合の速度を測ってみよう。

先ずは、生成済みデリゲートをキャッシュして呼び出すクラスを作ろう。

デリゲートのキャッシュ クラス
using System;
using System.Collections.Generic;
using System.Text;

// デリゲートのキャッシュ
public static class DelegateCache
{
    // 生成したデリゲートのキャッシュ
    static readonly Dictionary<string, Delegate> methods = new Dictionary<string, Delegate>();

    // メソッド呼び出し
    public static TResult Call<T, TResult>(this T @this, string methodName, Func<string, Func<T, TResult>> generator)
    {
        var targetType = @this.GetType();
        var key        = ToKey(targetType, methodName);

        ToCache<T, TResult>(key: key, item: @this, methodName: methodName, generator: generator); // キャッシュする
        // キャッシュ内のデリゲートを呼ぶ
        return ((Func<T, TResult>)methods[key: key])(@this);
    }

    // キャッシュに入れる
    static void ToCache<T, TResult>(string key, T item, string methodName, Func<string, Func<T, TResult>> generator)
    {
        if (!methods.ContainsKey(key: key)) {     // キャッシュに無い場合は
            var method = generator(methodName);   // 動的にデリゲートを生成して
            methods.Add(key: key, value: method); // キャッシュに格納
        }
    }

    // キーに変換
    static string ToKey(Type type, string methodName)
    {
        return new StringBuilder().Append(type.FullName).Append(".").Append(methodName).ToString();
    }
}

それでは、実際に測ってみよう。

using Roslyn.Scripting.CSharp;
using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;

// 検証用のクラス
public class Something
{
    // 検証用のメソッド
    public string DoSomething()
    {
        return "Hello!";
    }
}

static class Program
{
    // 普通の静的なメソッド呼び出し
    static string Call(Something item)
    {
        // 静的なメソッド呼び出し
        return item.DoSomething();
    }

    // リフレクションを使ったメソッド呼び出し
    static object CallByReflection(object item)
    {
        // リフレクションを使って動的にメソッド情報を取得
        var doSomething = item.GetType().GetMethod("DoSomething");
        // メソッドの動的呼び出し
        return doSomething.Invoke(item, null);
    }

    // dynamic を使ったメソッド呼び出し
    static dynamic CallByDynamic(dynamic item)
    {
        // dynamic による動的なメソッド呼び出し
        return item.DoSomething();
    }

    // Reflection.Emit の DynamicMethod によるメソッド呼び出しメソッドの生成
    static Func<T, TResult> CallByEmit<T, TResult>(string methodName)
    {
        // DynamicMethod
        var method = new DynamicMethod(
            name          : "call"             ,
            returnType    : typeof(string)     ,
            parameterTypes: new[] { typeof(T) }
        );

        // 引数 item 生成用
        var item = method.DefineParameter(position: 1, attributes: ParameterAttributes.In, parameterName: "item");
        // ILGenerator
        var generator = method.GetILGenerator();

        // 生成したい IL
        // IL_0000: ldarg.0
        // IL_0001: callvirt instance void Something::DoSomething()
        // IL_0006: ret

        // 「引数をスタックにプッシュする」コードを生成
        generator.Emit(opcode: OpCodes.Ldarg_0);
        // 「指定された名前のメソッドを呼ぶ」コードを生成
        generator.Emit(opcode: OpCodes.Callvirt, meth: typeof(T).GetMethod(name: methodName, types: Type.EmptyTypes));
        // 「リターンする」コードを生成
        generator.Emit(opcode: OpCodes.Ret);

        // 動的にデリゲートを生成
        return (Func<T, TResult>)method.CreateDelegate(delegateType: typeof(Func<T, TResult>));
    }

    // Expression (式) によるメソッド呼び出しメソッドの生成
    static Func<T, TResult> CallByExpression<T, TResult>(string methodName)
    {
        // 引数 item の式
        var parameterExpression = Expression.Parameter(type: typeof(T), name: "item");
        // item.methodName() の式
        var callExpression      = Expression.Call(
                                      instance: parameterExpression,
                                      method  : typeof(T).GetMethod(methodName, Type.EmptyTypes)
                                  );
        // item => item.methodName() の式
        var lambda              = Expression.Lambda(callExpression, parameterExpression);
        // ラムダ式をコンパイルしてデリゲートとして返す
        return (Func<T, TResult>)lambda.Compile();
    }

    // Roslyn によるメソッド呼び出しメソッドの生成
    static Func<T, TResult> CallByRoslyn<T, TResult>(string methodName)
    {
        var engine  = new ScriptEngine();
        engine.AddReference(typeof(T).Assembly); // 型 T のメソッドを参照するのに必要
        engine.ImportNamespace("System");        // System 名前空間のインポート
        var session = engine.CreateSession();
        // ソースコードを文字列として作成
        var code    = string.Format("(Func<{0}, {1}>)(item => item.{2}())", typeof(T).FullName, typeof(TResult).FullName, methodName);
        // ソースコードをコンパイルしてデリゲートを生成して返す
        return (Func<T, TResult>)session.Execute(code: code);
    }

    static void Main()
    {
        キャッシュによる実行のパフォーマンステスト();
    }

    static void キャッシュによる実行のパフォーマンステスト()
    {
        Console.WriteLine("【{0}】", MethodBase.GetCurrentMethod().Name); // メソッド名を表示

        // 検証用のアイテム
        var       item = new Something();

        const int 回数 = 1000;

        // コード生成しない場合
        パフォーマンステスト(() => Call            (item), 回数); // 静的メソッド
        パフォーマンステスト(() => CallByReflection(item), 回数); // リフレクションによる動的呼び出し
        パフォーマンステスト(() => CallByDynamic   (item), 回数); // dynamic による動的呼び出し

        // コード生成しつつ呼ぶ場合 (Reflection.Emit、式木、Roslyn の順)
        パフォーマンステスト(() => CallByEmit      <Something, string>("DoSomething")(item), 回数);
        パフォーマンステスト(() => CallByExpression<Something, string>("DoSomething")(item), 回数);
        パフォーマンステスト(() => CallByRoslyn    <Something, string>("DoSomething")(item), 回数);

        // コード生成にキャッシュを効かせた場合 (Reflection.Emit、式木、Roslyn の順)
        パフォーマンステスト(() => item.Call<Something, string>("DoSomething", methodName => CallByEmit      <Something, string>(methodName)), 回数);
        パフォーマンステスト(() => item.Call<Something, string>("DoSomething", methodName => CallByExpression<Something, string>(methodName)), 回数);
        パフォーマンステスト(() => item.Call<Something, string>("DoSomething", methodName => CallByRoslyn    <Something, string>(methodName)), 回数);
    }

    static void パフォーマンステスト(Expression<Action> 処理式, int 回数)
    {
        パフォーマンステスター.テスト(処理式, 回数, Console.WriteLine);
    }
}

実行結果は次の通り。

【キャッシュによる実行のパフォーマンステスト】
                                                      Call(value(Program+<>c__DisplayClass6).item):     0.02/1000000 秒
                                          CallByReflection(value(Program+<>c__DisplayClass6).item):     0.41/1000000 秒
                                             CallByDynamic(value(Program+<>c__DisplayClass6).item):     0.05/1000000 秒
                         Invoke(CallByEmit("DoSomething"), value(Program+<>c__DisplayClass6).item):    52.71/1000000 秒
                   Invoke(CallByExpression("DoSomething"), value(Program+<>c__DisplayClass6).item):    97.02/1000000 秒
                       Invoke(CallByRoslyn("DoSomething"), value(Program+<>c__DisplayClass6).item):  2349.83/1000000 秒
  value(Program+<>c__DisplayClass6).item.Call("DoSomething", methodName => CallByEmit(methodName)):     6.88/1000000 秒
value(Program+<>c__DisplayClass6).item.Call("DoSomething", methodName => CallByExpression(methodName)):     3.36/1000000 秒
value(Program+<>c__DisplayClass6).item.Call("DoSomething", methodName => CallByRoslyn(methodName)):     3.49/1000000 秒

こちらも速い順に並べてみよう。

順位 方法 時間 (マイクロ秒)
1 静的メソッド 0.02
2 dynamic による動的呼び出し 0.05
3 リフレクションによる動的呼び出し 0.41
4 式木 (キャッシュ有り) 3.36
5 Roslyn (キャッシュ有り) 3.49
6 Reflection.Emit (キャッシュ有り) 6.88
7 Reflection.Emit (キャッシュ無し) 52.71
8 式木 (キャッシュ無し) 97.02
9 Roslyn (キャッシュ無し) 2349.83

キャッシュ テーブルのオーバーヘッドはあるが、動的生成を行う場合は、キャッシュがとても有効であることが分かる。

まとめ

今回は、とても簡単なメソッド呼び出しの例で、静的な例、動的な例、動的にコードを生成する三通りの例の比較を行った。また、動的にコードを生成する場合にはキャッシュが有効であることを示した。

次回からは、もう少し応用例をあげていきたい。

2013年11月07日

[C#][.NET] メタプログラミング入門 - 応用編 - オブジェクトの文字列変換を静的/動的に行う

Metasequoia

※ 「[C#][.NET] メタプログラミング入門 - メソッド呼び出しのパフォーマンスの比較」の続き。

前回は、コード生成を行ってメソッド呼び出しを行う3通りの方法と静的なメソッド呼び出しや動的なメソッド呼び出しのパフォーマンスを比較した。

今回から、少しずつ応用に入っていきたい。

■ メタプログラミングの応用例

[C#][.NET] メタプログラミング入門 - はじめに」で、メタプログラミングが有効な例として次のようなものがあると述べた。

メタプログラミングが有効な例
  • コンパイラー/インタープリター
    ホスト言語のソースコードから動的に対象言語のプログラムを生成
  • O/R マッパー
    クラスやオブジェクトから動的に SQL を生成
  • XML や JSON の入出力
    クラスやオブジェクト等から動的に XMLJSON を生成/XML や JSON から動的にクラスやオブジェクト等を生成
    (生成するプログラムをプログラムで生成)
  • モック (mock) オブジェクト
    モック (ユニットテストで用いられる代用のオブジェクト) を動的に生成
    (生成するプログラムをプログラムで生成)
  • Web アプリケーション
    クライアント側で動作するプログラム (HTMLJavaScript 等) をサーバー側で動的に生成

この中から、今回は、「クラスやオブジェクト等から動的に文字列を生成する例」として、次にあげる例を見てみることにしよう。

クラスやオブジェクト等から動的に文字列を生成する例
  • デバッグ情報の出力
    クラスやオブジェクト等を、型情報を使って文字列に変換し、テストやデバッグで用いる。
  • ASP.NET (Web アプリケーション/Web サービス) のサーバー側の処理
    クラスやオブジェクト等を、型情報を使って HTML に変換し、出力する。
  • XML シリアライザー
    クラスやオブジェクト等を、型情報を使って XML に変換し、出力する。

これらについてメタプログラミング (動的コード生成) の例を示す前に、メタプログラミングによらない静的な方法とリフレクションによる動的な方法を見てみよう。

オブジェクトから文字列を生成する静的な例

先ずは、基本である静的な方法だ。

テスト用のクラス

文字列に変換する対象のクラスとして、次の単純な Book クラスを用意した。

// テスト用のクラス
public sealed class Book
{
    public string Title { get; set; }
    public int    Price { get; set; }
}

この Book クラスに対して、次の3通りを行うメソッドをそれぞれ作成しよう。

  • オブジェクトを、(デバッグ出力用の) 文字列に変換
  • オブジェクトを、XML に変換
  • オブジェクトを、HTML に変換
(デバッグ出力用の) 文字列に変換

これは、単純に文字列に変換するメソッドだ。Book クラスで ToString() メソッドをオーバーライドすることにする。

using System.Text;

// テスト用のクラス
public sealed class Book
{
    public string Title { get; set; }
    public int    Price { get; set; }

    // 文字列へ変換するメソッド ToString() をオーバーライド
    public override string ToString()
    {
        return new StringBuilder()                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
                   .Append("Title: ").Append(Title)
                   .Append(", "     )
                   .Append("Price: ").Append(Price)
                   .ToString();

        // または:
        //return string.Format("Title: {0}, Price: {1}", Title, Price);
    }
}
XML や HTML への変換

次は、XML や HTML に変換するメソッドだ。

XML への変換の拡張メソッドと HTML の table への変換の拡張メソッドを、それぞれ作成してみる。

using System.Collections.Generic;
using System.Text;

public static class BookExtensions
{
    // XML へ変換
    public static string ToXml(this IEnumerable<Book> @this)
    {
        var stringBuilder = new StringBuilder();
        stringBuilder.Append("<?xml version=\"1.0\"?>");
        @this.ForEach(
            book =>
               stringBuilder.Append("<Book>" )
                            .Append("<Title>").Append(book.Title).Append("</Title>")
                            .Append("<Price>").Append(book.Price).Append("</Price>")
                            .Append("</Book>")
        );
        return stringBuilder.ToString();
    }

    // HTML の table へ変換
    public static string ToHtmlTable(this IEnumerable<Book> @this)
    {
        var stringBuilder = new StringBuilder();
        stringBuilder.Append("<table><thead><tr><th>Title</th><th>Price</th></tr></thead><tbody>");
        @this.ForEach(
            book => stringBuilder.Append("<tr><td>").Append(book.Title).Append("</td>"     )
                                 .Append(    "<td>").Append(book.Price).Append("</td></tr>"));
        return stringBuilder.Append("</tbody></table>").ToString();
    }
}

上のコードで呼び出している ForEach メソッドは別クラスに拡張メソッドとして用意した。

using System;
using System.Collections.Generic;

// ForEach 用
public static class EnumerableExtensions
{
    public static void ForEach<T>(this IEnumerable<T> @this, Action<T> action)
    {
        foreach (var item in @this)
            action(item);
    }
}
文字列を生成する静的な例のテスト

それでは、これらのメソッドを呼び出してみよう。

using System;
using System.Reflection;

static class Program
{
    static void Main()
    {
        文字列への変換のテスト();
        XMLへの変換のテスト   ();
        HTMLへの変換のテスト  ();
    }

    static void 文字列への変換のテスト()
    {
        Console.WriteLine("【{0}】", MethodBase.GetCurrentMethod().Name); // メソッド名を表示

        var book = new Book { Title = "Metaprogramming C#", Price = 3200 };

        Console.WriteLine(book.ToString());
    }

    static void XMLへの変換のテスト()
    {
        Console.WriteLine("【{0}】", MethodBase.GetCurrentMethod().Name); // メソッド名を表示

        var books = new[] {
            new Book { Title = "Metaprogramming C#", Price = 3200 },
            new Book { Title = "Metaprogramming VB", Price = 2100 },
            new Book { Title = "Metaprogramming F#", Price = 4300 }
        };

        Console.WriteLine(books.ToXml());
    }

    static void HTMLへの変換のテスト()
    {
        Console.WriteLine("【{0}】", MethodBase.GetCurrentMethod().Name); // メソッド名を表示

        var books = new[] {
            new Book { Title = "Metaprogramming C#", Price = 3200 },
            new Book { Title = "Metaprogramming VB", Price = 2100 },
            new Book { Title = "Metaprogramming F#", Price = 4300 }
        };

        Console.WriteLine(books.ToHtmlTable());
    }
}

実行結果は次のようになる。

【文字列への変換のテスト】
Title: Metaprogramming C#, Price: 3200
【XMLへの変換のテスト】
<?xml version="1.0"?><Book><Title>Metaprogramming C#</Title><Price>3200</Price></Book><Book><Title>Metaprogramming VB</Title><Pri
ce>2100</Price></Book><Book><Title>Metaprogramming F#</Title><Price>4300</Price></Book>
【HTMLへの変換のテスト】
<table><thead><tr><th>Title</th><th>Price</th></tr></thead><tbody><tr><td>Metaprogramming C#</td><td>3200</td></tr><tr><td>Metapr
ogramming VB</td><td>2100</td></tr><tr><td>Metaprogramming F#</td><td>4300</td></tr></tbody></table>

単純なデバッグ用の文字列と XML、HTML が出力されている。

オブジェクトから文字列を生成する動的な例 (リフレクションを使った例)

上記の静的な方法には、大きな問題点がある。これでは、対象とする型ごとに手でコードを書かなければならない。

上では Book クラスを用いて試しているが、Book クラス以外のクラスを対象とする場合には、また同様の3つのメソッドを作る必要があるだろう。

そこで、そのようなことにならないように、今度は、型情報から動的に処理を行うようにしてみよう。リフレクションを使ってみる。

リフレクションを使ってオブジェクトを動的に文字列や XML、HTML に変換

実際のコードは次のようになるだろう。静的な場合と比べてやや複雑になる。

using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

// リフレクションによる型によらない各種文字列への変換 (リフレクション版)
public static class ToStringByReflectionExtensions
{
    // 文字列へ変換 (リフレクション版)
    public static string ToStringByReflection<T>(this T @this)
    {
        // リフレクションで public なインスタンス プロパティの情報を全て取得
        var properties = @this.GetType().GetProperties(bindingAttr: BindingFlags.Instance | BindingFlags.Public);
        // LINQ でプロパティの情報の集まりから、読み込み可能なものに絞り込み、名前と値から作った文字列の集まりにする
        var textCollection = properties
                             .Where(property => property.CanRead)
                             .Select(property => string.Format("{0}: {1}", property.Name, property.GetValue(@this, null)));
        // string.Join で連結
        return string.Join(separator: ", ", values: textCollection);
    }

    // XML へ変換 (リフレクション版)
    public static string ToXmlByReflection<T>(this IEnumerable<T> @this)
    {
        var stringBuilder = new StringBuilder();
        stringBuilder.Append("<?xml version=\"1.0\"?>");

        var itemType      = typeof(T);

        // 要素の型のプロパティの一覧を取得
        var properties    = itemType.GetProperties(BindingFlags.Instance | BindingFlags.Public)
                                    .Where(property => property.CanRead);

        @this.ForEach(
            // 要素毎の XML
            item => {
                stringBuilder.Append("<" ).Append(itemType.Name).Append(">");
                properties.ForEach(
                    // 要素のプロパティ毎の XML
                    property => stringBuilder.Append("<" ).Append(property.Name).Append(">")
                                             .Append(property.GetValue(item))
                                             .Append("</").Append(property.Name).Append(">")
                );
                stringBuilder.Append("</").Append(itemType.Name).Append(">");
            }
        );
        return stringBuilder.ToString();
    }

    // HTML の table へ変換 (リフレクション版)
    public static string ToHtmlTableByReflection<T>(this IEnumerable<T> @this)
    {
        var itemType      = typeof(T);

        // 要素の型のプロパティの一覧を取得
        var properties    = itemType.GetProperties(BindingFlags.Instance | BindingFlags.Public)
                                    .Where(property => property.CanRead);
        if (properties.Count() == 0)
            return string.Empty;

        var stringBuilder = new StringBuilder();

        // table のヘッダー部の追加
        stringBuilder.Append("<table><thead><tr>");
        properties.ForEach(property => stringBuilder.Append("<th>").Append(property.Name).Append("</th>"));
        stringBuilder.Append("</tr></thead>");

        // table の本体を追加
        stringBuilder.Append("<tbody>");
        @this.ForEach(
            item => {
                stringBuilder.Append("<tr>");
                properties.Where(property => property.CanRead)
                          .ForEach(
                               property =>
                                   stringBuilder.Append("<td>").Append(property.GetValue(item)).Append("</td>")
                           );
                stringBuilder.Append("</tr>");
            }
        );
        return stringBuilder.Append("</tbody></table>").ToString();
    }
}

コードでは、ジェネリック プログラミングによって型への依存を無くしている。また、リフレクションで動的に型情報を利用することでも対象とするオブジェクトの型に依存しない処理を実現している。

こうすることにより、Book 以外のクラスのオブジェクトにも用いることができ、対象とするオブジェクトの型ごとにコードを書かなくて済む訳だ。

文字列を生成するリフレクションによる動的な例のテスト

こちらも呼び出してみよう。先の Program クラスを少し書き換えてリフレクション版のメソッドを呼ぶようにしてみる。

using System;
using System.Reflection;

static class Program
{
    static void Main()
    {
        文字列への変換のテスト();
        XMLへの変換のテスト   ();
        HTMLへの変換のテスト  ();
    }

    static void 文字列への変換のテスト()
    {
        Console.WriteLine("【{0}】", MethodBase.GetCurrentMethod().Name); // メソッド名を表示

        var book = new Book { Title = "Metaprogramming C#", Price = 3200 };

        Console.WriteLine(book.ToStringByReflection());
    }

    static void XMLへの変換のテスト()
    {
        Console.WriteLine("【{0}】", MethodBase.GetCurrentMethod().Name); // メソッド名を表示

        var books = new[] {
            new Book { Title = "Metaprogramming C#", Price = 3200 },
            new Book { Title = "Metaprogramming VB", Price = 2100 },
            new Book { Title = "Metaprogramming F#", Price = 4300 }
        };

        Console.WriteLine(books.ToXmlByReflection());
    }

    static void HTMLへの変換のテスト()
    {
        Console.WriteLine("【{0}】", MethodBase.GetCurrentMethod().Name); // メソッド名を表示

        var books = new[] {
            new Book { Title = "Metaprogramming C#", Price = 3200 },
            new Book { Title = "Metaprogramming VB", Price = 2100 },
            new Book { Title = "Metaprogramming F#", Price = 4300 }
        };

        Console.WriteLine(books.ToHtmlTableByReflection());
    }
}

実行してみると、結果は次のように静的な場合と変わらない。

【文字列への変換のテスト】
Title: Metaprogramming C#, Price: 3200
【XMLへの変換のテスト】
<?xml version="1.0"?><Book><Title>Metaprogramming C#</Title><Price>3200</Price></Book><Book><Title>Metaprogramming VB</Title><Pri
ce>2100</Price></Book><Book><Title>Metaprogramming F#</Title><Price>4300</Price></Book>
【HTMLへの変換のテスト】
<table><thead><tr><th>Title</th><th>Price</th></tr></thead><tbody><tr><td>Metaprogramming C#</td><td>3200</td></tr><tr><td>Metapr
ogramming VB</td><td>2100</td></tr><tr><td>Metaprogramming F#</td><td>4300</td></tr></tbody></table>

まとめ

ここまで、オブジェクトを文字列に変換する静的な方法と動的な方法を示した。動的な方法では、リフレクションを使うことにより柔軟な処理を行うことができた。

しかし、リフレクションには、前回試したように実行速度が遅い、という欠点がある。

次回からは、メタプログラミングを行い、動的にコードを生成する方法を試していきたい。

2013年11月08日

[C#][.NET] メタプログラミング入門 - 応用編 - オブジェクトの文字列変換のメタプログラミング

Metasequoia

※ 「[C#][.NET] メタプログラミング入門 - 応用編 - オブジェクトの文字列変換を静的/動的に行う」の続き。

前回は、メタプログラミングによらない静的な方法とリフレクションによる動的な方法で、オブジェクトを文字列に変換した。

今回から、メタプログラミングによる動的コード生成を試していきたい。

■ 静的な方法とリフレクションによる動的な方法の問題点

メタプログラミングによる動的コード生成を試す前に、前回試した2つの方法の問題点を再確認しておこう。

静的な方法の問題点

前回は、Book クラスをデバッグ用文字列、XML、HTML に変換する静的なプログラミングの例をあげた。

対象とする型ごとに手でコードを書かなければならない、というのが問題点だった。

リフレクションによる動的な方法の問題点

前回は、ジェネリック プログラミングとリフレクションによって対象とするオブジェクトの型への依存を無くすことで、汎用的なプログラムを書くことができた。

だが、リフレクションは、動的に型情報を取得して利用する為に、実行速度が遅いという問題があった。

■ メタプログラミングによる動的コード生成による方法

そこで考えられるアプローチが、メタプログラミングだ。

つまり、静的なプログラミングの「対象とする型ごとに手でコードを書かなければならない」の「手で」の部分を、プログラムで生成してしまえば良いのではないか、ということだ。 「対象とする型ごとにプログラムでコードを生成し、それを用いる」のだ。

コード生成自体は、対象とするオブジェクトの型に依存せずに行う。ジェネリック プログラミングとリフレクションも用いることになるだろう。

しかし、一旦生成してしまえば、それを実行するのは静的なコードと変わらない。

[C#][.NET] メタプログラミング入門 - Add メソッドのパフォーマンスの比較」で調べたように、文字列変換の度にコード生成していたのでは、とても時間が掛かってしまう。「[C#][.NET] メタプログラミング入門 - メソッド呼び出しのパフォーマンスの比較」で行ったように、キャッシュを用いる必要があるだろう。生成したコードを対象の型毎にキャッシュにしまうことにしよう。

次回から、これまで行った3つの動的コード生成の方法で、オブジェクトの文字列への変換をやっていく。また、実行時のパフォーマンスに関しても調べることにしたい。

2013年11月09日

[C#][.NET] メタプログラミング入門 - 目次

Metasequoia

2013年11月10日

[C#][.NET] メタプログラミング入門 - 応用編 - オブジェクトの文字列変換のメタプログラミング (Reflection.Emit 編)

Metasequoia

※ 「[C#][.NET] メタプログラミング入門 - 応用編 - オブジェクトの文字列変換のメタプログラミング」の続き。

Reflection.Emit によるメタプログラミング

それでは、Reflection.Emit を用いて文字列生成を行う例を見ていこう。

題材は 「[C#][.NET] メタプログラミング入門 - 応用編 - オブジェクトの文字列変換を静的/動的に行う」の中の「(デバッグ用の) 文字列に変換」だ。

今回も同様に、例えば、次のようなクラスのオブジェクトを文字列に変換する。

// テスト用のクラス
public sealed class Book
{
    public string Title { get; set; }
    public int    Price { get; set; }
}
Reflection.Emit によって生成したいプログラムの例

この Book クラスの場合、プログラムによって生成したいプログラムは、例えば、次のようなものだ。

    // 動的に作りたいコードの例:
    public static string ToString(Book book)
    {
        StringBuilder stringBuilder;
        stringBuilder       = new StringBuilder();
        stringBuilder.Append("Title: ");
        var           title = book.Title;
        stringBuilder.Append(title);
        stringBuilder.Append(", ");
        stringBuilder.Append("Price: ");
        var           price = book.Price;
        stringBuilder.Append(price);
        return stringBuilder.ToString();
    }
ILSpy を使って IL を見る

今回も、「[C#][.NET] メタプログラミング入門 - Reflection.Emit による Add メソッドの動的生成」でやったように、ILSpy を使ってこのコードの IL (Intermediate Language) を表示し、参考にしよう。

上記 ToString(Book book) メソッドを含むプログラムをビルドし (Release ビルド)、ILSpy で開くと次のように表示される。

ILSpy で Call メソッドの IL を見る
ILSpy で ToString メソッドの IL を見る

これを参考に IL を生成するコードを書いていこう。

これから書くプログラムは生成する方で、このプログラムで生成されるプログラムと混同しないように注意する必要がある。例えば、このプログラム生成プログラムは、Book クラスに依存しないように書く必要がある。動的で汎用的なものだ。これに対して、生成されるコードの方は、対象とするオブジェクトのクラス (例えば Book クラス) 専用の静的で高速なものだ。

表にしてみよう。

プログラム 手書き/生成 汎用性 プログラムの動作 動作速度
文字列変換プログラムを生成するプログラム 手書き 対象とするオブジェクトのクラス (例えば Book クラス) に依存せず汎用的 動的 遅い
文字列変換プログラム プログラムによって生成 対象とするオブジェクトのクラス (例えば Book クラス) 専用 静的 速い
Reflection.Emit によるオブジェクトの文字列への変換プログラム生成プログラム

では、IL を参考にプログラム生成プログラムを作ろう。

using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;

// ToString メソッド生成器 (Reflection.Emit 版)
public static class ToStringGeneratorByEmit
{
    // メソッドを生成
    public static Func<T, string> Generate<T>()
    {
        // DynamicMethod
        var method = new DynamicMethod(
            name          : "ToString"         ,
            returnType    : typeof(string)     ,
            parameterTypes: new[] { typeof(T) }
        );

        // 引数 item 生成用
        var item      = method.DefineParameter(position: 1, attributes: ParameterAttributes.In, parameterName: "item");
        // ILGenerator
        var generator = method.GetILGenerator();
        Generate(generator, typeof(T));

        // 動的にデリゲートを生成
        return (Func<T, string>)method.CreateDelegate(delegateType: typeof(Func<T, string>));
    }

    // メソッドを生成
    static void Generate(ILGenerator ilGenerator, Type targetType)
    {
        // 対象とする型の全プロパティ情報を取得
        var properties = targetType.GetProperties(bindingAttr: BindingFlags.Public | BindingFlags.Instance).Where(property => property.CanRead).ToArray();
        if (properties.Length > 0) {
            // 動的に作りたいコードの例 (実際のコードは targetType による):
            //public static string ToText(Book book)
            //{
            //    StringBuilder stringBuilder;
            //    stringBuilder = new StringBuilder();
            //    stringBuilder.Append("Title: ");
            //    var title     = book.Title;
            //    stringBuilder.Append(title);
            //    stringBuilder.Append(", ");
            //    stringBuilder.Append("Price: ");
            //    var price     = book.Price;
            //    stringBuilder.Append(price);
            //    return stringBuilder.ToString();
            //}

            // 動的に作りたい IL:
            // newobj instance void [mscorlib]System.Text.StringBuilder::.ctor()
            // stloc.0
            // ldloc.0

            var stringBuilderType = typeof(StringBuilder);
            // 「ローカルに StringBuilder を宣言する」コードを追加
            ilGenerator.DeclareLocal(localType: stringBuilderType);
            // 「StringBuilder のコンストラクターを使ってインスタンスを new する」コードを追加
            ilGenerator.Emit(opcode: OpCodes.Newobj, con: stringBuilderType.GetConstructor(Type.EmptyTypes));
            // 「現在の値 (StringBuilder のインスタンスへの参照) をスタックからポップし、ローカル変数に格納する」コードを追加
            ilGenerator.Emit(opcode: OpCodes.Stloc_0);
            // 「ローカル変数 (StringBuilder のインスタンスへの参照) をスタックに読み込む」コードを追加
            ilGenerator.Emit(opcode: OpCodes.Ldloc_0);

            // プロパティ毎に文字列に変換するコードを生成
            for (var index = 0; index < properties.Length; index++)
                GenerateForEachProperty(ilGenerator: ilGenerator, property: properties[index], needsSeparator: index < properties.Length - 1);

            // 「スタックからポップする」コードを追加
            ilGenerator.Emit(opcode: OpCodes.Pop);
            // 「ローカル変数 (StringBuilder のインスタンスへの参照) をスタックに読み込む」コードを追加
            ilGenerator.Emit(opcode: OpCodes.Ldloc_0);
            // 「StringBuilder のバーチャル メソッド ToString を呼ぶ」コードを追加
            ilGenerator.Emit(opcode: OpCodes.Callvirt, meth: stringBuilderType.GetMethod(name: "ToString", types: Type.EmptyTypes));
        } else {
            // 動的に作りたい IL:
            // ldstr ""

            // 「空の文字列をプッシュする」コードを追加
            ilGenerator.Emit(opcode: OpCodes.Ldstr, str: string.Empty);
        }
        // 動的に作りたい IL:
        // ret

        // 「リターンする」コードを追加
        ilGenerator.Emit(opcode: OpCodes.Ret);
    }

    // 文字列が引数の StringBuilder.Append メソッドが何度も使われるため static メンバーに
    static readonly MethodInfo appendMethod = typeof(StringBuilder).GetMethod(name: "Append", types: new[] { typeof(string) });

    // プロパティ毎に文字列に変換するコードを生成
    static void GenerateForEachProperty(ILGenerator ilGenerator, PropertyInfo property, bool needsSeparator)
    {
        // 動的に作りたいコードの例 (実際のコードは property による):
        // stringBuilder.Append("Title: ");
        // var title = item.Title;
        // stringBuilder.Append(title);

        // 動的に作りたい IL の例:
        // ldstr "Title: "
        // callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
        // ldarg.0
        // callvirt instance string Book::get_Title()
        // callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)

        // 「プロパティ名 + ": " をプッシュする」コードを追加
        ilGenerator.Emit(opcode: OpCodes.Ldstr, str: property.Name + ": ");
        // 「文字列が引数の StringBuilder.Append を呼ぶ」コードを追加
        ilGenerator.Emit(opcode: OpCodes.Callvirt, meth: appendMethod);

        // 「インスタンスへの参照をプッシュする」コードを追加
        ilGenerator.Emit(opcode: OpCodes.Ldarg_0);
        var propertyGetMethod = property.GetGetMethod(); // 渡されたプロパティの get メソッド
        // 「渡されたプロパティの get メソッドを呼ぶ」コードを追加
        ilGenerator.Emit(propertyGetMethod.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, propertyGetMethod);

        var propertyGetMethodReturnType = propertyGetMethod.ReturnType; // 渡されたプロパティの get メソッドの戻り値の型
        //  渡されたプロパティの get メソッドの戻り値の型が引数の StringBuilder.Append メソッド
        var typedAppendMethod = typeof(StringBuilder).GetMethod(name: "Append", types: new[] { propertyGetMethodReturnType });

        // 型が違っていて、値型だった場合はボクシングするコードを追加
        if (typedAppendMethod.GetParameters()[0].ParameterType != propertyGetMethodReturnType &&
            propertyGetMethodReturnType.IsValueType)
            ilGenerator.Emit(opcode: OpCodes.Box, cls: propertyGetMethodReturnType);

        // 「渡されたプロパティの get メソッドの戻り値の型が引数の StringBuilder.Append メソッドを呼ぶ」コードを追加
        ilGenerator.Emit(opcode: OpCodes.Callvirt, meth: typedAppendMethod);

        if (needsSeparator) {
            // 動的に作りたいコード:
            // stringBuilder.Append(", ");

            // 動的に作りたい IL:
            // ldstr ", "
            // callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)

            // 「", " をプッシュする」コードを追加
            ilGenerator.Emit(opcode: OpCodes.Ldstr, str: ", ");
            // 「文字列が引数の StringBuilder.Append メソッドを呼ぶ」コードを追加
            ilGenerator.Emit(opcode: OpCodes.Callvirt, meth: appendMethod);
        }
    }
}
Reflection.Emit によるオブジェクトの文字列への変換 (キャッシュ無し)

それでは、これを使って変換メソッドを作ろう。先ずは、単純な「メソッドをキャッシュをせず、毎回コード生成するもの」から。

// 改良前 (メソッドのキャッシュ無し)
public static class ToStringByEmitExtensions初期型
{
    // ToString に代わる拡張メソッド (Reflection.Emit 版)
    public static string ToStringByEmit初期型<T>(this T @this)
    {
        // 動的にメソッドを生成し、それを実行
        return ToStringGeneratorByEmit.Generate<T>()(@this);
    }
}
メソッドのキャッシュ無し版の動作テスト

次のような簡単なプログラムで動作させてみよう。

using System;

static class Program
{
    static void Main()
    {
        var book = new Book { Title = "Metaprogramming C#", Price = 3200 };

        Console.WriteLine(book.ToStringByEmit初期型());
    }
}

実行結果は次のようになる。

Title: Metaprogramming C#, Price: 3200

正しく動作する。

生成したメソッドのキャッシュ

上の "ToStringByEmit初期型" メソッドでは、呼び出される度にメソッドを生成している。 これでは、その度に時間が掛かる。 一度生成したメソッドは、キャッシュしておくことにしたい。

型毎に1つずつメソッドが必要なので、型毎に最初にメソッドが必要になったときにだけ生成し、それをキャッシュしておくことにしよう。 2回目からは、キャッシュにあるメソッドを使用するのだ。

この為、先にメソッド キャッシュ クラスを作成しよう。 次のようなものだ。Dictionary の中に型情報をキーとしてメソッドを格納する。 このクラスでは、ジェネリックを使い型に依存しないようにする。

using System;
using System.Collections.Generic;

//  生成したメソッド用のキャッシュ
public class MethodCache<TResult>
{
    // メソッド格納用
    readonly Dictionary<Type, Delegate> methods = new Dictionary<Type, Delegate>();

    // メソッドの呼び出し (メソッド生成用のメソッドを引数 generator として受け取る)
    public TResult Call<T>(T item, Func<Func<T, TResult>> generator)
    {
        return Get<T>(generator)(item); // キャッシュにあるメソッドを呼び出す
    }

    // メソッドをキャッシュを介して取得 (メソッド生成用のメソッドを引数 generator として受け取る)
    Func<T, TResult> Get<T>(Func<Func<T, TResult>> generator)
    {
        var      targetType = typeof(T);
        Delegate method;
        if (!methods.TryGetValue(key: targetType, value: out method)) { // キャッシュに無い場合は
            method = generator();                                       // 動的にメソッドを生成して
            methods.Add(key: targetType, value: method);                // キャッシュに格納
        }
        return (Func<T, TResult>)method;
    }
}
Reflection.Emit によるオブジェクトの文字列への変換 (キャッシュ有り)

では、キャッシュを行う「オブジェクトの文字列への変換」を作成しよう。

上のメソッド キャッシュ クラス MethodCache を利用して、次のようにする。

// 改良後 (メソッドのキャッシュ有り)
public static class ToStringByEmitExtensions改
{
    // 生成したメソッドのキャッシュ
    static readonly MethodCache<string> toStringCache = new MethodCache<string>();

    // ToString に代わる拡張メソッド (Reflection.Emit 版)
    public static string ToStringByEmit改<T>(this T @this)
    {
        // キャッシュを利用してメソッドを呼ぶ
        return toStringCache.Call(item: @this, generator: ToStringGeneratorByEmit.Generate<T>);
    }
}
メソッドのキャッシュ有り版の動作テスト

こちらも動作させてみよう。

using System;

static class Program
{
    static void Main()
    {
        var book = new Book { Title = "Metaprogramming C#", Price = 3200 };

        Console.WriteLine(book.ToStringByEmit改());
    }
}

勿論、実行結果は同じだ。

Title: Metaprogramming C#, Price: 3200

まとめ

今回は、Reflection.Emit を用いて、動的に「オブジェクトを文字列に変換する」メソッドを生成するプログラムを作成した。

次回は、式木を使って同様のことを行う。

2013年11月11日

[C#][.NET] プログラミング C# (開発関連) - 目次

プログラミング C# (開発関連) - 目次

その他 C#関連

Windows ストア アプリ開発

2013年11月12日

[C#][.NET][式木] メタプログラミング入門 - 応用編 - オブジェクトの文字列変換のメタプログラミング (式木編)

Metasequoia

※ 「[C#][.NET] メタプログラミング入門 - 応用編 - オブジェクトの文字列変換のメタプログラミング (Reflection.Emit 編)」の続き。

式木によるメタプログラミング

Reflection.Emit の次は、式木によって文字列生成を行う例を見ていこう。

題材は同じく 「[C#][.NET] メタプログラミング入門 - 応用編 - オブジェクトの文字列変換を静的/動的に行う」の中の「(デバッグ用の) 文字列に変換」だ。

今回もまた同様に、例えば、次のようなクラスのオブジェクトを文字列に変換する。

// テスト用のクラス
public sealed class Book
{
    public string Title { get; set; }
    public int    Price { get; set; }
}
式木によって生成したいプログラムの例

この Book クラスの場合、プログラムによって生成したい「文字列変換を行うラムダ式」は、例えば、次のようなものだ。

    // 動的に作りたいラムダ式の例 (実際のコードは targetType による):
    item => new StringBuilder().Append("Title: ").Append(item.Title)
                               .Append(", ")
                               .Append("Price: ").Append(item.Price)
                               .ToString()

このようなラムダ式を、これまで何度か行ったように、動的に組み立てることにしよう。

但し、上のラムダ式は、Book クラスの場合の例で、対象とするオブジェクトのクラスによって、必要なラムダ式は異なる。

そのため、ラムダ式を生成する部分は、リフレクションを用いて動的に行うことにする。 また、ジェネリックを用いて、型に依存しないようにする。

生成したラムダ式は、コンパイルしてデリゲートにする。 デリゲートになってしまえば、それは静的なコードと同様に動作させることができる。

そして、今回もデリゲートをキャッシュしておくことで、文字列変換を行う度に毎回デリゲートを動的に生成しなくても良いようにしよう。

式木によるオブジェクトの文字列への変換プログラム生成プログラム

では、上のようなラムダ式を生成し、コンパイルしてデリゲートとするプログラムを作ろう。

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

// ToString メソッド生成器 (式木版)
public static class ToStringGeneratorByExpression
{
    // メソッドを生成
    public static Func<T, string> Generate<T>()
    {
        // 対象とする型の全プロパティ情報を取得
        var            properties                  = typeof(T).GetProperties(bindingAttr: BindingFlags.Public | BindingFlags.Instance)
                                                              .Where(property => property.CanRead)
                                                              .ToArray();
        var parameterExpression = Expression.Parameter(typeof(T), "item");
        LambdaExpression lambdaExpression;
        if (properties.Length > 0) {
            // 動的に作りたいラムダ式の例 (実際のコードは targetType による):
            // item => new StringBuilder().Append("Title: ").Append(item.Title)
            //                            .Append(", ")
            //                            .Append("Price: ").Append(item.Price)
            //                            .ToString()

            var        callGetTypeOfItemExpression = Expression.Call(
                                                         instance: parameterExpression           ,
                                                         method  : typeof(T).GetMethod("GetType")
                                                     );
            Expression stringBuilderExpression     = Expression.New(type: typeof(StringBuilder));

            // プロパティ毎に文字列に変換する式を生成
            for (var index = 0; index < properties.Length; index++)
                stringBuilderExpression = GenerateForEachProperty(
                    property                   : properties[index]            , 
                    expression                 : stringBuilderExpression      ,
                    parameterExpression        : parameterExpression          ,
                    callGetTypeOfItemExpression: callGetTypeOfItemExpression  ,
                    needsSeparator             : index < properties.Length - 1
                );

            stringBuilderExpression                = stringBuilderExpression.CallMethod(typeof(StringBuilder).GetMethod("ToString", Type.EmptyTypes));
            lambdaExpression                       = Expression.Lambda(stringBuilderExpression, parameterExpression);
        } else {
            // 動的に作りたいラムダ式の例:
            // item => string.Empty
            lambdaExpression = Expression.Lambda(Expression.Constant(string.Empty, typeof(string)),
                                                 parameterExpression);
        }
        // ラムダ式をコンパイルしてデリゲートとして返す
        return (Func<T, string>)lambdaExpression.Compile();
    }

    // 何度も使われるメソッドは static メンバーに
    static readonly MethodInfo stringBuilderAppendStringMethod = typeof(StringBuilder).GetMethod("Append"     , new[] { typeof(string) });
    static readonly MethodInfo stringBuilderAppendObjectMethod = typeof(StringBuilder).GetMethod("Append"     , new[] { typeof(object) });
    static readonly MethodInfo typeGetPropertyMethod           = typeof(Type         ).GetMethod("GetProperty", new[] { typeof(string) });
    static readonly MethodInfo propertyInfoGetValueMethod      = typeof(PropertyInfo ).GetMethod("GetValue"   , new[] { typeof(object) });

    // プロパティ毎に文字列に変換する式を生成
    static Expression GenerateForEachProperty(PropertyInfo property, Expression expression, ParameterExpression parameterExpression, MethodCallExpression callGetTypeOfItemExpression, bool needsSeparator)
    {
        // 例えば、item.Title の式を生成 (実際のコードは property による)
        var callGetValueExpression = callGetTypeOfItemExpression
                                     .CallMethod(typeGetPropertyMethod         , Expression.Constant(property.Name)        )
                                     .CallMethod(propertyInfoGetValueMethod    , parameterExpression                       );

        // 例えば、stringBuilder.Append("Title: ").Append(item.Title) の式を生成 (実際のコードは property による)
        expression                 = expression
                                     .CallMethod(stringBuilderAppendStringMethod, Expression.Constant(property.Name + ": "))
                                     .CallMethod(stringBuilderAppendObjectMethod, callGetValueExpression                   );

        // 必要なら、stringBuilder.Append(", ") の式を生成
        if (needsSeparator)
            expression = expression.CallMethod(stringBuilderAppendStringMethod, Expression.Constant(", "));

        return expression;
    }

    // Expression.Call をメソッドチェーンにするための拡張メソッド
    static Expression CallMethod(this Expression @this, MethodInfo method, params Expression[] arguments)
    {
        return Expression.Call(@this, method, arguments);
    }
}
式木によるオブジェクトの文字列への変換 (キャッシュ無し)

それでは、これを使って変換メソッドを作ろう。先ずは、単純な「メソッドをキャッシュをせず、毎回コード生成するもの」から。

// 改良前 (メソッドのキャッシュ無し)
public static class ToStringByExpressionExtensions初期型
{
    // ToString に代わる拡張メソッド (式木版)
    public static string ToStringByExpression初期型<T>(this T @this)
    {
        // 動的にメソッドを生成し、それを実行
        return ToStringGeneratorByExpression.Generate<T>()(@this);
    }
}
メソッドのキャッシュ無し版の動作テスト

今回も、次のような簡単なプログラムで動作させてみよう。

using System;

static class Program
{
    static void Main()
    {
        var book = new Book { Title = "Metaprogramming C#", Price = 3200 };

        Console.WriteLine(book.ToStringByExpression初期型());
    }
}

実行結果は次のようになる。

Title: Metaprogramming C#, Price: 3200

正しく動作する。

生成したメソッドのキャッシュ

前回同様、キャッシュを利用してみよう。

前回作成したメソッド キャッシュ クラスは次のようなものだった。

using System;
using System.Collections.Generic;

//  生成したメソッド用のキャッシュ
public class MethodCache<TResult>
{
    // メソッド格納用
    readonly Dictionary<Type, Delegate> methods = new Dictionary<Type, Delegate>();

    // メソッドの呼び出し (メソッド生成用のメソッドを引数 generator として受け取る)
    public TResult Call<T>(T item, Func<Func<T, TResult>> generator)
    {
        return Get<T>(generator)(item); // キャッシュにあるメソッドを呼び出す
    }

    // メソッドをキャッシュを介して取得 (メソッド生成用のメソッドを引数 generator として受け取る)
    Func<T, TResult> Get<T>(Func<Func<T, TResult>> generator)
    {
        var      targetType = typeof(T);
        Delegate method;
        if (!methods.TryGetValue(key: targetType, value: out method)) { // キャッシュに無い場合は
            method = generator();                                       // 動的にメソッドを生成して
            methods.Add(key: targetType, value: method);                // キャッシュに格納
        }
        return (Func<T, TResult>)method;
    }
}
式木によるオブジェクトの文字列への変換 (キャッシュ有り)

では、キャッシュを行う「オブジェクトの文字列への変換」を作成しよう。

上のメソッドキャッシュ クラス MethodCache を利用して、次のようにする。

// 改良後 (メソッドのキャッシュ有り)
public static class ToStringByExpressionExtensions改
{
    // 生成したメソッドのキャッシュ
    static readonly MethodCache<string> toStringCache = new MethodCache<string>();

    // ToString に代わる拡張メソッド (式木版)
    public static string ToStringByExpression改<T>(this T @this)
    {
        // キャッシュを利用してメソッドを呼ぶ
        return toStringCache.Call(item: @this, generator: ToStringGeneratorByExpression.Generate<T>);
    }
}
メソッドのキャッシュ有り版の動作テスト

こちらも動作させてみよう。

using System;

static class Program
{
    static void Main()
    {
        var book = new Book { Title = "Metaprogramming C#", Price = 3200 };

        Console.WriteLine(book.ToStringByExpression改());
    }
}

実行結果は同じだ。

Title: Metaprogramming C#, Price: 3200

まとめ

今回は、前回の Reflection.Emit を用いた方法に続き、式木を使って動的に「オブジェクトを文字列に変換する」メソッドを生成するプログラムを作成した。

次回は、Roslyn による方法だ。

2013年11月13日

[C#][.NET][Roslyn] メタプログラミング入門 - 応用編 - オブジェクトの文字列変換のメタプログラミング (Roslyn 編)

Metasequoia

※ 「[C#][.NET][式木] メタプログラミング入門 - 応用編 - オブジェクトの文字列変換のメタプログラミング (式木編)」の続き。

Roslyn によるメタプログラミング

Roslyn によるメタプログラミングに関しては、以前、次にあげる記事で扱った。参考にしてほしい。

今回も、題材は同じく 「[C#][.NET] メタプログラミング入門 - 応用編 - オブジェクトの文字列変換を静的/動的に行う」の中の「(デバッグ用の) 文字列に変換」だ。

例えば、次のようなクラスのオブジェクトを文字列に変換する。

// テスト用のクラス
public sealed class Book
{
    public string Title { get; set; }
    public int    Price { get; set; }
}
Roslyn に渡す C# のソースコード

前回は、式木でラムダ式を組み立て、それをコンパイルすることにより、デリゲートを生成した。

Book クラスの場合を例にあげ、プログラムによって生成したい「文字列変換を行うラムダ式」として次のものを想定したのだった。

    // 動的に作りたいラムダ式の例 (実際のコードは targetType による):
    item => new StringBuilder().Append("Title: ").Append(item.Title)
                               .Append(", ")
                               .Append("Price: ").Append(item.Price)
                               .ToString()

対象とするオブジェクトのクラスによってラムダ式が異なるため、式木は動的に生成した。

今回は、「文字列変換を行うラムダ式」の C# のソースコードを動的に作成し、そのソースコードから Roslyn を用いてデリゲートを生成することにしよう。

先ず、Roslyn を使う前に、上のようなラムダ式の C# のソースコードを、リフレクションを用いて動的に作成する。

対象とするオブジェクトとその型から、C# のソースコードを文字列として作成するメソッドを書く。 この時、リフレクションとジェネリックを用いて、型に依存しないようにする。

// ToString メソッド生成器 (Roslyn 版)
public static class ToStringGeneratorByRoslyn
{
    // ToString() メソッドの C# のソースコードを作成する
    public static string CreateCodeOfToStringByRoslyn<T>()
    {
        // 動的に作りたい C# のソースコードの例 (実際のコードは typeof(T) による):
        // item => new StringBuilder().Append("Title: ").Append(item.Title)
        //                            .Append(", ")
        //                            .Append("Price: ").Append(item.Price)
        //                            .ToString()

        var bodyCode = "new StringBuilder()" +
                       string.Join(".Append(\", \")",
                                   typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public)
                                            .Where (property => property.CanRead)
                                            .Select(property => string.Format(".Append(\"{0}: \").Append(item.{0})", property.Name))) +
                       ".ToString()";
        return string.Format("(Func<{0}, string>)(item => {1})", typeof(T).FullName, bodyCode);
    }
}

これで正しい「文字列変換を行うラムダ式」の C# のソースコードが文字列として作成されるか、Book クラスの場合で試してみよう。

using System;

static class Program
{
    static void Main()
    {
        var code = ToStringGeneratorByRoslyn.CreateCodeOfToStringByRoslyn<Book>();
        Console.WriteLine(code);
    }
}

実行してみよう。

(Func<Book, string>)(item => new StringBuilder().Append("Title: ").Append(item.Title).Append(", ").Append("Price: ").Append(item.
Price).ToString())

目的とするソースコードができているようだ。ここまでは、まだ Roslyn は使用していない。

Roslyn によるオブジェクトの文字列への変換プログラム生成プログラム

この C# のソースコードから Roslyn を使ってデリゲートを生成しよう。このやり方は、「Roslyn による Add メソッドの動的生成」や「メソッド呼び出しのパフォーマンスの比較」で行ったのと同様だ。

次のようになる。

using Roslyn.Scripting.CSharp;
using System;
using System.Linq;
using System.Reflection;

// ToString メソッド生成器 (Roslyn 版)
public static class ToStringGeneratorByRoslyn
{
    // メソッドを生成
    public static Func<T, string> Generate<T>()
    {
        var code = CreateCodeOfToStringByRoslyn<T>(); // C# のソースコードを生成
        return Generate<T>(code: code);
    }

    // Roslyn でメソッドを生成
    static Func<T, string> Generate<T>(string code)
    {
        var engine  = CreateEngine(); // スクリプトエンジン
        var session = engine.CreateSession(); // 実行するには Session が必要
        return (Func<T, string>)session.Execute(code: code); // コードの生成
    }

    // Roslyn のスクリプトエンジンを作成する
    static ScriptEngine CreateEngine()
    {
        var engine = new ScriptEngine(); // Roslyn のスクリプトエンジン
        engine.ImportNamespace(@namespace: "System"     ); // System      名前空間を using
        engine.ImportNamespace(@namespace: "System.Text"); // System.Text 名前空間を using
        engine.AddReference(typeof(ToStringGeneratorByRoslyn).Assembly); // このアセンブリ内のクラスを使用する為に参照
        return engine;
    }

    // ToString() メソッドの C# のソースコードを作成する
    static string CreateCodeOfToStringByRoslyn<T>()
    {
        // 動的に作りたい C# のソースコードの例 (実際のコードは typeof(T) による):
        // item => new StringBuilder().Append("Title: ").Append(item.Title)
        //                            .Append(", ")
        //                            .Append("Price: ").Append(item.Price)
        //                            .ToString()

        var bodyCode = "new StringBuilder()" +
                       string.Join(".Append(\", \")",
                                   typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public)
                                            .Where(property => property.CanRead)
                                            .Select(property => string.Format(".Append(\"{0}: \").Append(item.{0})", property.Name))) +
                       ".ToString()";
        return string.Format("(Func<{0}, string>)(item => {1})", typeof(T).FullName, bodyCode);
    }
}
Roslyn によるオブジェクトの文字列への変換 (キャッシュ無し)

これを使って、これまでと同様、先ずはキャッシュ無しの変換メソッドを作ろう。 次のプログラムでは、呼ばれる度に毎回コードを生成する。

// 改良前 (メソッドのキャッシュ無し)
public static class ToStringByRoslynExtensions初期型
{
    // ToString に代わる拡張メソッド (Roslyn 版)
    public static string ToStringByRoslyn初期型<T>(this T @this)
    {
        return ToStringGeneratorByRoslyn.Generate<T>()(@this);
    }
}
メソッドのキャッシュ無し版の動作テスト

次のような簡単なプログラムで動作させてみよう。

using System;

static class Program
{
    static void Main()
    {
        var book = new Book { Title = "Metaprogramming C#", Price = 3200 };

        Console.WriteLine(book.ToStringByRoslyn初期型());
    }
}

実行結果は次のようになり、正しく動作する。

Title: Metaprogramming C#, Price: 3200
生成したメソッドのキャッシュ

では、キャッシュを利用してみよう。

今回もメソッド キャッシュ クラスを使う。

using System;
using System.Collections.Generic;

//  生成したメソッド用のキャッシュ
public class MethodCache<TResult>
{
    // メソッド格納用
    readonly Dictionary<Type, Delegate> methods = new Dictionary<Type, Delegate>();

    // メソッドの呼び出し (メソッド生成用のメソッドを引数 generator として受け取る)
    public TResult Call<T>(T item, Func<Func<T, TResult>> generator)
    {
        return Get<T>(generator)(item); // キャッシュにあるメソッドを呼び出す
    }

    // メソッドをキャッシュを介して取得 (メソッド生成用のメソッドを引数 generator として受け取る)
    Func<T, TResult> Get<T>(Func<Func<T, TResult>> generator)
    {
        var      targetType = typeof(T);
        Delegate method;
        if (!methods.TryGetValue(key: targetType, value: out method)) { // キャッシュに無い場合は
            method = generator();                                       // 動的にメソッドを生成して
            methods.Add(key: targetType, value: method);                // キャッシュに格納
        }
        return (Func<T, TResult>)method;
    }
}
Roslyn によるオブジェクトの文字列への変換 (キャッシュ有り)

では、キャッシュを行う「オブジェクトの文字列への変換」を作成しよう。

上のメソッドキャッシュ クラス MethodCache を利用して、次のようにする。

// 改良後 (メソッドのキャッシュ有り)
public static class ToStringByRoslynExtensions改
{
    // 生成したメソッドのキャッシュ
    static readonly MethodCache<string> toStringCache = new MethodCache<string>();

    // ToString に代わる拡張メソッド (Roslyn 版)
    public static string ToStringByRoslyn改<T>(this T @this)
    {
        // キャッシュを利用してメソッドを呼ぶ
        return toStringCache.Call(item: @this, generator: ToStringGeneratorByRoslyn.Generate<T>);
    }
}
メソッドのキャッシュ有り版の動作テスト

こちらも動作させてみよう。

using System;

static class Program
{
    static void Main()
    {
        var book = new Book { Title = "Metaprogramming C#", Price = 3200 };

        Console.WriteLine(book.ToStringByRoslyn改());
    }
}

やはり、実行結果は同じだ。

Title: Metaprogramming C#, Price: 3200

まとめ

今回は、前回の式木を用いた方法に続き、Roslyn を使って動的に「オブジェクトを文字列に変換する」メソッドを生成するプログラムを作成した。

次回は、メタプログラミングによる文字列変換のまとめとして、それぞれの方法でのパフォーマンスの比較を行う。

2013年11月14日

[C#][.NET] メタプログラミング入門 - 応用編 - オブジェクトの文字列変換のメタプログラミング (パフォーマンスのテスト)

Metasequoia

※ 「[C#][.NET][Roslyn] メタプログラミング入門 - 応用編 - オブジェクトの文字列変換のメタプログラミング (Roslyn 編)」の続き。

オブジェクトの文字列変換のメタプログラミング

ここまで、8通りの文字列変換メソッドを見てきた。

これらの方法について実行速度を比較していこう。

静的なメソッドに比べ、リフレクションによる動的なメソッドでは実行速度が低下する筈だ。また、メソッドの動的生成自体は遅いが、キャッシュがうまく効けば、リフレクションよりも速度面で有利な筈だ。

Add メソッドのパフォーマンスの比較」や「メソッド呼び出しのパフォーマンスの比較」で行ったように、実行速度を測ってみよう。

先ず、パフォーマンス測定用のクラスを用意する。

using System;
using System.Diagnostics;
using System.Linq.Expressions;

public static class パフォーマンステスター
{
    public static void テスト(Expression<Action> 処理式, int 回数, Action<string> output)
    {
        // 処理でなく処理式として受け取っているのは、文字列として出力する為
        var 処理 = 処理式.Compile();
        var 時間 = 計測(処理, 回数).TotalMilliseconds; // 回数分の処理に掛かったミリ秒数
        // 一回当たり何秒掛かったかを出力
        output(string.Format("{0,64}: {1,8:F}/{2} 秒", 処理式.Body.ToString(), 時間, 回数 * 1000));
    }

    static TimeSpan 計測(Action 処理, int 回数)
    {
        var stopwatch = new Stopwatch(); // 時間計測用
        stopwatch.Start();
        回数.回(処理);
        stopwatch.Stop();
        return stopwatch.Elapsed;
    }

    static void 回(this int @this, Action 処理)
    {
        for (var カウンター = 0; カウンター < @this; カウンター++)
            処理();
    }
}

テスト用のクラスは、これまでと同じ Book クラスだ。

// テスト用のクラス
public sealed class Book
{
    public string Title { get; set; }
    public int    Price { get; set; }
}
パフォーマンスの測定

では、計ってみよう。

次のプログラムを走らせてみる。

using System;
using System.Linq.Expressions;
using System.Reflection;

static class Program
{
    static void Main()
    {
        パフォーマンステスト();
    }

    static void パフォーマンステスト()
    {
        Console.WriteLine("【{0}】", MethodBase.GetCurrentMethod().Name); // メソッド名を表示

        var book = new Book { Title = "メタプログラミング C#", Price = 3800 };

        パフォーマンステスト(() => book.ToString                  ());
        パフォーマンステスト(() => book.ToStringByReflection      ());
        パフォーマンステスト(() => book.ToStringByEmit初期型      ());
        パフォーマンステスト(() => book.ToStringByEmit改          ());
        パフォーマンステスト(() => book.ToStringByExpression初期型());
        パフォーマンステスト(() => book.ToStringByExpression改    ());
        パフォーマンステスト(() => book.ToStringByRoslyn初期型    ());
        パフォーマンステスト(() => book.ToStringByRoslyn改        ());
    }

    static void パフォーマンステスト(Expression<Action> 処理式)
    {
        const int 回数 = 1000;
        パフォーマンステスター.テスト(処理式, 回数, Console.WriteLine);
    }
}

次のようになった。勿論、動作環境などによって結果は異なる。

【パフォーマンステスト】
               value(Program+<>c__DisplayClass1).book.ToString():     1.11/1000000 秒
   value(Program+<>c__DisplayClass1).book.ToStringByReflection():    20.06/1000000 秒
      value(Program+<>c__DisplayClass1).book.ToStringByEmit初期型():   716.75/1000000 秒
        value(Program+<>c__DisplayClass1).book.ToStringByEmit改():     4.84/1000000 秒
value(Program+<>c__DisplayClass1).book.ToStringByExpression初期型():   466.80/1000000 秒
  value(Program+<>c__DisplayClass1).book.ToStringByExpression改():     2.65/1000000 秒
    value(Program+<>c__DisplayClass1).book.ToStringByRoslyn初期型():  4311.85/1000000 秒
      value(Program+<>c__DisplayClass1).book.ToStringByRoslyn改():     6.02/1000000 秒

速い順に並べてみよう。

順位 方法 時間 (マイクロ秒)
1 ToString() - ToString() をオーバーライドした静的なメソッド 1.11
2 ToStringByExpressionExtensions改() - キャッシュ有り 2.65
3 ToStringByEmit改() - キャッシュ有り 4.84
4 ToStringByRoslyn改() - キャッシュ有り 6.02
5 ToStringByReflection() - リフレクションによる動的なメソッド 20.06
6 ToStringByExpressionExtensions初期型() - キャッシュ無し 466.80
7 ToStringByEmit初期型() - キャッシュ無し 716.75
8 ToStringByRoslyn初期型() - キャッシュ無し 4311.85

ほぼ予想通りの結果となった。

静的なメソッドが最速だ。メソッドの動的生成はかなり遅い。キャッシュを行うことでこれらを大きく改善でき、場合によるが、リフレクションに対しても速度上のメリットがある。

まとめ

今回は、メタプログラミングによる文字列変換のまとめとして、パフォーマンスの比較を行った。

2013年11月15日

[Event] November 2013 MVP Global Summit - 出発前

Microsoft MVP Global Summit

MVP Global Summit 2013 (前回)

明日から、Microsoft MVP Global Summit に参加予定。今年は二回目。

世界中から、招待された数千人の Microsoft MVP が集まり、米国ワシントン州のマイクロソフト本社及びその周辺で、製品チームのマイクロソフトの社員と共に、沢山のマイクロソフトのテクノロジーに関するセッションが行われる。

セッションの内容も素晴らしいが、毎晩のように開催されるパーティなどでの、各国の MVP やマイクロソフトの技術者との交流の機会もとても貴重なものだ。

MVP Global Summit の記事

2013年11月23日

[Event] November 2013 MVP Global Summit

MVP Global Summit 2013 (前回)

※ 「November 2013 MVP Global Summit - 出発前」の続き。

Microsoft MVP Global Summit に参加してきた。9回めの参加。

C# の Microsoft MVP として、多くの技術セッションに参加することができた。

セッションを含めて英語漬けの毎日だったが、1人で他国の人達のテーブルに混ざって雑談する等、英語でのコミュニケーションにも少しは慣れてきた。

NDA (Non-disclosure agreement: 秘密保持契約) の為セッション内容等は公開できず、観光や食事、パーティの光景ばかりになるが、一部紹介したい。

11月16日 ― 0日目

時差対策もあり、Global Summit の一日前に着いて、隣国カナダのバンクーバーにドライブしてみることにした。 シアトル (Seattle) には何度も行っているが、カナダは初めて行った。

時差は17時間あり、この日は長い一日となる。

小松空港から ANABombardier Canadair Regional Jet 700成田空港へ。 関西国際空港の方が近いので、JR で行ってそちらから飛ぶことが多いのだが、今回はそちらに安い直行便が無かったので、成田から行くことにした。
成田空港で Global Wifi をレンタルし、ANA の Boeing 787 で米国ワシントン州最大の都市シアトルの南に位置するシアトル・タコマ空港 (Sea-Tac Airport)へ。
シアトル・タコマ空港に到着。
日本の MVP の方数人と合流してレンタカーを借りた。北上してカナダへ向かう。
シアトル ダウンタウンを横目に、州間高速道路5号線 (I-5) をどんどん北上。
シアトルの北のエバレット (Everett) というところで一休み。 シアトルで有名なシーフード レストランのアイバース (Ivar's) でランチ。 鮭のチャウダーとクラムのチップスを食べた。モールでコーチ (COACH) のバッグを土産に買ったり、スターバックスでシアトル コーヒーを楽しんだり、大型家電店のベスト・バイ (Best Buy) に寄ったり。
5号線を更に北上し、米国とカナダの国境を越えた。
カナダのブリティッシュコロンビア州バンクーバーに到着し、スーパーマーケットへ。4リットルの凄い色のアイス クリームが山積みに。
バンクーバーは中華が美味しいらしいので、中華レストランで夕食。 魚介類を中心に頼んだが、とても美味。
バンクーバーに入ってから適当なホテルを選んでチェックイン。

11月17日 ― 1日目

Global Summit の一日目。

朝ホテルを出発し、バンクーバーを少しドライブ。
ブリティッシュコロンビア大学 (The University of British Columbia)の中のUBC人類学博物館 (Museum of Anthropology at UBC) の前に、ロゼッタ・ストーンのような石が有った。 上は何語だろうか。
バンクーバーでよく見かけた「RIGHT TURNS YIELD TO PEDESTRIANS (右折では歩行者に道を譲れ: 右折時歩行者優先)」の看板。 C# の yield return っぽい。
カナディアン レストランでランチ。全てが巨大過ぎて食べ切れないが、どっしりとした肉の味に満足。
再び米国に入国し、シアトルのワシントン湖を挟んだ東の都市ベルビュー (Bellevue) へ。 Global Summit は、主にベルビューとマイクロソフト本社キャンパスがあるレドモンド (Redmond) で開催される。
途中何度か道を間違えて、予定より遅れたが、会場兼宿泊先のハイアット・リージェンシー・ベルビュー (Hyatt Regency Bellevue) に到着。 ホテルの部屋からの景色は既に夜景。 先ず Global Summit へのレジストレーションを行った。
先ずは、各国の有志の MVP がブースを構える MVP Showcase に参加。 (ちなみに、この MVP Showcase では日本の MVP の方が優勝
ホテル近くのベルビュー・スクエア (Bellevue Square) 内の Microsoft StoreSurface 2 を購入。
その後近くのイタリアン レストランで Microsoft MVP Dinner 2013 に参加。 日本を含むアジアの MVP の方々と交流した。
QFC (Quality Food Centers) に寄って、チーズやビールを購入。 ここのチーズやビールはとても美味しい。 チーズは、シアトル ダウンタウンのパイク・プレース・マーケット (Pike Place Market) で作られている Beecher's がお気に入り。 ビールは、シアトルで生まれた REDHOOKESB。 その後は、ホテルの部屋で交流した。

11月18日 ― 2日目

Global Summit の二日目。 レドモンドのマイクロソフト キャンパスでの技術セッションが始まった。

先ずは、ホテルで朝食。 ホテルの朝食は美味い。
マイクロソフト キャンパスへはバスで移動。
会場内には、コーヒーや茶、軽食などがいつも用意されていて、セッション中も含めて好きなだけ飲食できる。 冷蔵庫には缶飲料が冷えている。
セッション中。 全て英語。 内容は、撮影禁止でお伝えできない。 前日に買った Surface 2 を使用。
セッション会場でランチ。 ユニークな味。
セッションによっては、Tシャツなどのロゴ入りグッズが貰えることも。
セッションが終わって、バスでホテルに戻る。
再び、ベルビュー・スクエア (Bellevue Square) へ。 Microsoft Store で、米国では11月22日に発売の Xbox one を見に行った。
夜は、ホテルで Welcome Reception というパーティ。 世界中の MVP と交流した。 前の Global Summit で出会った方々と再会することもできた。 facebook で繋がっている人や、改めて facebook で繋がる人も多い。 いつもブラジル カラーで統一して元気なのは、ブラジルの MVP。 今回は、日本の MVP も青の T シャツで統一。
プールバーに移動して、Insiders Party by Infragistics という開発系 MVP 向けのパーティに参加。 各国の MVP と交流。 ここでは、こちらで良く飲むレドモンドの地ビール "Mac & Jack's" を頼んだ。

11月19日 ― 3日目

Global Summit の三日目。 この日もレドモンドのマイクロソフト キャンパスでの技術セッション。

ホテルで朝食後、バスでマイクロソフト キャンパスへ移動。
C# の父であるアンダース・ヘルスバーグ (Anders Hejlsberg) 氏にお会いしたので、買ったばかりの Surface 2 にサインしてもらったり、一緒に写真を撮ってもらったりした。
マイクロソフト キャンパスの様子とユニークな味のランチ。
この日の夜は、マイクロソフト キャンパス内の Commons という社員用の飲食店街で PG Evening Event というマイクロソフトの製品開発チームとのパーティがあった。
その後は、ホテルに戻り、近くの会場で GitHub Private Party に参加。 更に、他国の知人と近くのバーへ行ったり、GitHub パーティで知り合ったカナダの MVP を誘って部屋で飲むなどした。

11月20日 ― 4日目

Global Summit の四日目。 この日もレドモンドのマイクロソフト キャンパスでの技術セッション。

矢張りホテルで朝食の後、マイクロソフト キャンパスへバスで移動。
この日のランチもユニークな感じだったので、日本からいらしたマイクロソフトの社員の方に案内していただいて、マイクロソフトの社員の方用のレストランへ。 飲茶やスープなどを食べた。 美味しい。
最後の夜は、シアトル水族館 (Seattle Aquarium) を借り切った Attendee Party。 パーティの後は、いつものように近くのバーへ行ったり部屋で飲むなどした。

11月21日 ― 5日目

Global Summit は、もう一日続いたが、そちらには参加せず帰った。

ホテルの朝食が用意されていたが、空港で食べたいものがあったので取らずにホテル近くを散歩。 QFC で土産を買うなどした。
Shuttle Express を呼んで、シアトル・タコマ空港へ移動。 空港まで19ドル+チップ。 タクシーだと50ドル。バスだと乗るまでと降りてからスーツケースを引いて少し歩かないとならないが、2ドル程度と安い。
シアトル・タコマ空港に到着。 空港のアイバースのクラム・チャウダーとフィッシュ・チップを食べる。 最高に美味い。
往路と同じく、ANA の Boeing 787 で成田空港、更に ANA の Bombardier Canadair Regional Jet 700 で小松空港へ戻ってきた。

今回も、技術的にも多くの知識を得られた。

そして、マイクロソフトの方や多くの MVP の方と沢山の交流ができたのが何よりの財産だ。

参加の度に強く感じることだが、このような機会が得られたことは、エンジニアとしてとても幸せなことだと思う。

2013年12月03日

[C#][.NET][CodeDOM] メタプログラミング入門 - CodeDOM による Hello world!

Metasequoia

※ 「[C#][.NET] メタプログラミング入門 - 応用編 - オブジェクトの文字列変換のメタプログラミング (パフォーマンスのテスト)」の続き。

CodeDOM による動的コード生成

これまで、Reflection.Emit、式木、Roslyn による動的コード生成を試してきた。これらは、比較的新しい方法だが、実は .NET Framework には初期の頃から動的コード生成を行う仕組みが備わっていた。

それが、CodeDOM (Code Document Object Model) だ。

System.CodeDom 名前空間System.CodeDom.Compiler 名前空間にあるクラスを使うことで、C# や Visual Basic.NET 等のコードを生成することができる。

今回は、CodeDOM を使って Hello world! を表示する簡単なプログラムを生成してみよう。

CodeDOM による Hello world!

CodeDOM を使って、次の手順でプログラムを組み立てていく。

  1. 名前空間の生成
  2. 名前空間への System 名前空間のインポート
  3. Program クラスの生成
  4. Program クラスの CodeDomHelloWorldDemo 名前空間への追加
  5. Main メソッドの生成
  6. Main メソッドの中身に文を追加
  7. Program クラスに Main メソッドを追加

では、やってみよう。 System.CodeDom 名前空間を使用する。

using System.CodeDom;

class Program
{
    // Hello world プログラムの CodeDOM
    static CodeNamespace HelloWorldCodeDom()
    {
        // CodeDomHelloWorldDemo 名前空間
        var nameSpace = new CodeNamespace(name: "CodeDomHelloWorldDemo");
        // System 名前空間のインポート
        nameSpace.Imports.Add(new CodeNamespaceImport(nameSpace: "System"));
        // Program クラス
        var programClass = new CodeTypeDeclaration(name: "Program");
        // Program クラスの CodeDomHelloWorldDemo 名前空間への追加
        nameSpace.Types.Add(programClass);
        // Main メソッド
        var mainMethod   = new CodeMemberMethod { Attributes = MemberAttributes.Static, Name = "Main" };
        // Main メソッドの中身に文を追加
        mainMethod.Statements.Add(
            new CodeMethodInvokeExpression( // 関数呼び出し式
                targetObject: new CodeSnippetExpression("Console")       , // オブジェクト名: Console.
                methodName  : "WriteLine"                                , // メソッド名    : WriteLine
                parameters  : new CodePrimitiveExpression("Hello world!")  // 引数          : ("Hello world!")
            )
        );
        // Main メソッドの中身に文を追加
        mainMethod.Statements.Add(
            new CodeMethodInvokeExpression( // 関数呼び出し式
                targetObject: new CodeSnippetExpression("Console"), // オブジェクト名: Console.
                methodName  : "ReadKey"                             // メソッド名    : ReadKey()
            )
        );
        // Program クラスに Main メソッドを追加
        programClass.Members.Add(mainMethod);

        return nameSpace;
    }

    static void Main()
    {
        // Hello world プログラムの名前空間を生成
        var helloWorldCodeDom = HelloWorldCodeDom();
    }
}

この HelloWorldCodeDom メソッドで、Hello world! を表示する Main メソッドを含む名前空間を生成したことになる。

CodeDOM によるソースコードの生成

次に、上で作った名前空間から CodeDOM を使って C# のソースコードを生成してみる。 System.CodeDom.Compiler 名前空間を使用する。

using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.IO;
using System.Text;

class Program
{
    // Hello world プログラムの CodeDOM
    static CodeNamespace HelloWorldCodeDom()
    {
        …… 同じなので省略 ……
    }

    // 名前空間からソースコードを生成
    static string GenerateCode(CodeNamespace codeNamespace)
    {
        // コンパイル オプション
        var compilerOptions = new CodeGeneratorOptions { IndentString = "    ", BracingStyle = "C" };

        var codeText = new StringBuilder();
        using (var codeWriter = new StringWriter(codeText)) {
            // 名前空間からソースコードを生成
            CodeDomProvider.CreateProvider("C#").GenerateCodeFromNamespace(codeNamespace, codeWriter, compilerOptions);
        }
        return codeText.ToString(); // 生成されたソースコード
    }

    static void Main()
    {
        // Hello world プログラムの名前空間を生成
        var helloWorldCodeDom = HelloWorldCodeDom();

        // Hello world プログラムのソースコードを生成
        var code = GenerateCode(helloWorldCodeDom);
        Console.WriteLine(code); // 表示
    }
}

実行してみると、次のように C# のソースコードが表示される。

namespace CodeDomHelloWorldDemo
{
    using System;


    public class Program
    {

        static void Main()
        {
            Console.WriteLine("Hello world!");
            Console.ReadKey();
        }
    }
}

ちなみに、上記 GenerateCode メソッド中の "C#" とある部分を "VB" と置き換えて実行してみると、次のように Visual Basic.NET のソースコードとなる。

Imports System

Namespace CodeDomHelloWorldDemo

    Public Class Program

        Shared Sub Main()
            Console.WriteLine("Hello world!")
            Console.ReadKey
        End Sub
    End Class
End Namespace

"C#" を "JScript" に置き換えた場合は、次のようになる。

//@cc_on
//@set @debug(off)

import System;

package CodeDomHelloWorldDemo
{

    public class Program
    {

        private static function Main()
        {
            Console.WriteLine("Hello world!");
            Console.ReadKey();
        }
    }
}

CodeDOM によるアセンブリの生成

では次に、CodeDOM で生成した名前空間をコンパイルしてアセンブリを生成してみよう。

先程までのプログラムに、更に書き足して、次のようにする。

using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Diagnostics;
using System.IO;
using System.Text;

class Program
{
    // Hello world プログラムの CodeDOM
    static CodeNamespace HelloWorldCodeDom()
    {
        …… 同じなので省略 ……
    }

    // 名前空間からソースコードを生成
    static string GenerateCode(CodeNamespace codeNamespace)
    {
        …… 同じなので省略 ……
    }

    // 名前空間を実行可能アセンブリへコンパイル
    static void CompileExecutableAssembly(CodeNamespace codeNamespace, string outputAssemblyName)
    {
        var codeCompileUnit = new CodeCompileUnit();     // コンパイル単位
        codeCompileUnit.Namespaces.Add(codeNamespace);   // コンパイル単位に名前空間を追加
        // 実行可能アセンブリへコンパイル
        CodeDomProvider.CreateProvider("C#").CompileAssemblyFromDom(
            options         : new CompilerParameters {   // コンパイル オプション
                OutputAssembly     = outputAssemblyName, // 出力アセンブリのファイル名
                GenerateExecutable = true                // 実行可能なアセンブリを生成
            },
            compilationUnits: codeCompileUnit                     // コンパイル単位
        );
    }

    static void Main()
    {
        // Hello world プログラムの名前空間を生成
        var helloWorldCodeDom = HelloWorldCodeDom();

        // Hello world プログラムのソースコードを生成
        var code = GenerateCode(helloWorldCodeDom);
        Console.WriteLine(code); // 表示

        // Hello world プログラムを、実行可能アセンブリとしてコンパイル
        const string outputAssemblyName = "CodeDomHelloWorldDemo.exe";
        CompileExecutableAssembly(codeNamespace: HelloWorldCodeDom(), outputAssemblyName: outputAssemblyName);
        Process.Start(fileName: outputAssemblyName); // 生成された実行可能アセンブリを実行
    }
}

実行してみると、"CodeDomHelloWorldDemo.exe" が立ち上がり、Hello world! と表示される。

Hello world!

まとめ

今回は、CodeDOM を使った動的コード生成を行ってみた。 次回も CodeDOM を使ってみよう。

2013年12月04日

[C#][.NET][CodeDOM] メタプログラミング入門 - CodeDOM によるクラスの生成

Metasequoia

※ 「[C#][.NET][CodeDOM] メタプログラミング入門 - CodeDOM による Hello world!」の続き。

CodeDOM によるクラスの動的生成

前回は、CodeDOM を使って Hello world! を表示するプログラムを動的に生成した。

もう少し CodeDOM を使ってみよう。 今回は、CodeDOM を使ってクラスを作ってみる。

次のような Item クラスを動的に作ることにする。

namespace CodeDomClassDemo
{
    public class Item
    {
        private int price;

        public int Price
        {
            get
            {
                return this.price;
            }
            set
            {
                this.price = value;
            }
        }

        public override string ToString()
        {
            return (this.Price + "円");
        }
    }
}

次のような手順だ。

  1. CodeDomClassDemo 名前空間の生成
    ― CodeNamespace を new
  2. Item クラスの生成
    ― CodeTypeDeclaration を new
  3. Item クラスを CodeDomClassDemo 名前空間へ追加
  4. price フィールドを生成
    ― CodeMemberField を new
  5. price フィールドを Item クラスに追加
  6. Price プロパティを生成
    ― CodeMemberProperty を new
  7. Price プロパティの Get を追加
    ― price フィールドへの参照を return する文を生成して追加
  8. Price プロパティの Set を追加
    ― price フィールドへの参照に value を代入する文を生成して追加
  9. Price プロパティを Item クラスに追加
  10. ToString メソッドを生成
    ― CodeMemberMethod を new
  11. ToString メソッドの中身を追加
    ― Price プロパティへの参照に &qout;円&qout; を + して return する文を生成して追加
  12. ToString メソッドを Item クラスに追加

では、やってみよう。 前回同様、System.CodeDom 名前空間を使用する。

using System.CodeDom;

class Program
{
    // Item クラスの Code DOM
    static CodeNamespace ItemClassCodeDom()
    {
        // CodeDomHelloWorldDemo 名前空間
        var nameSpace              = new CodeNamespace(name: "CodeDomClassDemo");
        //// System 名前空間のインポート
        //nameSpace.Imports.Add(new CodeNamespaceImport(nameSpace: "System"));

        // Item クラス
        var itemClass              = new CodeTypeDeclaration(name: "Item");
        // CodeDomHelloWorldDemo 名前空間に Item クラスを追加
        nameSpace.Types.Add(itemClass);

        // price フィールド
        var priceField             = new CodeMemberField(type: typeof(int), name: "price") {
            Attributes = MemberAttributes.Private // private
        };
        // Item クラスに price フィールドを追加
        itemClass.Members.Add(priceField);

        // Price プロパティ
        var priceProperty          = new CodeMemberProperty {
            Name       = "Price"                                         , // 名前は、Price                 
            Attributes = MemberAttributes.Public | MemberAttributes.Final, // public で virtual じゃない
            Type       = new CodeTypeReference(typeof(int))                // 型は int
        };
        // price フィールドへの参照
        var priceFieldReference    = new CodeFieldReferenceExpression(
            targetObject: new CodeThisReferenceExpression(), // this.
            fieldName   : "price"                            // price
        );
        // Price プロパティの Get を追加
        priceProperty.GetStatements.Add(
            new CodeMethodReturnStatement(priceFieldReference) // price フィールドへの参照を return する文
        );
        // Price プロパティの Set を追加
        priceProperty.SetStatements.Add(
            new CodeAssignStatement(                                  // 代入文
                left : priceFieldReference                          , // 左辺は、price フィールドへの参照
                right: new CodePropertySetValueReferenceExpression()  // 右辺は、value
            )
        );
        // Item クラスに Price プロパティを追加
        itemClass.Members.Add(priceProperty);

        // ToString メソッド
        var toStringMethod         = new CodeMemberMethod {
            Name       = "ToString"                                         , // 名前は、ToString
            Attributes = MemberAttributes.Public | MemberAttributes.Override, // public で override
            ReturnType = new CodeTypeReference(typeof(string))                // 戻り値の型は、string
        };
        // Price プロパティへの参照
        var pricePropertyReference = new CodePropertyReferenceExpression(
            targetObject: new CodeThisReferenceExpression(), // this.
            propertyName: "Price"                            // Price
        );
        // ToString メソッドの中身
        toStringMethod.Statements.Add(
            new CodeMethodReturnStatement(                    // return 文
                new CodeBinaryOperatorExpression(             // 二項演算子の式
                    left : pricePropertyReference           , // Price プロパティへの参照
                    op   : CodeBinaryOperatorType.Add       , // +
                    right: new CodePrimitiveExpression("円")  // "円"
                )
            )
        );
        // Item クラスに ToString メソッドを追加
        itemClass.Members.Add(toStringMethod);

        return nameSpace;
    }

    static void Main()
    {
        // Item クラスを含む名前空間を CodeDOM で生成
        var itemClassCodeDom = ItemClassCodeDom();
    }
}

この ItemClassCodeDom メソッドで、Item クラスを含む名前空間を生成したことになる。

CodeDOM によるソースコードの生成

前回同様、上で作った名前空間から CodeDOM を使って C# のソースコードを生成してみる。 手順は前回と全く同じで、System.CodeDom.Compiler 名前空間を使用する。

using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.IO;
using System.Text;

class Program
{
    // Item クラスの Code DOM
    static CodeNamespace ItemClassCodeDom()
    {
        …… 同じなので省略 ……
    }

    // 名前空間からソースコードを生成
    static string GenerateCode(CodeNamespace codeNamespace)
    {
        // コンパイル オプション
        var compilerOptions = new CodeGeneratorOptions { IndentString = "    ", BracingStyle = "C" };

        var codeText        = new StringBuilder();
        using (var codeWriter = new StringWriter(codeText)) {
            // 名前空間からソースコードを生成
            CodeDomProvider.CreateProvider("C#").GenerateCodeFromNamespace(codeNamespace, codeWriter, compilerOptions);
        }
        return codeText.ToString(); // 生成されたソースコード
    }

    static void Main()
    {
        // Item クラスを含む名前空間を CodeDOM で生成
        var itemClassCodeDom = ItemClassCodeDom();

        // Item クラスのソースコードを生成
        var code             = GenerateCode(itemClassCodeDom);
        Console.WriteLine(code);
    }
}

実行してみると、次のように C# のソースコードが表示される。

namespace CodeDomClassDemo
{


    public class Item
    {

        private int price;

        public int Price
        {
            get
            {
                return this.price;
            }
            set
            {
                this.price = value;
            }
        }

        public override string ToString()
        {
            return (this.Price + "円");
        }
    }
}

GenerateCode メソッド中の "C#" の部分を "VB" に置き換えて実行した場合は次の通り。

Namespace CodeDomClassDemo

    Public Class Item

        Private price As Integer

        Public Property Price() As Integer
            Get
                Return Me.price
            End Get
            Set
                Me.price = value
            End Set
        End Property

        Public Overrides Function ToString() As String
            Return (Me.Price + "円")
        End Function
    End Class
End Namespace

CodeDOM によるアセンブリの生成

次に、CodeDOM で生成した名前空間をコンパイルしてアセンブリを生成してみよう。

前回は、アセンブリをファイルに出力したが、今回はオンメモリで生成してみる。

生成したアセンブリの中から Item クラスを取り出してインスタンスを生成し、Price プロパティや ToString メソッドを使ってみよう。

using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;

class Program
{
    // Item クラスの Code DOM
    static CodeNamespace ItemClassCodeDom()
    {
        …… 同じなので省略 ……
    }
    
    // 名前空間からソースコードを生成
    static string GenerateCode(CodeNamespace codeNamespace)
    {
        …… 同じなので省略 ……
    }

    // 名前空間をアセンブリへコンパイル
    static Assembly CompileAssembly(CodeNamespace codeNamespace)
    {
        var codeCompileUnit = new CodeCompileUnit();   // コンパイル単位
        codeCompileUnit.Namespaces.Add(codeNamespace); // コンパイル単位に名前空間を追加
        // アセンブリへコンパイル
        var compilerResults = CodeDomProvider.CreateProvider("C#").CompileAssemblyFromDom(
            options         : new CompilerParameters { // コンパイル オプション
                GenerateInMemory = true                // アセンブリをメモリ内で生成
            },
            compilationUnits: codeCompileUnit          // コンパイル単位
        );
        return compilerResults.CompiledAssembly; // コンパイルされたアセンブリを返す
    }

    static void Main()
    {
        // Item クラスを含む名前空間を CodeDOM で生成
        var itemClassCodeDom = ItemClassCodeDom();

        // Item クラスのソースコードを生成
        var code             = GenerateCode(itemClassCodeDom);
        Console.WriteLine(code);

        // Item クラスを、アセンブリとしてコンパイル
        var itemAssembly     = CompileAssembly(codeNamespace: itemClassCodeDom);
        // Item クラス アセンブリのテスト
        TestItemAssembly(itemAssembly);
    }

    // Item クラス アセンブリのテスト
    static void TestItemAssembly(Assembly itemAssembly)
    {
        // Item クラスを、アセンブリから取得
        var     itemType = itemAssembly.GetTypes().First();
        // Item クラスのインスタンスを動的に生成
        dynamic item     = Activator.CreateInstance(itemType);

        // Item クラスの Price プロパティのテスト
        item.Price       = 2980;
        Console.WriteLine(item.Price);
        // Item クラスの ToString メソッドのテスト
        Console.WriteLine(item);
    }
}

実行してみると、次のように表示され、Price プロパティとToString メソッドが正常に使えるのが分かる。

2980
2980円

まとめ

今回は、CodeDOM を使った動的にクラスを生成した。CodeDOM を使うことで、クラスを実行時に作ることができる。

2013年12月12日

[C#][.NET][Roslyn] メタプログラミング入門 - Roslyn による C# ソースコードの解析と変更

Metasequoia

この記事は、「C# Advent Calendar 2013」の 12 月 12 日分。

※ 「[C#][.NET][CodeDOM] メタプログラミング入門 - CodeDOM によるクラスの生成」の続き。

Roslyn

Roslyn は、C# や Visual Basic のコンパイラーの内部の API 等を公開したものだ。"Compiler as a Service" と表現されている。

Roslyn に関しては、以前、次にあげる記事で扱った。参考にしてほしい。

Roslyn によるコード解析

先ず、「Roslyn による Visual Studio のアドイン」で行った Roslyn を使った C# のソースコードの解析を、もっと簡単な例でやってみたい。

次の手順でコード解析のサンプルを準備する。

  1. Visual Studio で C# のコンソール アプリケーションを作成
  2. メタプログラミング入門 - Roslyn による Add メソッドの動的生成」のときと同様の手順で Roslyn をインストール
    1. Visual Studio の「ソリューション エクスプローラー」でプロジェクト名を右クリックし、「NuGet パッケージの管理...」を開く
    2. "Roslyn" を検索し、インストール

このプロジェクトの Main メソッド内に、単純な C# のソースコードを文字列として準備する。 「Program クラスの中に空の Main メソッドがあるだけ」のソースコードだ。

class Program
{
    static void Main()
    {
        // 解析する C# のソースコード
        var sourceCode = @"
            using System;

            class Program
            {
                static void Main()
                {}
            }
        ";
    }
}

この単純な C# のソースコードの文字列を、Roslyn でパースしてシンタックス ツリーに変換し、簡単な解析をしてみよう。

それには、Roslyn.Compilers.CSharp 名前空間の SyntaxWalker クラスを用いる。

このクラスは、Visitor パターンになっていて、これを継承し、各種メソッドをオーバーライドすることで、様々な種類のノードやトークンを辿ることができるようになっている。

例えば、次のような SyntaxWalker の派生クラスを用意し、各ノードを Visit するメソッドをオーバーライドすると、ソースコードの構成要素であるノードを全部辿ることができる。

using Roslyn.Compilers.CSharp;
using System;

class Walker : SyntaxWalker // Visitor パターンでソースコードを解析
{
    public override void Visit(SyntaxNode node) // 各ノードを Visit
    {
        if (node != null)
            Console.WriteLine("[Node  - Type: {0}, Kind: {1}]\n{2}\n", node.GetType().Name, node.Kind, node);

        base.Visit(node);
    }
}

この Walker クラスで、先程の単純な C# のソースコードを解析してみる。

using Roslyn.Compilers.CSharp;

class Program
{
    static void Main()
    {
        // 解析する C# のソースコード
        var sourceCode = @"
            using System;

            class Program
            {
                static void Main()
                {}
            }
        ";

        var syntaxTree = SyntaxTree.ParseText(sourceCode); // ソースコードをパースしてシンタックス ツリーに
        var rootNode   = syntaxTree.GetRoot();             // ルートのノードを取得

        new Walker().Visit(rootNode);                      // 解析
    }
}

実行してみよう。

[Node  - Type: CompilationUnitSyntax, Kind: CompilationUnit]
using System;

    class Program
    {
        static void Main()
        {}
    }


[Node  - Type: UsingDirectiveSyntax, Kind: UsingDirective]
using System;

[Node  - Type: IdentifierNameSyntax, Kind: IdentifierName]
System

[Node  - Type: ClassDeclarationSyntax, Kind: ClassDeclaration]
class Program
    {
        static void Main()
        {}
    }

[Node  - Type: MethodDeclarationSyntax, Kind: MethodDeclaration]
static void Main()
        {}

[Node  - Type: PredefinedTypeSyntax, Kind: PredefinedType]
void

[Node  - Type: ParameterListSyntax, Kind: ParameterList]
()

[Node  - Type: BlockSyntax, Kind: Block]
{}

各ノードの情報が表示される。 ノードは入れ子になっているのが分かる。

次に、Walker クラスを少し変更して、ノードでなく、より細かいソースコードの構成要素であるトークンを表示してみる。 今度は、各トークンを Visit する VisitToken メソッドをオーバーライドして、全トークンを辿ってみる。

class Walker : SyntaxWalker // Visitor パターンでソースコードを解析
{
    public Walker() : base(depth: SyntaxWalkerDepth.Token) // トークンの深さまで Visit
    {}

    public override void VisitToken(SyntaxToken token) // 各トークンを Visit
    {
        if (token != null)
            Console.WriteLine("[Token - Type: {0}, Kind: {1}]\n{2}\n", token.GetType().Name, token.Kind, token);

        base.VisitToken(token);
    }
}

実行してみよう。

[Token - Type: SyntaxToken, Kind: UsingKeyword]
using

[Token - Type: SyntaxToken, Kind: IdentifierToken]
System

[Token - Type: SyntaxToken, Kind: SemicolonToken]
;

[Token - Type: SyntaxToken, Kind: ClassKeyword]
class

[Token - Type: SyntaxToken, Kind: IdentifierToken]
Program

[Token - Type: SyntaxToken, Kind: OpenBraceToken]
{

[Token - Type: SyntaxToken, Kind: StaticKeyword]
static

[Token - Type: SyntaxToken, Kind: VoidKeyword]
void

[Token - Type: SyntaxToken, Kind: IdentifierToken]
Main

[Token - Type: SyntaxToken, Kind: OpenParenToken]
(

[Token - Type: SyntaxToken, Kind: CloseParenToken]
)

[Token - Type: SyntaxToken, Kind: OpenBraceToken]
{

[Token - Type: SyntaxToken, Kind: CloseBraceToken]
}

[Token - Type: SyntaxToken, Kind: CloseBraceToken]
}

[Token - Type: SyntaxToken, Kind: EndOfFileToken]

今度は、より細かく "using"、"System"、";"、"class" 等の各トークンの情報が表示される。 ノードと異なり入れ子にはなっていない。

Roslyn によるコードの変更

ReplaceNode メソッドによるコードの変更

Roslyn では、コードを単に解析するだけでなく、改変することも可能だ。

試しに先程の Program クラスの中に空の Main メソッドがあるだけの C# のソースコードの Main の中を、"Hello world!" を表示するコードに変更してみよう。 こんな感じだ。

  1. Roslyn.Compilers.CSharp.Syntax クラスを用い、Console.WriteLine("Hello world!"); が入ったブロックをノードとして作成する「CreateHelloWorldBlock メソッド」を用意
  2. 元の単純な C# のソースコードのソースコードをパースしてシンタックス ツリーにする
  3. Main メソッドからブロック ("{" と "}" で囲まれた部分) を取り出す
  4. Roslyn.Compilers.CommonSyntaxNodeExtensions クラスにある ReplaceNode 拡張メソッドを使って、空のブロックをConsole.WriteLine("Hello world!"); が入ったブロックに置き換える

実装してみると、次のようになる。

using Roslyn.Compilers;
using Roslyn.Compilers.CSharp;
using System;
using System.Linq;

class Program
{
    // Roslyn.Compilers.CSharp.Syntax クラスを用いた Console.WriteLine("Hello world!"); が入ったブロックの作成
    static BlockSyntax CreateHelloWorldBlock()
    {
        var invocationExpression = Syntax.InvocationExpression(       // Console.WriteLine("Hello world!");
            expression: Syntax.MemberAccessExpression(                // Console.WriteLine というメンバー アクセス
                kind      : SyntaxKind.MemberAccessExpression,
                expression: Syntax.IdentifierName("Console"  ),
                name      : Syntax.IdentifierName("WriteLine")
            ),
            argumentList: Syntax.ArgumentList(                        // 引数リスト
                arguments: Syntax.SeparatedList<ArgumentSyntax>(
                    node: Syntax.Argument(                            // "Hello world!"
                        expression: Syntax.LiteralExpression(
                            kind : SyntaxKind.StringLiteralExpression,
                            token: Syntax.Literal("Hello world!")
                        )
                    )
                )
            )
        );

        var statement            = Syntax.ExpressionStatement(expression: invocationExpression);
        return Syntax.Block(statement);
    }

    static void Main()
    {
        // 改変する C# のソースコード
        var sourceCode = @"
            using System;

            class Program
            {
                static void Main()
                {}
            }
        ";

        var syntaxTree           = SyntaxTree.ParseText(sourceCode); // ソースコードをパースしてシンタックス ツリーに
        var rootNode             = syntaxTree.GetRoot();             // ルートのノードを取得

        // Main メソッドのブロックを取得
        var block                = rootNode.DescendantNodes().First(node => node.Kind == SyntaxKind.Block);

        var newNode              = rootNode.ReplaceNode(                 // ノードの置き換え
                                        oldNode: block                  , // 元の空のブロック
                                        newNode: CreateHelloWorldBlock()  // Console.WriteLine("Hello world!"); が入ったブロック
                                   );

        Console.WriteLine(newNode.NormalizeWhitespace()); // 整形して表示
    }
}

実行してみよう。

using System;

class Program
{
    static void Main()
    {
        Console.WriteLine(@"Hello world!");
    }
}

Main メソッドの空だったブロックが、Console.WriteLine(@"Hello world!"); 入りのブロックに変更されたのが分かる。

SyntaxRewriter クラスによるコードの変更

上では ReplaceNode 拡張メソッドを使ったが、Roslyn.Compilers.CSharp.SyntaxRewriter を使ってもコードを変更することができる。

こちらの方は、ノード内に一斉に同じ変更を行うのに向いている。

SyntaxRewriter クラスは、上の方でソースコードの解析に用いた SyntaxWalker クラスと同様に、継承することで Visitor パターンによって、様々な種類のノードやトークンを辿ることができるクラスだ。

SyntaxRewriter クラスでは、適宜メソッドをオーバーライドすることで、ノードやトークンを書き換えることができる。

今回は、SyntaxRewriter を使い、ソースコード中の邪魔な #region と #endregion を消してみよう。

先ず、C# のソースコードの文字列として、次のように #region と #endregion が入ったものを用意する。

class Program
{
    static void Main()
    {
        // 改変する #region と #endregion 入の C# のソースコード
        var sourceCode = @"
            public class MyViewModel : INotifyPropertyChanged
            {
            #region INotifyPropertyChanged メンバー
                public event PropertyChangedEventHandler PropertyChanged;

                protected void OnPropertyChanged(string name)
                {
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs(name));
                }
            #endregion // INotifyPropertyChanged メンバー
            }
        ";
    }
}

次に #region と #endregion を除去するためのクラス RemoveRegionRewriter を用意する。 SyntaxRewriter クラスからの派生クラスだ。

using Roslyn.Compilers.CSharp;

// #region と #endregion を除去するクラス
class RemoveRegionRewriter : SyntaxRewriter
{
    public RemoveRegionRewriter() : base(visitIntoStructuredTrivia: true) // true にすることで #region や #endregion まで辿れる
    {}

    // #region を Visit
    public override SyntaxNode VisitRegionDirectiveTrivia(RegionDirectiveTriviaSyntax node)
    {
        return Syntax.SkippedTokensTrivia(); // スキップする
    }

    // #endregion を Visit
    public override SyntaxNode VisitEndRegionDirectiveTrivia(EndRegionDirectiveTriviaSyntax node)
    {
        return Syntax.SkippedTokensTrivia(); // スキップする
    }
}

では、このクラスを使って先程の #region と #endregion 入の C# のソースコードから #region と #endregion を除いてみよう。

using Roslyn.Compilers.CSharp;
using System;

class Program
{
    static void Main()
    {

        // 改変する C# のソースコード
        var sourceCode = @"
            public class MyViewModel : INotifyPropertyChanged
            {
            #region INotifyPropertyChanged メンバー
                public event PropertyChangedEventHandler PropertyChanged;

                protected void OnPropertyChanged(string name)
                {
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs(name));
                }
            #endregion // INotifyPropertyChanged メンバー
            }
        ";

        var syntaxTree = SyntaxTree.ParseText(sourceCode);                 // ソースコードをパースしてシンタックス ツリーに
        var rootNode   = syntaxTree.GetRoot();                             // ルートのノードを取得
        var newNode    = new RemoveRegionRewriter().Visit(node: rootNode); // #region と #endregion の除去
        Console.WriteLine(newNode.NormalizeWhitespace());                  // 整形して表示
    }
}

実行してみよう。

public class MyViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string name)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(name));
    }
}

#region と #endregion の行が除去されているのが分かるだろう。

まとめ

今回は、Roslyn を使い、簡単な C# ソースコードの解析と変更を行った。

C# Advent Calendar 2013」の明日は yone64 さん。

2014年01月08日

[TypeScript] TypeScript に関する参考資料

TypeScript

TypeScript に関する参考資料を集めてみた。

Web

書籍

2014年01月10日

Microsoft Virtual Academy のご紹介

study.jpg

Microsoft Virtual Academy のご紹介

Microsoft Virtual Academy は、マイクロソフトの無償のオンライン トレーニングです。

特徴

入門者向けの内容が多いのが特徴です。

スライドと動画を観ながら講義を受けられます。

(英語のコンテンツもありますが、字幕が出るものが多くあります)

マイクロソフト アカウントでサインアップして参加することで、学習の進み具合も記録され、継続的な学習ができます。

C# / XAML のコンテンツ

C# と XAML によるアプリケーション開発について学びたい人向けのコンテンツの一覧は次の場所にあります。

例えば、これから Windows ストア アプリの C# での開発方法を学習したい人は、次の順に進めていくこ良いでしょう。

  1. 最新 .NET 技術によるアプリケーション開発入門
     これから .NET を始める方向け。Visual Studio 2013 に合わせた新しい内容。
  2. いまさら聞けない Windows アプリ開発入門 XAML/C# 編
    初めて XAML/C# で Windows 用のアプリを開発する人向け。
  3. C# を使用した Windows ストア アプリ開発概要ジャンプ スタート
    より詳しく XAML/C# でのWindows ストア アプリ開発について解説。
  4. C# を使用した高度な Windows ストア アプリ開発ジャンプ スタート
    中級~上級学習者向け。

2014年01月26日

[Event] Hokuriku.NET Vol.13 in 富山

Hokuriku.NET Vol.13 in 富山』に参加してきた。

Hokuriku.NET Vol.13 in 富山
開催日 2014/01/25
会場 富山県民会館
詳細 http://atnd.org/events/46157

イベントの写真

イベントの様子
懇親会はブリしゃぶ

当日のスライド (一部)

参加された方が書かれた記事

2014年02月17日

[TypeScript][WebGL] Hokuriku.NET Vol.13 in 富山「3D で遊ぼう ~C#er も TypeScript で楽々 WebGL~」の資料公開

Hokuriku.NET Vol.13 in 富山 (2月16-17日) のライトニングトークでやった資料を公開。

サンプル コード

サンプル コードはこちら

■ 関連記事

2014年02月18日

[TypeScript] TypeScript の特長

typescriptlogo.png

TypeScript とは

C# のような静的型付き言語に慣れているプログラマーにとって、JavaScript は多少違和感がある。

「クラスがない」などは、ちょっと何言ってるか分からないかも知れない。

そこで TypeScript の登場だ。

特長

次のような特長がある。

  • JavaScript のスーパーセット
  • クラスベースオブジェクト指向のサポート
  • 型を明記することによる静的型チェック
  • JavaScript の次期標準仕様である ECMAScript6 を先駆けて取り込み

また、開発に便利な機能が多くある。

TypeScript にあって JavaScript にないものは例えば次のようなものだ (抜粋)。

  • 静的型付け
  • ラムダ式 ( => )
  • アクセス制御 (public や private など)
  • プロパティ (簡易に書ける C# のようなプロパティ)
  • オーバーロード (これは C# などに比べるとイマイチ)
  • デフォルト引数
  • クラス
  • 継承
  • インタフェイス
  • ジェネリクス
  • 名前空間またはパッケージ
  • 型推論 (C# 程ではない)
  • Visual Studio での静的エラーの即時フィードバック
  • Visual Studio でのインテリセンス

これらの機能を Visual Studio 上で使うことで、効率よく開発を行うことができる。

尚、Visual Studio では、TypeScript 上にブレークポイントを置いてデバッグ実行することが可能だ。

入手場所

TypeScript は次の場所でダウンロードでき、Visual Studio 2012 や 2013 にインストールすることができる。

TypeScript の JavaScript へのコンパイル

TypeScript コンパイラーは、TypeScript へのコードを JavaScript へとコンパイルする。

出来上がった JavaScript を HTML などに組み込んで使う訳だ。

実際にどのようにコンパイルされるか見てみよう。

TypeScript の新規プロジェクト

TypeScript をインストールした Visual Studio 2012 または 2013 で、メニュー「ファイル」 - 「新規作成」 - 「プロジェクト」から「他の言語」の中の「TypeScript」 - 「TypeScript を使用した HTML アプリケーション」を選択し、プロジェクトを新規に作成する。

「TypeScript を使用した HTML アプリケーション」の新規作成
「TypeScript を使用した HTML アプリケーション」の新規作成

このプロジェクトに TypeScript ファイルを新規に追加してコンパイルしてみよう。

注意点が一点。TypeScript ファイルの文字コードが「シフトJIS」になっていると、作られた JavaScript で日本語の文字が文字化けを起こすことがある。

例えば、次のような感じだ。

TypeScript ファイルの文字コードが「シフトJIS」になっていたために日本語の文字が文字化けを起こした JavaScript
TypeScript ファイルの文字コードが「シフトJIS」になっていたために日本語の文字が文字化けを起こした JavaScript

TypeScript ファイルの文字コードを Unicode に変更しておこう。

TypeScript ファイルを開いた状態で、メニュー「ファイル」 - 「保存オプションの詳細設定」で変更する。

保存オプションの詳細設定

に変更する。

※ この問題は、TypeScript 0.9.5 で起こることを確認していたが、TypeScript 1.0RC で修正されていることを確認。(2014-02-28 追記)

試しに次のような TypeScript ファイル "vector.ts" を作成し、コンパイルしてみよう。

// モジュール (名前空間やパッケージのようなもの)
module Graphics {
    // 二次元ベクトルのクラス
    export class Vector {
        // コンストラクター
        constructor(public x: number = 0.0, public y: number = 0.0) {}

        // プロパティ (C# のプロパティにあたる/型付き)
        get length(): number {
            return Math.sqrt(this.x * this.x + this.y * this.y);
        }

        // メソッド (引数、戻り値ともに型付きにできる)
        add(vector: Vector): Vector {
            return new Vector(this.x + vector.x, this.y + vector.y);
        }

        multiply(vector: Vector): Vector {
            return new Vector(this.x * vector.x, this.y * vector.y);
        }
    }
}

Pascal のように型名が前でなく後ろにくるなどの違いはあるが、C# などに慣れている人にはそれ程違和感のないコードだ。

これは、C# の次のようなコードにあたる。

using System;

// 名前空間
namespace Graphics
{
    // 二次元ベクトルのクラス
    public class Vector {
        public double X { get; set; }
        public double Y { get; set; }

        // コンストラクター
        public Vector(double x = 0.0, double y = 0.0)
        {
            X = x;
            Y = y;
        }

        // プロパティ
        public double Length
        {
            get { return Math.Sqrt(X * X + Y * Y); }
        }

        // メソッド
        public Vector Add(Vector vector)
        {
            return new Vector(X + vector.X, Y + vector.Y);
        }

        public Vector Multiply(Vector vector)
        {
            return new Vector(X * vector.X, this.Y * vector.Y);
        }
    }
}

この TypeScript を、コンパイルすると "vector.js" という JavaScript ファイルが作られる。

// モジュール (名前空間やパッケージのようなもの)
var Graphics;
(function (Graphics) {
    // 二次元ベクトルのクラス
    var Vector = (function () {
        // コンストラクター
        function Vector(x, y) {
            if (typeof x === "undefined") { x = 0.0; }
            if (typeof y === "undefined") { y = 0.0; }
            this.x = x;
            this.y = y;
        }
        Object.defineProperty(Vector.prototype, "length", {
            // プロパティ (C# のプロパティにあたる/型付き)
            get: function () {
                return Math.sqrt(this.x * this.x + this.y * this.y);
            },
            enumerable: true,
            configurable: true
        });

        // メソッド (引数、戻り値ともに型付きにできる)
        Vector.prototype.add = function (vector) {
            return new Vector(this.x + vector.x, this.y + vector.y);
        };

        Vector.prototype.multiply = function (vector) {
            return new Vector(this.x * vector.x, this.y * vector.y);
        };
        return Vector;
    })();
    Graphics.Vector = Vector;
})(Graphics || (Graphics = {}));
//# sourceMappingURL=vector.js.map

ちゃんと JavaScript に変換されているのが分かる。

これを HTML に組み込んで使えば OK だ。

2014年02月19日

[TypeScript] ImageData によるピクセル単位の描画

typescriptlogo.png

今回は TypeScript による JavaScript で、HTML5Canvas 上の ImageData にピクセル単位の描画を行う例をあげてみる。

描画自体は、特に TypeScript に依存した内容ではないが、TypeScript に慣れるためのサンプルという意味合いだ。

HTML5 の Canvas に ImageData を作成してピクセル単位の描画を行う

前回を参考に、Visual Studio で「TypeScript を使用した HTML アプリケーション」を作成してやってみよう。

先ず setpixelsample.html という HTML ファイルを用意する。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>SetPixel Sample</title>
    <script src="setpixelsample.js"></script>
</head>
<body onload="SetPixelSample.Program.run()">
    <canvas width="500" height="500"></canvas>
</body>
</html>

この HTML ファイルでは、setpixelsample.js という JavaScript ファイルを組み込んでいる。そこで setpixelsample.ts という TypeScript ファイルを追加する。

また、onload="SetPixelSample.Program.run()" という記述があるが、このメソッドも setpixelsample.ts 内に用意する。

そして、このメソッド内から HTML 内の canvas に描画を行うこととする。

canvas オブジェクトを取得し、その中に (canvas オブジェクトと同じ幅と高さの) ImageData オブジェクトを作成するには、createImageData を使う。

	var canvas    = <HTMLCanvasElement>document.querySelector("canvas");
	var context   = canvas.getContext("2d");
	var imageData = context.createImageData(canvas.width, canvas.height);

また、ImageData オブジェクトに任意の色のピクセルを置くには、次のように ImageData オブジェクトの data に赤、緑、青、アルファ値の順で1バイトずつ書き込めば良い。

    // ImageData の指定した座標の 1 ピクセルを指定した色にする
    private static setPixel(imageData: ImageData, x: number, y: number, red: number, green: number, blue: number, alpha: number = 0xff) {
        // 指定した座標のピクセルが ImageData の data のどの位置にあるかを計算
        var index = (x + y * imageData.width) * 4;

        // その位置から、赤、緑、青、アルファ値の順で1バイトずつ書き込むことで、ピクセルがその色になる
        imageData.data[index + 0] = red  ;
        imageData.data[index + 1] = green;
        imageData.data[index + 2] = blue ;
        imageData.data[index + 3] = alpha;
    }

任意の色のピクセルが置かれた ImageData オブジェクトを描画するには putImageData を使う。

    // ImageData を描画
    context.putImageData(imageData, 0, 0);

ImageData オブジェクトにランダムに 100,000 個のピクセルを置いて描画するサンプルを作ってみると次のようになる (setpixelsample.ts)。

module SetPixelSample {
    export class Program {
        static run() {
            var canvas    = <HTMLCanvasElement>document.querySelector("canvas");
            var context   = canvas.getContext("2d");
            var imageData = context.createImageData(canvas.width, canvas.height);

            // ImageData にランダムにピクセルを置く
            Program.setRandomPixels(canvas, imageData);

            // ImageData を描画
            context.putImageData(imageData, 0, 0);
        }

        // ImageData のランダムな座標のピクセルをランダムな色にする
        private static setRandomPixels(canvas: HTMLCanvasElement, imageData: ImageData) {
            for (var index = 0; index < 100000; index++) {
                var x     = Program.randomInteger(canvas.height);
                var y     = Program.randomInteger(canvas.width );
                var red   = Program.randomInteger(0x100        );
                var green = Program.randomInteger(0x100        );
                var blue  = Program.randomInteger(0x100        );
                Program.setPixel(imageData, x, y, red, green, blue);
            }
        }

        // 疑似乱数 (0 から value 未満の整数)
        private static randomInteger(value: number): number {
            return Math.floor(value * Math.random());
        }

        // ImageData の指定した座標の 1 ピクセルを指定した色にする
        private static setPixel(imageData: ImageData, x: number, y: number, red: number, green: number, blue: number, alpha: number = 0xff) {
            // 指定した座標のピクセルが ImageData の data のどの位置にあるかを計算
            var index = (x + y * imageData.width) * 4;

            // その位置から、赤、緑、青、アルファ値の順で1バイトずつ書き込むことで、ピクセルがその色になる
            imageData.data[index + 0] = red  ;
            imageData.data[index + 1] = green;
            imageData.data[index + 2] = blue ;
            imageData.data[index + 3] = alpha;
        }
    }
}

コンパイルして setpixelsample.js を生成し、setpixelsample.html を Internet Explorer 9 以降等の HTML5 対応の Web ブラウザーで表示してみると次のようにランダムな 100,000 個のピクセルが描画される。

setpixelsample.html
setpixelsample.html

実際のサンプルを次の場所に用意した。

2014年02月20日

[TypeScript][C#][Windows ストア アプリ] ImageData によるマンデルブロ集合の描画

typescriptlogo.png

『[TypeScript] ImageData によるピクセル単位の描画』の続き。

前回は、HTML5 の Canvas に ImageData を作成してランダムなピクセルを描画した。

今回も同様にピクセル単位の描画を行ってみよう。少しだけ応用してマンデルブロ集合を描画してみる。

マンデルブロ集合

マンデルブロ集合は、フラクタルとして有名な図形だ。

詳しくは、次の場所が参考になる:

HTML5 の Canvas にマンデルブロ集合を描画する

HTML5 の Canvas にピクセル単位の描画を行う方法については、前回を参考にしてほしい。

今回は、TypeScript の特長である「クラス設計のやり易さ」を活かすこととし、前回よりもきちんとクラス分けを行ってみたい。

先ず、前回同様の mandelbrot.html という HTML ファイルを用意する。 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>Mandelbrot Sample</title>
    <script src="graph.js"></script>
    <script src="mandelbrot.js"></script>
</head>
<body onload="MandelbrotSample.MainProgram.run()">
    <canvas width="800" height="800"></canvas>
</body>
</html>
<!--
Usage:
    mandelbrot.html		                  : position = (-2.8, 2.0), scale = 4.0
    mandelbrot.html?posy=1&scale=2        : position = (-2.8, 1.0), scale = 2.0
    mandelbrot.html?posx=0&posy=0&scale=1 : position = ( 0.0, 0.0), scale = 1.0

    - This HTML can work with mandelbrot.js and graph.js online or offline.
-->

この HTML ファイルでは、graph.js と mandelbrot.js という2つの JavaScript ファイルを組み込んでいる。 それぞれ graph.ts と mandelbrot.ts いう TypeScript ファイルから生成されることになる。

onload="MandelbrotSample.MainProgram.run()" という記述があるが、このメソッドは mandelbrot.ts 内に用意する。

graph.ts

graph.ts は、Canvas にピクセル単位の描画を行うモジュールだ。

今回は、Canvas もクラス化してみよう。ピクセル毎に描画を行う部分もクラス化する。

また、座標、大きさ、矩形、色などの基本的なクラスもここに置くことにする。

module Graph {
    // 色
    export class Color {
        constructor(public red: number = 0x00, public green: number = 0x00, public blue: number = 0x00, public alpha: number = 0xff) { }
    }

    // 二次元ベクトル
    export class Vector {
        constructor(public x: number = 0.0, public y: number = 0.0) {}

        add(vector: Vector): Vector {
            return new Vector(this.x + vector.x, this.y + vector.y);
        }

        multiply(vector: Vector): Vector {
            return new Vector(this.x * vector.x, this.y * vector.y);
        }

        multiplyBy(value: number): Vector {
            return new Vector(this.x * value, this.y * value);
        }
    }

    // 二次元の大きさ
    export class Size {
        constructor(public width: number, public height: number) {}
    }

    // 矩形
    export class Rectangle {
        constructor(public position: Vector, public size: Size) {}
    }

    // ImageData への描画用
    class ImageDataHelper {
        // ImageData の指定した座標の 1 ピクセルを指定した色にする
        static setPixel(imageData: ImageData, position: Vector, color: Color) {
            // ImageData のサイズ
            var imageDataSize         = new Size(imageData.width, imageData.height);
            // 指定したピクセルの座標が有効でなかったら
            if (!ImageDataHelper.isValid(imageDataSize, position))
                return;

            // 指定した座標のピクセルが ImageData の data のどの位置にあるかを計算
            var index                 = ImageDataHelper.toIndex(imageDataSize, position);
            // その位置から、赤、緑、青、アルファ値の順で1バイトずつ書き込むことで、ピクセルがその色になる
            imageData.data[index + 0] = color.red  ;
            imageData.data[index + 1] = color.green;
            imageData.data[index + 2] = color.blue ;
            imageData.data[index + 3] = color.alpha;
        }

        // 指定したピクセルの座標が有効かどうか
        private static isValid(imageDataSize: Size, position: Vector): boolean {
            return position.x >= 0.0 && position.x <= imageDataSize.width  &&
                   position.y >= 0.0 && position.y <= imageDataSize.height;
        }

        // 指定した座標のピクセルが ImageData の data のどの位置にあるかを計算
        private static toIndex(imageDataSize: Size, position: Vector): number {
            return (position.x + position.y * imageDataSize.width) * 4;
        }
    }

    // Canvas に ImageData を置きピクセル毎に描画を行う
    export class Sheet {
        private context_  : CanvasRenderingContext2D;
        private imageData_: ImageData;

        get context() {
            return this.context_;
        }

        constructor(context: CanvasRenderingContext2D, size: Size) {
            this.context_   = context;
            this.imageData_ = context.createImageData(size.width, size.height);
        }

        // ImageData の指定した座標の 1 ピクセルを指定した色にする
        setPixel(position: Vector, color: Color) {
            ImageDataHelper.setPixel(this.imageData_, position, color);
        }

        // 指定した位置に ImageData を描画
        draw(position: Vector = new Vector()) {
            this.context.putImageData(this.imageData_, position.x, position.y);
        }
    }

    // キャンバス
    export class Canvas {
        private canvas_ : HTMLCanvasElement;
        private context_: CanvasRenderingContext2D;

        constructor() {
            this.canvas_  = <HTMLCanvasElement>document.querySelector("canvas");
            this.context_ = this.canvas_.getContext("2d");
        }

        get size(): Graph.Size {
            return Canvas.getCanvasSize(this.canvas_);
        }

        get position(): Graph.Rectangle {
            return new Graph.Rectangle(new Graph.Vector(), this.size);
        }

        get context(): CanvasRenderingContext2D {
            return this.context_;
        }

        private static getCanvasSize(canvas: HTMLCanvasElement): Graph.Size {
            return new Graph.Size(canvas.width, canvas.height);
        }
    }
}

mandelbrot.ts

mandelbrot.ts は、Canvas にマンデルブロ集合を描画する行うモジュールだ。

メイン プログラム、マンデルブロ集合、マンデルブロ集合を描画するときのパラメーター、そしてパラメーターをクエリ文字列から取得するためのユーティリティの各クラスからなる。

/// <reference path="graph.ts"/>

module MandelbrotSample {
    class Utility {
        // クエリ文字列から数を取得
        static getNumberFromQueryString(key: string): number {
            var queryString = Utility.getQueryString(key);
            if (queryString != "") {
                try {
                    return parseInt(queryString);
                } catch (ex) {}
            }
            return null;
        }
        
        // クエリ文字列の取得
        static getQueryString(key: string, default_: string = null): string {
            if (default_ == null)
                default_ = "";
            key             = key.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
            var regex       = new RegExp("[\\?&]" + key + "=([^&#]*)");
            var queryString = regex.exec(window.location.href);
            return queryString == null ? default_ : queryString[1];
        }
    }

    // パラメーター
    class Parameter {
        position      : Graph.Vector = new Graph.Vector(-2.8, 2.0);
        maximum       : number       = 32;
        private scale_: number       = 4.0;

        get ratio(): number {
            return this.scale_ / this.size.width;
        }

        constructor(public size: Graph.Size) {
            this.setPositionX();
            this.setPositionY();
            this.setScale    ();
            this.setMaximum  ();
        }

        private setPositionX() {
            var positionX = Utility.getNumberFromQueryString("posx");
            if (positionX != null)
                this.position.x = positionX;
        }

        private setPositionY() {
            var positionY = Utility.getNumberFromQueryString("posy");
            if (positionY != null)
                this.position.y = positionY;
        }

        private setScale() {
            var scale = Utility.getNumberFromQueryString("scale");
            if (scale != null)
                this.scale_ = scale;
        }

        private setMaximum() {
            var maximum = Utility.getNumberFromQueryString("max");
            if (maximum != null)
                this.maximum = maximum;
        }
    }

    class Mandelbrot {
        private position_ : Graph.Vector;
        private sheet_    : Graph.Sheet;
        private parameter_: Parameter;

        constructor(context: CanvasRenderingContext2D, position: Graph.Rectangle, size: Graph.Size) {
            this.position_  = position.position;
            this.sheet_     = new Graph.Sheet(context, size);
            this.parameter_ = new Parameter(size);
        }

        draw(palette: Graph.Color[]) {
            var point = new Graph.Vector();
            for (point.y = 0; point.y < this.parameter_.size.height; point.y++) {
                for (point.x = 0; point.x < this.parameter_.size.width; point.x++) {
                    var a = this.parameter_.position.add(new Graph.Vector(point.x, -point.y).multiplyBy(this.parameter_.ratio));
                    this.setPixel(point, this.getCount(a), palette);
                }
            }
            this.sheet_.draw(this.position_);
        }

        private getCount(a: Graph.Vector): number {
            var squareBorder = 25.0;
            var square       = new Graph.Vector();
            var point        = new Graph.Vector();
            var count        = 0;
            do {
                point  = new Graph.Vector(square.x - square.y + a.x, 2.0 * point.x * point.y + a.y);
                square = point.multiply(point);
                count++;
            } while (square.x + square.y < squareBorder && count <= this.parameter_.maximum);
            return count < this.parameter_.maximum ? count : 0;
        }

        private setPixel(point: Graph.Vector, count: number, palette: Graph.Color[]) {
            this.sheet_.setPixel(point, palette[Mandelbrot.toColorIndex(count, palette.length)]);
        }

        private static toColorIndex(colorNumber: number, paletteSize: number): number {
            var colorIndexNumber = paletteSize * 2 - 2;
            var colorIndex       = colorNumber % colorIndexNumber;
            if (colorIndex >= paletteSize)
                colorIndex = colorIndexNumber - colorIndex;
            return colorIndex;
        }
    }

    export class MainProgram {
        static run() {
            var canvas     = new Graph.Canvas();
            var mandelbrot = new Mandelbrot(canvas.context, canvas.position, canvas.size);
            mandelbrot.draw(MainProgram.getPalette());
        }

        // パレット (予め色を格納しておき、パレット番号で色を参照)
        private static getPalette(): Graph.Color[] {
            return [
                new Graph.Color(0x02, 0x08, 0x80),
                new Graph.Color(0x10, 0x10, 0x70),
                new Graph.Color(0x20, 0x18, 0x60),
                new Graph.Color(0x30, 0x20, 0x50),
                new Graph.Color(0x40, 0x28, 0x40),
                new Graph.Color(0x50, 0x30, 0x30),
                new Graph.Color(0x60, 0x38, 0x20),
                new Graph.Color(0x70, 0x40, 0x10),
                new Graph.Color(0x80, 0x48, 0x0e),
                new Graph.Color(0x90, 0x50, 0x0c),
                new Graph.Color(0xa0, 0x58, 0x0a),
                new Graph.Color(0xb0, 0x60, 0x08),
                new Graph.Color(0xc0, 0x68, 0x06),
                new Graph.Color(0xd0, 0x70, 0x04),
                new Graph.Color(0xe8, 0x78, 0x02),
                new Graph.Color(0xff, 0x80, 0x01)
            ];
        }
    }
}

クラス図

以上のクラス構成を、クラス図で見てみよう。次のようになる。

クラス図
クラス図

TypeScript を使っている為に、このようなクラス設計は比較的容易だ。

実行例

では、実行してみよう。

コンパイルすると、graph.js と mandelbrot.js が生成される。

mandelbrot.html を Internet Explorer 9 以降等の HTML5 対応の Web ブラウザーで表示してみると、次のようにマンデルブロ集合が描画される。

mandelbrot.html
mandelbrot.html

実際のサンプルを次の場所に用意した。

マンデルブロ集合のどこを描画するかを指定するパラメーターがクエリ文字列で渡せるようになっているので、次のようにして描画位置を変えることができる。

C# で作成した例

比較の為に、同様のものを C# (Windows ストア アプリ) で作成してみよう。

Visual Studio でメニュー「新規作成」 - 「プロジェクト」から「Visual C#」 - 「Windows ストア」 - 「新しいアプリケーション (XAML)」で Windows ストア アプリを新規作成する。

App.xaml と App.xaml.cs には特に変更を加えない。

MainPage.xaml と MainPage.xaml.cs

謂わば mandelbrot.html にあたる部分。 TypeScript の場合同様、こちらも Canvas にイメージのオブジェクトを置き、描画する。

<Page
    x:Class="MandelbrotSample.WindowsStore.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MandelbrotSample.WindowsStore"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" Loaded="Page_Loaded">
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Canvas x:Name="canvas">
            <Image x:Name="image" />
        </Canvas>
        <ProgressRing x:Name="progress" Width="50" Height="50" Foreground="Blue" />
    </Grid>
</Page>
using MandelbrotSample.WindowsStore.Graph;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace MandelbrotSample.WindowsStore
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            InitializeComponent();
        }

        async void Page_Loaded(object sender, RoutedEventArgs e)
        {
            await Run();
        }

        async Task Run()
        {
            var canvasPosition = new IntRectangle { Position = new IntVector(),
                                                    Size     = new IntSize { Width  = (int)canvas.ActualWidth ,
                                                                             Height = (int)canvas.ActualHeight } };

            progress.IsActive  = true ;
            await MainProgram.RunAsync(canvasPosition, image);
            progress.IsActive  = false;
        }
    }
}
Graph.cs

graph.ts にあたる部分は次のような感じ。

こちらでは、Palette はクラスにした。

また、TypeScript では number という型だったものは、int と double に分けた。

using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.UI;
using Windows.UI.Xaml.Media.Imaging;

namespace MandelbrotSample.WindowsStore.Graph
{
    public class Palette
    {
        readonly Color[] colors;

        public int Size
        {
            get { return colors.Length; }
        }

        public Palette(int size)
        { colors = new Color[size]; }

        public Color this[int index]
        {
            get
            {
                return index >= 0 && index < Size ? colors[index] : new Color();
            }
            set
            {
                if (index >= 0 && index < Size)
                    colors[index] = value;
            }
        }
    }

    public class Vector
    {
        public double X { get; set; }
        public double Y { get; set; }

        public static Vector operator +(Vector vector1, Vector vector2)
        {
            return new Vector { X = vector1.X + vector2.X, Y = vector1.Y + vector2.Y };
        }

        public static Vector operator -(Vector vector1, Vector vector2)
        {
            return new Vector { X = vector1.X - vector2.X, Y = vector1.Y - vector2.Y };
        }

        public static Vector operator *(Vector vector1, Vector vector2)
        {
            return new Vector { X = vector1.X * vector2.X, Y = vector1.Y * vector2.Y };
        }

        public static Vector operator *(Vector vector, double value)
        {
            return new Vector { X = vector.X * value, Y = vector.Y * value };
        }

        public static Vector operator /(Vector vector, double value)
        {
            return new Vector { X = vector.X / value, Y = vector.Y / value };
        }
    }

    public class IntVector
    {
        public int X { get; set; }
        public int Y { get; set; }

        public static IntVector operator -(IntVector vector, IntSize size)
        {
            return new IntVector { X = vector.X - size.Width, Y = vector.Y - size.Height };
        }
    }
    
    public class IntSize
    {
        public int Width  { get; set; }
        public int Height { get; set; }

        public static IntSize operator +(IntSize size1, IntSize size2)
        {
            return new IntSize { Width = size1.Width + size2.Width, Height = size1.Height + size2.Height };
        }

        public static IntSize operator *(IntSize size, IntVector vector)
        {
            return new IntSize { Width = size.Width * vector.X, Height = size.Height * vector.Y };
        }
    }

    public class IntRectangle
    {
        public IntVector Position { get; set; }
        public IntSize   Size     { get; set; }
    }

    public static class WriteableBitmapExtension
    {
        public static void Clear(this WriteableBitmap bitmap, Color color)
        {
            var arraySize = bitmap.PixelBuffer.Capacity;
            var array     = new byte[arraySize];
            for (var index = 0; index < arraySize; index += 4) {
                array[index    ] = color.B;
                array[index + 1] = color.G;
                array[index + 2] = color.R;
                array[index + 3] = color.A;
            }
            using (var pixelStream = bitmap.PixelBuffer.AsStream()) {
                pixelStream.Seek(0, SeekOrigin.Begin);
                pixelStream.Write(array, 0, array.Length);
            }
        }

        public static void SetPixel(this WriteableBitmap bitmap, int x, int y, Color color)
        {
            var bitmapWidth  = bitmap.PixelWidth;
            var bitmapHeight = bitmap.PixelHeight;
            if (!IsValid(bitmapWidth, bitmapHeight, x, y))
                return;
            var index = ToIndex(bitmapWidth, x, y);
            Debug.Assert(index >= 0 && index < bitmap.PixelBuffer.Capacity);

            using (var pixelStream = bitmap.PixelBuffer.AsStream()) {
                var array = new byte[] { color.B, color.G, color.R, color.A };
                pixelStream.Seek(index, SeekOrigin.Begin);
                pixelStream.Write(array, 0, array.Length);
            }
        }

        static int ToIndex(int bitmapWidth, int x, int y)
        {
            return (x + y * bitmapWidth) * 4;
        }

        static bool IsValid(int width, int height, int x, int y)
        {
            return x >= 0 && x < width  &&
                   y >= 0 && y < height;
        }
    }

    public class Sheet
    {
        public WriteableBitmap Bitmap { get; set; }

        public Sheet(IntSize size, Color backgroundColor = new Color())
        {
            Bitmap = new WriteableBitmap(size.Width, size.Height);
            Bitmap.Clear(backgroundColor);
        }

        public void SetPixel(IntVector position, Color color)
        { Bitmap.SetPixel(position.X, position.Y, color); }
    }
}
Mandelbrot.cs

mandelbrot.ts にあたる部分は次のような感じ。

TypeScript の run メソッドは、非同期の RunAsync とした。

using MandelbrotSample.WindowsStore.Graph;
using System;
using System.Threading.Tasks;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;

namespace MandelbrotSample.WindowsStore
{
    public class Mandelbrot
    {
        public class Parameter {
            public Vector    Position { get; private set; }
            public int       Maximum  { get; private set; }
            public IntSize   Size     { get; private set; }
            public double    Ratio    { get; private set; }

            public Parameter(IntSize size) : this(position: new Vector { X = -2.8, Y = 1.5 }, scale: 5.0, maximum: 32, size: size)
            {}

            public Parameter(Vector position, double scale, int maximum, IntSize size)
            {
                Position = position;
                Maximum  = maximum;
                Size     = size;
                Ratio    = scale / size.Width;
            }
        }

        const double       squareBorder = 25.0;

        readonly Sheet     sheet;
        readonly Parameter parameter;

        public WriteableBitmap Bitmap
        {
            get { return sheet.Bitmap; }
        }

        public Mandelbrot(IntRectangle position, Color backgroundColor = new Color())
        {
            sheet          = new Sheet(position.Size, backgroundColor);
            this.parameter = new Parameter(size: position.Size);
        }

        public Mandelbrot(IntRectangle position, Parameter parameter, Color backgroundColor = new Color())
        {
            sheet          = new Sheet(position.Size, backgroundColor);
            this.parameter = parameter;
        }

        public void Draw(Palette palette)
        {
            var point = new Graph.IntVector();
            for (point.Y = 0; point.Y < parameter.Size.Height; point.Y++) {
                for (point.X = 0; point.X < this.parameter.Size.Width; point.X++) {
                    var a = parameter.Position + new Vector { X = point.X, Y = -point.Y } * parameter.Ratio;
                    SetPixel(point, GetCount(a), palette);
                }
            }
        }

        int GetCount(Vector a)
        {
            var square = new Vector();
            var point  = new Vector();
            var count  = 0;
            do {
                point  = new Vector { X = square.X - square.Y + a.X, Y = 2.0 * point.X * point.Y + a.Y };
                square = point * point;
                count++;
            } while (square.X + square.Y < squareBorder && count <= parameter.Maximum);
            return count < parameter.Maximum ? count : 0;
        }

        void SetPixel(IntVector point, int count, Palette palette)
        {
            sheet.SetPixel(point, palette[ToColorIndex(count, palette.Size)]);
        }

        static int ToColorIndex(int colorNumber,  int paletteSize)
        {
            var colorIndexNumber = paletteSize * 2 - 2;
            var colorIndex       = colorNumber % colorIndexNumber;
            if (colorIndex >= paletteSize)
                colorIndex = colorIndexNumber - colorIndex;
            return colorIndex;
        }
    }

    class MainProgram
    {
        public static async Task RunAsync(IntRectangle canvasPosition, Image image)
        {
            await Window.Current.Dispatcher.RunIdleAsync(e => image.Source = DrawMandelbrot(canvasPosition));
        }

        static Palette GetPalette()
        {
            var palette = new Graph.Palette(16);
            palette[ 0] = new Color { R = 0x02, G = 0x08, B = 0x80 };
            palette[ 1] = new Color { R = 0x10, G = 0x10, B = 0x70 };
            palette[ 2] = new Color { R = 0x20, G = 0x18, B = 0x60 };
            palette[ 3] = new Color { R = 0x30, G = 0x20, B = 0x50 };
            palette[ 4] = new Color { R = 0x40, G = 0x28, B = 0x40 };
            palette[ 5] = new Color { R = 0x50, G = 0x30, B = 0x30 };
            palette[ 6] = new Color { R = 0x60, G = 0x38, B = 0x20 };
            palette[ 7] = new Color { R = 0x70, G = 0x40, B = 0x10 };
            palette[ 8] = new Color { R = 0x80, G = 0x48, B = 0x0e };
            palette[ 9] = new Color { R = 0x90, G = 0x50, B = 0x0c };
            palette[10] = new Color { R = 0xa0, G = 0x58, B = 0x0a };
            palette[11] = new Color { R = 0xb0, G = 0x60, B = 0x08 };
            palette[12] = new Color { R = 0xc0, G = 0x68, B = 0x06 };
            palette[13] = new Color { R = 0xd0, G = 0x70, B = 0x04 };
            palette[14] = new Color { R = 0xe8, G = 0x78, B = 0x02 };
            palette[15] = new Color { R = 0xff, G = 0x80, B = 0x01 };
            return palette;
        }

        static WriteableBitmap DrawMandelbrot(IntRectangle canvasPosition)
        {
            var mandelbrot = new Mandelbrot(position: canvasPosition, backgroundColor: Colors.MidnightBlue);
            mandelbrot.Draw(GetPalette());
            return mandelbrot.Bitmap;
        }
    }
}

実行結果もほぼ変わらない。

MandelbrotSample.WindowsStore
MandelbrotSample.WindowsStore

2014年02月21日

[Event] de:code

米国サンフラシスコで 4/2 - 4/4 に開催される Build 2014 の内容を凝縮して日本で開催される有償の開発者向けイベント。

3月上旬に登録開始予定。

かつて日本でも Microsoft TechEd という開発者/ITプロ向けの最大級のマイクロソフトの技術に関するカンファレンスが毎年横浜などで開かれていたが、2011年の東日本大震災の影響で開催が延期、その後中止され、それ以降開催されていない。

de:code は、日本での今年最大の開発者向けののマイクロソフトの技術に関するカンファレンスとなるだろう。

マイクロソフト デベロッパー イベント デコード
Japan Microsoft Developer Event de:code
会期 2014年5月29日(木)-30日(金)
会場 ザ・プリンス パークタワー東京
参加費 有償
主催 日本マイクロソフト
詳細 マイクロソフト デベロッパー イベント デコード | Japan Microsoft Developer Event de:code - Microsoft Events & Seminars

2014年02月22日

[Event] MVP Community Camp 2014

Community Camp 2014

昨年まで Community Open Day として全国で一斉に開催されていたマイクロソフト技術の勉強会だが、今年は MVP Community Camp 2014 としてアジア パシフィック全体で行われる。

17日から21日迄はオンライン イベント、22日はオフライン イベントとなる。

私は北陸会場の担当。

MVP Community Camp 2014

MVP Community Camp 2014 東京会場

  • MVP Community Camp 2014 東京会場
    2014年3月22日(土)
    日本マイクロソフト株式会社 品川本社 セミナールーム (品川グランドセントラルタワー 31F)
MVP Community Camp 2014 東京会場 リーフレット

MVP Community Camp 2014 北陸会場

MVP Community Camp 2014 北陸会場
会期 2014年3月22日(土)
会場 石川工業高等専門学校 (石川県河北郡津幡町北中条タ1)
6号館 (トライアル研究センター) 2階講義室
参加費 無料
主催 Hokuriku.NET
福井情報技術者協会[FITEA]
協力 日本マイクロソフト
詳細/お申込み MVP Community Camp 2014 北陸会場 - ATND
MVP Community Camp 2014 北陸会場 リーフレット

参考: 昨年までのイベント

参考: 昨年までの北陸会場

2014年02月23日

[TypeScript][WebGL][Three.js] 3D で遊ぼう ~C#er も TypeScript で楽々 WebGL~ ― 準備編

typescriptlogo.png

※ 『Hokuriku.NET Vol.13 in 富山「3D で遊ぼう ~C#er も TypeScript で楽々 WebGL~」』の資料の内容の解説。

今回は、TypeScript を使って WebGL に挑戦してみよう。

素で使うと中々に取っつき難い WebGL だが、TypeScript を使うことで比較的楽に使うことができる。 C#er の方も是非 Web での 3D プログラミングに興味を持っていただきたい。

WebGL とは

WebGL は、HTML5Canvas に三次元 (または二次元) のグラフィックを表示する標準仕様だ。

Web ブラウザーで、プラグインなしで動作する。

WebGL 動作環境

現状 WebGL は、PC 上のブラウザーでは、最新の IEGoogle ChromeFireFoxでは、ほぼ動作する。但し全てが動くとは限らない。

モバイルで動作するものは多くない。

モバイル環境の WebGL 対応状況
Platform iPhone, iPad Phones & Tablet Android 4.0+ Kindle Fire Phones BB10 Tablet MeeGo - N9 Symbian Windows Phone Windows 8 Android & Symbian Java,iOS Android Android, MeeGo* Firefox OS
WebGL
3D Canvas for the web
 
Specific
device

30+
   
 

2.0+
     
11+

12+ (android)
 
 

 
Mobile HTML5 compatibility on iPhone, Android, Windows Phone, BlackBerry, Firefox OS and other mobile devices より引用。

Windows RT タブレットでは動作する。

WebGL プログラミング

WebGL のプログラミングには、シェーディング言語である GLSL (OpenGL 2.0) と JavaScript を用いる。

次の図のような感じだ。左側が GLSL、右側が JavaScript による部分だ。

GLSL と JavaScript による WebGL プログラミング
GLSL と JavaScript による WebGL プログラミング

慣れていない人には、結構大変だ。

three.js

そこで、ここでは three.js という JavaScript のライブラリを用いることによる。

three.js は、WebGL のラッパーで、WebGL を抽象化して易しく使えるようにしてくれるのだ。

次の場所で入手することができる。

ダウンロードし、three.js または three.min.js というファイルを HTML ファイルから参照すれば OK だ。

three.d.ts

three.js を TypeScript から使う為には、型定義ファイルの three.d.ts も必要だ。

こちらは、次の場所で入手することができる。

または、NuGet で入手することができる。

Visual Studio で NuGet を検索してインストールすることも可能だ。

Visual Studio で NuGet の three.d.ts を検索
Visual Studio で NuGet の three.d.ts を検索

この three.d.ts を自分の TypeScript ファイルから参照する。

three.js による 3D プログラミング

では three.js で 3D プログラミングを始めてみよう。

ここでは、次のような順序で進めていく。

  • 0. 準備
  • 1. WebGL レンダラーの作成
  • 2. カメラの作成
  • 3. シーン (空間) の作成
    • 3.1 ライト (光源) の作成 → シーンに追加
    • 3.2 メッシュの作成 ― ジオメトリー (形) とマテリアル (材料) からメッシュを作成 → シーンに追加
  • 4. レンダリング ― レンダラーとカメラでシーンをレンダリング
0. 準備
  • 0.1 『[TypeScript] TypeScript の特長』を参考に、Visual Studio で「TypeScript を使用した HTML アプリケーション」を作成する。
  • 0.2 three.min.js と three.d.ts をプロジェクトのフォルダーに置く (ここではプロジェクトのフォルダーにその儘フラットに置いている)。
  • 0.3 threejswebglsample.ts というファイル名の TypeScript ファイルをプロジェクトに追加する。
  • 0.4 threejswebglsample.ts の一行目に、
    /// <reference path="three.d.ts"/>
    という1行を書き、three.d.ts を参照する
    (three.d.ts が threejswebglsample.ts と異なるフォルダーにある場合は、相対パスで書く)。
  • 0.5 threejswebglsample.html というファイル名の HTML ファイルをプロジェクトに追加する。

threejswebglsample.html では、three.min.js と threejswebglsample.ts から生成される threejswebglsample.js を参照する。

次のようにする。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8" />
    <script src="three.min.js"></script>
    <script src="threejswebglsample.js"></script>
    <title>THREE.js で WebGL</title>
</head>
<body>
    <div id="viewport"></div>
</body>
</html>

中に viewport という id の div があるが、後で three.js によって、この中に Canvas を作る。

1. WebGL レンダラーの作成

レンダラーを作成する TypeScript のコードは次のようになる。

    // 1. WebGL レンダラーを作成
    try { var renderer = new THREE.WebGLRenderer({ antialias: true }); } catch (e) {}
    if (renderer == null) {
        alert("お使いの環境では WebGL はご利用いただけません。");
        return;
    }
    // サイズの設定
    renderer.setSize(window.innerWidth, window.innerHeight);
    // 背景色の設定(色, 透明度)
    renderer.setClearColorHex(0x000000, 1);
    // レンダラーによって、viewport の ID を持つ HTML 要素に子要素として canvas を追加
    document.querySelector("#viewport").appendChild(renderer.domElement);

これで描画する準備ができた。

1. WebGL レンダラーの作成
1. WebGL レンダラーの作成
2. カメラの作成

次にカメラを作成する。

     // 2. カメラを作成
    // (透視投影の) カメラを作成
    var fov                   = 100;                                     // 画角
    var aspect                = window.innerWidth / window.innerHeight;  // 縦横比
    var camera                = new THREE.PerspectiveCamera(fov, aspect);
    camera.position           = new THREE.Vector3(0, 0, 1000);           // z 方向に 1000 ずらす

カメラは、z 方向に 1000 ずらした位置に置くことにする。

2. カメラの作成
2. カメラの作成
3. シーン (空間) の作成

シーン (空間) を作成する。 シーンには、後でライト (光源) を置いたり、メッシュと呼ばれるオブジェクトを配置したりする。

    // 3. シーン (空間) を作成
    var scene                 = new THREE.Scene();

シーン (空間) が作成された。

3. シーン (空間) の作成
3. シーン (空間) の作成
3.1 ライト (光源) の作成 → シーンに追加

ライト (光源) を作成し、シーンに追加する。

ライトには幾つかの種類があるが、ここでは太陽光のような平行光源 (無限遠光源) を作成する。

    // 3.1 ライト (光源) を作成 → シーンに追加
    // 平行光源 (無限遠光源) を作成 
    var directionalLight      = new THREE.DirectionalLight(0xffffff, 1); // 引数: 色, 強さ
    directionalLight.position = new THREE.Vector3(0, 0, 1);              // z 方向から照らす
    // 光源をシーンに追加
    scene.add(directionalLight);

シーン (空間) にライト (光源) が置かれた。

3.1 ライト (光源) の作成 → シーンに追加
3.1 ライト (光源) の作成 → シーンに追加
3.2 メッシュの作成 ― ジオメトリー (形) とマテリアル (材料) からメッシュを作成 → シーンに追加

ジオメトリー (形) とマテリアル (材料) からメッシュを作成し、シーンに追加する。

three.js では、予めプリミティブ (基本的な) ジオメトリー (形) が幾つか用意されている。ここでは、その中から直方体を作成する。

また、マテリアル (材料) も幾つもの種類が用意されているが、ここでは単純な色だけを持ったものを用いる。

    // 3.2 メッシュを用意 → シーンに追加
    // ジオメトリー (形) とマテリアル (材料) からメッシュを作成

    // プリミティブなジオメトリーを作成
    var geometry = new THREE.CubeGeometry(500, 500, 500);
    // マテリアルを作成 (赤)
    var material = new THREE.MeshLambertMaterial({ color: 0xff0000 });

    // ジオメトリーとマテリアルを合わせてメッシュを作成
    var cubeMesh = new THREE.Mesh(geometry, material);
    // メッシュをシーンに追加
    scene.add(cubeMesh);

これで、シーン (空間) にメッシュが置かれた。

3.2 メッシュの作成 ― ジオメトリー (形) とマテリアル (材料) からメッシュを作成 → シーンに追加
3.2 メッシュの作成 ― ジオメトリー (形) とマテリアル (材料) からメッシュを作成 → シーンに追加
4. レンダリング ― レンダラーとカメラでシーンをレンダリング

最後にレンダリングを行うと描画される。

    // 4. レンダリング - レンダラーとカメラでシーンをレンダリング
    renderer.render(scene, camera);

ここまでをクラスのメソッドに入れ、実行できるようにしてみよう。threejswebglsample.ts は、こうなる。

/// <reference path="three.d.ts"/>

module ThreeJSWebGLSample {
    export class Application {
        static run() {
            // 1. WebGL レンダラーを作成
            try { var renderer = new THREE.WebGLRenderer({ antialias: true }); } catch (e) {}
            if (renderer == null) {
                alert("お使いの環境では WebGL はご利用いただけません。");
                return;
            }
            // サイズの設定
            renderer.setSize(window.innerWidth, window.innerHeight);
            // 背景色の設定(色, 透明度)
            renderer.setClearColorHex(0x000000, 1);
            // レンダラーによって、viewport の ID を持つ HTML 要素に子要素として canvas を追加
            document.querySelector("#viewport").appendChild(renderer.domElement);

            // 2. カメラを作成
            // (透視投影の) カメラを作成
            var fov                   = 100;                                     // 画角
            var aspect                = window.innerWidth / window.innerHeight;  // 縦横比
            var camera                = new THREE.PerspectiveCamera(fov, aspect);
            camera.position           = new THREE.Vector3(0, 0, 1000);           // z 方向に 1000 ずらす

            // 3. シーン (空間) を作成
            var scene                 = new THREE.Scene();

            // 3.1 ライト (光源) を作成 → シーンに追加
            // 平行光源 (無限遠光源) を作成 
            var directionalLight      = new THREE.DirectionalLight(0xffffff, 1); // 引数: 色, 強さ
            directionalLight.position = new THREE.Vector3(0, 0, 1);              // z 方向から照らす
            // 光源をシーンに追加
            scene.add(directionalLight);

            // 3.2 メッシュを用意 → シーンに追加
            // ジオメトリー (形) とマテリアル (材料) からメッシュを作成

            // プリミティブなジオメトリーを作成
            var geometry = new THREE.CubeGeometry(500, 500, 500);
            // マテリアルを作成 (赤)
            var material = new THREE.MeshLambertMaterial({ color: 0xff0000 });
            // ジオメトリーとマテリアルを合わせてメッシュを作成
            var cubeMesh = new THREE.Mesh(geometry, material);
            // メッシュをシーンに追加
            scene.add(cubeMesh);

            // 4. レンダリング - レンダラーとカメラでシーンをレンダリング
            renderer.render(scene, camera);
        }
    }
}

// ウィンドウがロードされた時
window.onload = () => {
    // アプリケーションの起動
    ThreeJSWebGLSample.Application.run();
};
実行 その 1

コンパイルして threejswebglsample.js を生成し、threejswebglsample.html を Internet Explorer 11 以降等の WebGL 対応の Web ブラウザーで表示してみよう。

threejswebglsample.html
threejswebglsample.html

赤い四角形が表示された。

実際には、立方体なのだが、真横から見ている為単なる正方形に見える。

実行 その 2

これでは面白くないので、アニメーションをさせてみよう。

アニメーションをさせる為に次のようなコードでレンダリングしてみる。 メッシュの角度を少しずつ変化させるコードを requestAnimationFrame で再帰的に呼び出す。

これで立方体が少しずつ回転する。

    static rendering(renderer: THREE.Renderer, camera: THREE.Camera, scene: THREE.Scene, cubeMesh: THREE.Mesh) {
        // メッシュを自転
        cubeMesh.rotation.x += 0.01;
        cubeMesh.rotation.y += 0.01;

        // レンダリング
        renderer.render(scene, camera);
        // 繰り返す
        // - setInterval(render, 1000 / 60); より軽い
        // -- 不要な場合にループを行わない
        // -- 最適化されている
        requestAnimationFrame(() => Application.rendering(renderer, camera, scene, cubeMesh));
    }

このコードを追加すると、threejswebglsample.ts 全体は次のようになる。

/// <reference path="three.d.ts"/>

module ThreeJSWebGLSample {
    export class Application {
        static run() {
            // 1. WebGL レンダラーを作成
            try { var renderer = new THREE.WebGLRenderer({ antialias: true }); } catch (e) {}
            if (renderer == null) {
                alert("お使いの環境では WebGL はご利用いただけません。");
                return;
            }
            // サイズの設定
            renderer.setSize(window.innerWidth, window.innerHeight);
            // 背景色の設定(色, 透明度)
            renderer.setClearColorHex(0x000000, 1);
            // レンダラーによって、viewport の ID を持つ HTML 要素に子要素として canvas を追加
            document.querySelector("#viewport").appendChild(renderer.domElement);

            // 2. カメラを作成
            // (透視投影の) カメラを作成
            var fov                   = 100;                                     // 画角
            var aspect                = window.innerWidth / window.innerHeight;  // 縦横比
            var camera                = new THREE.PerspectiveCamera(fov, aspect);
            camera.position           = new THREE.Vector3(0, 0, 1000);           // z 方向に 1000 ずらす

            // 3. シーン (空間) を作成
            var scene                 = new THREE.Scene();

            // 3.1 ライト (光源) を作成 → シーンに追加
            // 平行光源 (無限遠光源) を作成 
            var directionalLight      = new THREE.DirectionalLight(0xffffff, 1); // 引数: 色, 強さ
            directionalLight.position = new THREE.Vector3(0, 0, 1);              // z 方向から照らす
            // 光源をシーンに追加
            scene.add(directionalLight);

            // 3.2 メッシュを用意 → シーンに追加
            // ジオメトリー (形) とマテリアル (材料) からメッシュを作成

            // プリミティブなジオメトリーを作成
            var geometry = new THREE.CubeGeometry(500, 500, 500);
            // マテリアルを作成 (赤)
            var material = new THREE.MeshLambertMaterial({ color: 0xff0000 });
            // ジオメトリーとマテリアルを合わせてメッシュを作成
            var cubeMesh = new THREE.Mesh(geometry, material);
            // メッシュをシーンに追加
            scene.add(cubeMesh);

            // 4. レンダリング - レンダラーとカメラでシーンをレンダリング
            Application.rendering(renderer, camera, scene, cubeMesh);
        }

        static rendering(renderer: THREE.Renderer, camera: THREE.Camera, scene: THREE.Scene, cubeMesh: THREE.Mesh) {
            // メッシュを自転
            cubeMesh.rotation.x += 0.01;
            cubeMesh.rotation.y += 0.01;

            // レンダリング
            renderer.render(scene, camera);
            // 繰り返す
            // - setInterval(render, 1000 / 60); より軽い
            // -- 不要な場合にループを行わない
            // -- 最適化されている
            requestAnimationFrame(() => Application.rendering(renderer, camera, scene, cubeMesh));
        }
    }
}

// ウィンドウがロードされた時
window.onload = () => {
    // アプリケーションの起動
    ThreeJSWebGLSample.Application.run();
};

コンパイルして表示してみよう。

threejswebglsample.html
threejswebglsample.html

今度は、立方体が回っているのが分かるだろう。

実行 その 3

試しに、マテリアル (材料) を換えてみよう。 例えば、

    // マテリアルを作成 (赤)
    var material = new THREE.MeshLambertMaterial({ color: 0xff0000 });

の部分を、

    // マテリアルを作成 (赤)
    var material = new THREE.MeshPhongMaterial({ color    : 0xff0000,
                                                 specular : 0xcccccc,
                                                 shininess: 50      ,
                                                 ambient  : 0xffffff });

に変えてコンパイルし、表示してみよう。

threejswebglsample.html
threejswebglsample.html

先程のものより艶のある立方体が表示された筈だ。

実際のサンプルを次の場所に用意した。

マテリアル (材料) を変えたり、ジオメトリー (形) を立方体以外にしたり、ライト (証明) を変えたり、アニメーションを変えたり、色々と試してみてほしい。 3D のものが表示されるのは楽しいものだ。

次回は、別のサンプルをご紹介する。

2014年03月01日

[C#][.NET][Roslyn][式木] Room metro #23 大阪「メタプログラミング C#」の資料公開

Room metro #23 大阪 (3月1日) でやったセッションの資料を公開。

サンプル コード

サンプル コードはこちら

2014年03月11日

[Xamarin] Xamarin に関する情報

Xamarin

Xamarin は、iOS、Android、Windows、Mac のアプリケーションを C# で開発できるツールの名称であり、また、そのツールを開発/販売する企業の名称だ。

オープンソースの .NET Framework 互換環境である Mono を基盤としている。前身は、MonoTouchMono for Android 等だ。

Xamarin に関する Web ページ

2014年03月12日

[Xamarin] Xamarin を試した

Xamarin

Xamarin を試した。

  1. iMacMac 上で Windows を動作させるヴァーチャル環境である VMWare Fusion をインストールする。
  2. そのヴァーチャル環境に Windows 8.1 をインストール。
  3. 更にそこに、Visual Studio 2013 と Xamarin の Business Edition をインストール。
  4. これで、Visual Studio には Xamarin for Visual Studio というプラグインがインストールされる。
  5. iMac 自体にも、Xamarin の Business Edition をインストールしておく。
Mac に VMWare Fusion をインストールし、そこに Windows 8.1 をインストール
Mac に VMWare Fusion をインストールし、そこに Windows 8.1 をインストール

Xamarin for Visual Studio での iOS アプリ開発 (Windows 環境 + Mac の両方が必要)

  1. Visual Studio を起動し、新規 iOS アプリを作成。
  2. iOS アプリの起動には Mac が必要。Mac 側で、Xamarin.iOS Build Host を起動しておく。
  3. Visual Studio でビルドし、実行する。この時に、Mac 側での Xamarin.iOS Build Host とペアリングする (Mac と Windows 環境はネットワークで繋がっている必要がある)。
  4. iOS アプリが Mac 側のシミュレータで起動する。
Visual Studio から Xamarin による iOS アプリのシミュレータでの起動 (Mac 上)
Visual Studio から Xamarin による iOS アプリのシミュレータでの起動 (Mac 上)

Xamarin for Visual Studio での Android アプリ開発 (Windows 環境が必要)

  1. Visual Studio を起動し、新規 Android アプリを作成。こちらは Windows 環境の中だけで実行できる。
  2. Visual Studio でビルドし、実行する。
  3. Windows 環境で、Android エミュレータが起動され (時間が掛かる)、Android アプリが Windows 環境で起動する。
Visual Studio から Xamarin による Android アプリのエミュレータでの起動 (Mac 上)
Visual Studio から Xamarin による Android アプリのエミュレータでの起動 (Mac 上)

Mac 上の Xamarin Studio での iOS アプリ開発 (Mac が必要)

Mac (や Windows) 上で Xamarin に含まれる Xamarin Studio を使って iOS アプリを開発する場合は、もっと簡単だ。 Visual Studio と似た IDE で、iOS アプリを新規に作成し、ビルド・実行できる。

Xamarin Studio からの iOS アプリのシミュレータでの起動 (Mac 上)
Xamarin Studio からの iOS アプリのシミュレータでの起動 (Mac 上)

Xamarin Studio での Android アプリ開発 (Windows 環境か Mac の何れかが必要)

Mac (や Windows) 上での Xamarin Studio による Android アプリ開発も同様。

Xamarin Studio からの Android アプリのエミュレータでの起動 (Mac 上)
Xamarin Studio からの Android アプリのエミュレータでの起動 (Mac 上)

2014年03月14日

[Xamarin] MvvmCross の情報

Xamarin

Xamarin は、C#iOSAndroidWindowsMac OS のアプリケーションを開発できるクロス プラットフォーム開発環境だ。

ネイティブにアプリケーションを書けるのを特長としており、それぞれのプラットフォーム用のコードを C# で記述できる。

その為、特化した部分はプラットフォーム毎に別個に書く必要があり、複数のプラットフォームで動作するアプリケーションを効率よく書く為には、いかにコードを共通化するか、が重要となる。

Xamarin は、PCL (Portable Class Library: 複数のプラットフォームに対応可能な .NET Framework のクラス ライブラリ) に対応しており、ここにできるだけ多くのコードを集めたい。 その為には、MVVM (Model-View-ViewModel) パターンで Model と ViewModel の部分を PCL において共通化し、なるべく View の薄い部分だけを個々に記述するようにしたいものだ。 画面の記述は、プラットフォームによって異なるので、個々の画面の記述をできるだけ薄くし、内部処理等を切り離して共通化する訳だ。

Xamarin から利用できる MVVM フレームワーク "MvvmCross" 関連の情報を集めてみた。

2014年03月22日

[Event] MVP Community Camp 2014 北陸会場

MVP Community Camp 2014 (日本語)

昨年まで Community Open Day として全国で一斉に開催されていたマイクロソフト技術の勉強会が、今年は MVP Community Camp 2014 としてアジア パシフィック全体で行われた。

3月17日から21日迄はオンライン イベント、22日はオフライン イベントだった。

オフライン イベントは、例年通り北陸会場でも開催され、39名の方が参加された。

会場の案内板

関連サイト

概要

MVP Community Camp 2014 北陸会場
日時 2014年3月22日
会場 石川工業高等専門学校 (石川県河北郡津幡町)
 
会場
会場
主催 Hokuriku.NET福井情報技術者協会[FITEA]
協力 日本マイクロソフト
サイト MVP Community Camp 2014 北陸会場 - ATND

詳細

タイトル スピーカー 内容 写真
受付        
受付
受付
開始前の様子
開始前の様子
挨拶       ライブストリーミングの視聴
東京会場挨拶
東京会場挨拶
キーノート (基調講演) スクリーンのその先へ Beyond the screen : There and Back Again 東 賢 氏 インフラジスティックス・ジャパン㈱ 代表取締役、MVP ライブストリーミングの視聴
会場の様子
会場の様子
セッション 1 Windows Server 2012 R2 を使ったオンラインサービス連携 澤田 賢也 氏 Windows Server for Small and Medium Business MVP  
澤田 賢也 氏
セッション 2 まだ Windows 8.1 を使っていないの? ~そんなあなたに勧めたい! Windows 8.1 & Surface さくしま たかえ 氏 MVP for Windows Expert-Consumer 資料
さくしま たかえ 氏
ランチタイム   インド料理「るびな」の弁当
インド料理「るびな」の弁当
インド料理「るびな」の弁当
セッション 3 エバンジェリスト養成講座 パワースライド特別編@北陸 西脇 資哲 氏 日本マイクロソフト株式会社 エバンジェリスト  
西脇 資哲 氏
セッション 4 HTML5 時代に必要なスキル 山崎 大助 氏 デジタルハリウッド講師/INOP/U-SYS Microsoft MVP 資料
山崎 大助 氏
セッション 5 C# MVP によるドキドキ・ライブコーディング! 石野 光仁 氏、鈴木 孝明 氏、小島 富治雄 氏 C# MVP  
ライトニングトークス Windows Azure Web サイトと SendGrid で遊ぼう! 芝村 達郎 氏 Microsoft MVP for ASP.NET/IIS 資料
ライトニングトークス
ライトニングトークス
ライトニングトークス
ライトニングトークス
Windows Server 2012 R2 Essentialsの魅力~手間暇かけずに活用しよう! 那須 悟 氏 Microsoft MVP for Windows Home Server 資料
僕と君と、彼と彼女と ~ヨニンヨンショク、四人でリバーシ~ 敷浪俊樹 氏 学生さん  
アップルプラネット 寺本大輝 氏 学生さん  
.NET MicroFrameworkで遊ぼうとしてみた 西永俊文 氏 学生さん  
プレゼント抽選会         プレゼント
懇親会        
懇親会
懇親会

2014年04月11日

[Event] BUILD 2014 からの情報

BUILD

マイクロソフトの開発者会議 BUILD 2014 (4月2–4日、米国サンフランシスコ) とその周辺で発表されたマイクロソフト技術へのリンク。

2014年04月14日

[C#][.NET][Roslyn] Build 2014 でオープンソースになったと発表された Roslyn のソースコードを弄ってみた

BUILD

Build 2014 での Roslyn 関連の発表

先日、マイクロソフトの開発者会議 BUILD 2014 (4月2–4日、米国サンフランシスコ) で Roslyn がオープンソースとして公開されたことが発表された。

BUILD 2014 での Roslyn の発表に関連した記事/サイト

※ その他の BUILD 2014 に関する記事は、 「[Event] BUILD 2014 からの情報」を参照のこと。

Roslyn とは

Roslyn は、C# や Visual Basic のコンパイラーを再実装し、内部の コード分析などの API 等を公開したものだ。

Roslyn に関しては、以前、次にあげる記事で扱った。参考にしてほしい。

Roslyn Preview のインストール

今回は、公開された Roslyn を触ってみたい。

Taking a tour of Roslyn - C# Frequently Asked Questions - MSDN Blogs」にやり方が書いてある。

先ずは、Roslyn Preview のインストールからだ。

Roslyn の Preview Site に行き、Roslyn Preview をダウンロードする。マイクロソフト アカウントが必要だ (持っていない場合は新規登録すれば OK)。

ダウンロードした Roslyn SDK Preview.zip の中にある次のファイルをインストールする。

  • Roslyn End User Preview.vsix
  • Roslyn SDK Project Templates.vsix
  • Roslyn Syntax Visualizer.vsix

もし Visual Studio 2013 SDK がインストールされていない場合はこちらも必要だ。 ちなみに Visual Studio Express Edition にはインストールできない。 Professional Edition 以上が必要だ。

これらをインストールして Visual Studio を起動する。 すると、「ファイル」 - 「新規作成」 - 「プロジェクト」を開くと、「テンプレート」 - 「Visual C#」の下に「Roslyn」が増えていて、「Console Application」、「Code Refactoring」、「Diagnostic with Code Fix」が選択できるようになっている。 「Visual Basic」の下も同様だ。

Roslyn のプロジェクト テンプレート
Roslyn のプロジェクト テンプレート

Roslyn による診断機能やリファクタリング機能を作成することができるようになっている。

これらについては、後日別の記事で試そうと思う。 「The Future of C# | Build 2014 | Channel 9」でそれらの作成手順を確認することができる。

Roslyn のソースコードのダウンロード

次に Roslyn のソースコードをダウンロードしてみよう。

オープンソースとなった Roslyn のソースコードは、「.NET Compiler Platform ("Roslyn") - CodePlex」からダウンロードすることができる。

  1. このサイトの Source Code のページへ行く。
  2. "Clone" を選び URL をコピーする。

  3. Visual Studio の「チーム エクスプローラー」を開く。
  4. 「チーム プロジェクトへの接続」を選ぶ。
  5. 「ローカル Git リポジトリ」の「複製」を選ぶ。
  6. 上のテキスト ボックスに、上でコピーした URL を入力し、上のテキスト ボックスにローカル マシン上のソースコードの格納場所 (例えば C:\Source\Roslyn) を入力し、「複製」ボタンを押す。

これで、ローカル マシンの指定した場所に Roslyn のソースコードがダウンロードされる。

では、この中から Roslyn.sln を Visual Studio で開いてみよう。

ソリューション 'Roslyn' - ソリューション エクスプローラー

沢山のソースコードが確認できる。

Roslyn のソースコードのビルド

この儘 Visual Studio でビルドしてみよう。 ビルドが完了すると、Binaries というフォルダーの下に rcsc.exe (C# コンパイラー) や rvbc.exe (Visual Basic コンパイラー) などが作られる。

rcsc.exe (C# コンパイラー) を試してみよう。

今回は、"Hello world!" を表示する次のような C# のファイル hello.cs を (C:\Source\Roslyn\Test に) 作成した。

class Program
{
     static void Main()
     {
          System.Console.WriteLine("Hello world!");
     }
}

コマンドラインで、これをコンパイルしてみる。

C:\Source\Roslyn\Test>"C:\Source\Roslyn\Binaries\Debug\rcsc.exe" hello.cs
Microsoft (R) Visual C# Compiler version 0.7.0.0
Copyright (C) Microsoft Corporation. All rights reserved.

無事ビルドされ、hello.exe が作られる。

実行してみよう。

C:\Source\Roslyn\Test>hello
Hello world!

Hello world! と表示された。

Roslyn のソースコードを弄ってみる

折角ソースコードがあるので、「Taking a tour of Roslyn」を真似て、試しに Roslyn のソースコードを少し弄ってみることにする。

例えば、キーワード "class" の代わりに "クラス" を使うようにしてみよう。

変更するのは、C:\Source\Roslyn\Src\Compilers\CSharp\Source\Syntax\SyntaxKindFacts.cs だ。

このファイル中の "class" を "クラス" に文字列置換する。

    case "class":

    case "クラス":

に変更。

    return "class";

    return "クラス";

に変更。

次に、文字列リテラルで使われるダブル クォーテーションの代わりに鉤括弧 「」 も使えるようにしてみよう。

変更するのは、C:\Source\Roslyn\Src\Compilers\CSharp\Source\Parser\Lexer.cs だ。

            character = TextWindow.PeekChar();
            switch (character)
            {
                case '\"':
                case '\'':

            character = TextWindow.PeekChar();
            switch (character)
            {
                case '\"':
                case '\'':
                case '「':

に変更。

        private bool ScanStringLiteral(ref TokenInfo info, bool allowEscapes = true)
        {
            var quoteCharacter = TextWindow.PeekChar();
            if (quoteCharacter == '\'' || quoteCharacter == '"')
            {

        private bool ScanStringLiteral(ref TokenInfo info, bool allowEscapes = true)
        {
            var quoteCharacter = TextWindow.PeekChar();
            if (quoteCharacter == '\'' || quoteCharacter == '"' || quoteCharacter == '「')
            {

に変更。

                    else if (ch == quoteCharacter)
                    {
                        TextWindow.AdvanceChar();
                        break;
                    }

                    else if (ch == quoteCharacter || (ch == '」' && quoteCharacter == '「'))
                    {
                        TextWindow.AdvanceChar();
                        break;
                    }

に変更。

変更した Roslyn をビルドして使ってみよう。

今回用意するのは、次のような hello.cs だ。 "class" を"クラス"にし、ダブル クオーテーションを鉤括弧にした。

クラス プログラム
{
    static void Main()
    {
        System.Console.WriteLine(「こんにちは。ロズリン。」);
    }
}

新しい rcsc.exe でビルドする。

C:\Source\Roslyn\Test>"C:\Source\Roslyn\Binaries\Debug\rcsc.exe" hello.cs
Microsoft (R) Visual C# Compiler version 0.7.0.0
Copyright (C) Microsoft Corporation. All rights reserved.

無事ビルドされ、hello.exe が作られる。

実行してみよう。

C:\Source\Roslyn\Test>hello
こんにちは。ロズリン。

うまくいったようだ。

まとめ

今回は、オープンソースとなった Roslyn を弄ってみた。 次回は、新しい Roslyn Preview を使ってコードの診断やリファクタリングの機能を作ってみたい。

2014年04月22日

[Xamarin] Xamarin 勉強会の資料

Xamarin

Xamarin 勉強会用に資料を作ってみた。

2014年05月01日

[C#] C# 3.0 以降の資料

C# 3.0 以降の勉強会用に資料を作ってみた。

2014年05月09日

[TypeScript][WebGL][Three.js] 多体問題シミュレーション WebGL版

typescriptlogo.png

『[TypeScript][WebGL][Three.js] 3D で遊ぼう ~C#er も TypeScript で楽々 WebGL~ ― 準備編』の続き。

以前、HTML5 で多体問題シミュレーションというのを作った。

今回は、これを WebGL でやってみよう。

実装には、にやったように同様これを WebGL でやってみよう。

WebGL 対応のブラウザ (Internet Explorer、Firefox、Google Chrome、の現時点での最新版等) では、画像のクリックで実際に動作させてみることができる。

使い方等:

  • 物体は球体として表現し、見掛け上の半径は各質量の3分の1乗の比になるようにした。 但し、衝突は考慮していない。
  • 裸眼立体視版では、「交差法」により立体的に見ることができる。
  • クリックでスケールの変更。
  • クエリー文字列:
    • number で、物体の数の設定 (nbody.html?number=8 で 8 個。1-10個、デフォルト 5 個)
  • Internet Explorer、Firefox、Google Chrome、の現時点での最新版等 WebGL をサポートするブラウザで動作

例.

参考:

ソースコード:

ソースコードは TypeScript で書かれていて、three.js を使用している。

こちら: NBodyWebGL.Source.zip

クラス図:

TypeScript を使うことで、クラス設計も楽に行うことができる。

多体問題シミュレーション WebGL版のクラス図
多体問題シミュレーション WebGL版のクラス図

2014年08月01日

[C#] 「世界一IQの低いソースコードはこれ。」のような if 文を書いた場合の速度を測ってみた

「世界一IQの低いソースコードはこれ。」で始まる tweet が興味深かった。

https://twitter.com/vjroba/status/494882208788660226

世界一IQの低いソースコードはこれ。

if と else を使って KeyEvent クラスの中の 200 を超える数の定数と一つずつ比較して、文字列に変換している Java のコードだ。

色々リプライされているようだが、「初心者向けの入門書なので、リフレクションや連想配列を (switch すら) 使わない方針で書いている」のかも知れない。

しかし、何のために、こんな方法を使ってまで KeyEvent の中の定数を文字列にしてるのかは不明だ。

初心者向けサンプルにこういう「実務で余り役に立たないし、手本にならないコード」を選んでいることが間違っているように思う。

C# で試した

コードの良し悪しについては議論するまでもないような気がするし、定数名を文字列に変換する必要性が生じた時点でおかしい気がするが、if をこんなに続けた場合のパフォーマンスが気になったので、C# で試した。

以下では、あえて if を使って定数名を文字列に変換し、switch や Dictinary などを使った場合と処理時間を比較してみたい。

Sample クラス

先ず、225 個の const int を持つ Sample クラス。あえて enum は使っていない。

※ ぞっとするようなこのクラスや後述する else if の繰り返し, case の繰り返しのソースコードは、Enumerable.Range(1,225).ToList().ForEach(number => Console.WriteLine(適当なフォーマット文字列, number)) で作った。

public static class Sample
{
    public const int CONST_001 =   1;
    public const int CONST_002 =   2;
    public const int CONST_003 =   3;
    public const int CONST_004 =   4;
    public const int CONST_005 =   5;
    public const int CONST_006 =   6;
    public const int CONST_007 =   7;
    public const int CONST_008 =   8;
    public const int CONST_009 =   9;
    public const int CONST_010 =  10;
    public const int CONST_011 =  11;
    public const int CONST_012 =  12;
    public const int CONST_013 =  13;
    public const int CONST_014 =  14;
    public const int CONST_015 =  15;
    public const int CONST_016 =  16;
    public const int CONST_017 =  17;
    public const int CONST_018 =  18;
    public const int CONST_019 =  19;
    public const int CONST_020 =  20;
    public const int CONST_021 =  21;
    public const int CONST_022 =  22;
    public const int CONST_023 =  23;
    public const int CONST_024 =  24;
    public const int CONST_025 =  25;
    public const int CONST_026 =  26;
    public const int CONST_027 =  27;
    public const int CONST_028 =  28;
    public const int CONST_029 =  29;
    public const int CONST_030 =  30;
    public const int CONST_031 =  31;
    public const int CONST_032 =  32;
    public const int CONST_033 =  33;
    public const int CONST_034 =  34;
    public const int CONST_035 =  35;
    public const int CONST_036 =  36;
    public const int CONST_037 =  37;
    public const int CONST_038 =  38;
    public const int CONST_039 =  39;
    public const int CONST_040 =  40;
    public const int CONST_041 =  41;
    public const int CONST_042 =  42;
    public const int CONST_043 =  43;
    public const int CONST_044 =  44;
    public const int CONST_045 =  45;
    public const int CONST_046 =  46;
    public const int CONST_047 =  47;
    public const int CONST_048 =  48;
    public const int CONST_049 =  49;
    public const int CONST_050 =  50;
    public const int CONST_051 =  51;
    public const int CONST_052 =  52;
    public const int CONST_053 =  53;
    public const int CONST_054 =  54;
    public const int CONST_055 =  55;
    public const int CONST_056 =  56;
    public const int CONST_057 =  57;
    public const int CONST_058 =  58;
    public const int CONST_059 =  59;
    public const int CONST_060 =  60;
    public const int CONST_061 =  61;
    public const int CONST_062 =  62;
    public const int CONST_063 =  63;
    public const int CONST_064 =  64;
    public const int CONST_065 =  65;
    public const int CONST_066 =  66;
    public const int CONST_067 =  67;
    public const int CONST_068 =  68;
    public const int CONST_069 =  69;
    public const int CONST_070 =  70;
    public const int CONST_071 =  71;
    public const int CONST_072 =  72;
    public const int CONST_073 =  73;
    public const int CONST_074 =  74;
    public const int CONST_075 =  75;
    public const int CONST_076 =  76;
    public const int CONST_077 =  77;
    public const int CONST_078 =  78;
    public const int CONST_079 =  79;
    public const int CONST_080 =  80;
    public const int CONST_081 =  81;
    public const int CONST_082 =  82;
    public const int CONST_083 =  83;
    public const int CONST_084 =  84;
    public const int CONST_085 =  85;
    public const int CONST_086 =  86;
    public const int CONST_087 =  87;
    public const int CONST_088 =  88;
    public const int CONST_089 =  89;
    public const int CONST_090 =  90;
    public const int CONST_091 =  91;
    public const int CONST_092 =  92;
    public const int CONST_093 =  93;
    public const int CONST_094 =  94;
    public const int CONST_095 =  95;
    public const int CONST_096 =  96;
    public const int CONST_097 =  97;
    public const int CONST_098 =  98;
    public const int CONST_099 =  99;
    public const int CONST_100 = 100;
    public const int CONST_101 = 101;
    public const int CONST_102 = 102;
    public const int CONST_103 = 103;
    public const int CONST_104 = 104;
    public const int CONST_105 = 105;
    public const int CONST_106 = 106;
    public const int CONST_107 = 107;
    public const int CONST_108 = 108;
    public const int CONST_109 = 109;
    public const int CONST_110 = 110;
    public const int CONST_111 = 111;
    public const int CONST_112 = 112;
    public const int CONST_113 = 113;
    public const int CONST_114 = 114;
    public const int CONST_115 = 115;
    public const int CONST_116 = 116;
    public const int CONST_117 = 117;
    public const int CONST_118 = 118;
    public const int CONST_119 = 119;
    public const int CONST_120 = 120;
    public const int CONST_121 = 121;
    public const int CONST_122 = 122;
    public const int CONST_123 = 123;
    public const int CONST_124 = 124;
    public const int CONST_125 = 125;
    public const int CONST_126 = 126;
    public const int CONST_127 = 127;
    public const int CONST_128 = 128;
    public const int CONST_129 = 129;
    public const int CONST_130 = 130;
    public const int CONST_131 = 131;
    public const int CONST_132 = 132;
    public const int CONST_133 = 133;
    public const int CONST_134 = 134;
    public const int CONST_135 = 135;
    public const int CONST_136 = 136;
    public const int CONST_137 = 137;
    public const int CONST_138 = 138;
    public const int CONST_139 = 139;
    public const int CONST_140 = 140;
    public const int CONST_141 = 141;
    public const int CONST_142 = 142;
    public const int CONST_143 = 143;
    public const int CONST_144 = 144;
    public const int CONST_145 = 145;
    public const int CONST_146 = 146;
    public const int CONST_147 = 147;
    public const int CONST_148 = 148;
    public const int CONST_149 = 149;
    public const int CONST_150 = 150;
    public const int CONST_151 = 151;
    public const int CONST_152 = 152;
    public const int CONST_153 = 153;
    public const int CONST_154 = 154;
    public const int CONST_155 = 155;
    public const int CONST_156 = 156;
    public const int CONST_157 = 157;
    public const int CONST_158 = 158;
    public const int CONST_159 = 159;
    public const int CONST_160 = 160;
    public const int CONST_161 = 161;
    public const int CONST_162 = 162;
    public const int CONST_163 = 163;
    public const int CONST_164 = 164;
    public const int CONST_165 = 165;
    public const int CONST_166 = 166;
    public const int CONST_167 = 167;
    public const int CONST_168 = 168;
    public const int CONST_169 = 169;
    public const int CONST_170 = 170;
    public const int CONST_171 = 171;
    public const int CONST_172 = 172;
    public const int CONST_173 = 173;
    public const int CONST_174 = 174;
    public const int CONST_175 = 175;
    public const int CONST_176 = 176;
    public const int CONST_177 = 177;
    public const int CONST_178 = 178;
    public const int CONST_179 = 179;
    public const int CONST_180 = 180;
    public const int CONST_181 = 181;
    public const int CONST_182 = 182;
    public const int CONST_183 = 183;
    public const int CONST_184 = 184;
    public const int CONST_185 = 185;
    public const int CONST_186 = 186;
    public const int CONST_187 = 187;
    public const int CONST_188 = 188;
    public const int CONST_189 = 189;
    public const int CONST_190 = 190;
    public const int CONST_191 = 191;
    public const int CONST_192 = 192;
    public const int CONST_193 = 193;
    public const int CONST_194 = 194;
    public const int CONST_195 = 195;
    public const int CONST_196 = 196;
    public const int CONST_197 = 197;
    public const int CONST_198 = 198;
    public const int CONST_199 = 199;
    public const int CONST_200 = 200;
    public const int CONST_201 = 201;
    public const int CONST_202 = 202;
    public const int CONST_203 = 203;
    public const int CONST_204 = 204;
    public const int CONST_205 = 205;
    public const int CONST_206 = 206;
    public const int CONST_207 = 207;
    public const int CONST_208 = 208;
    public const int CONST_209 = 209;
    public const int CONST_210 = 210;
    public const int CONST_211 = 211;
    public const int CONST_212 = 212;
    public const int CONST_213 = 213;
    public const int CONST_214 = 214;
    public const int CONST_215 = 215;
    public const int CONST_216 = 216;
    public const int CONST_217 = 217;
    public const int CONST_218 = 218;
    public const int CONST_219 = 219;
    public const int CONST_220 = 220;
    public const int CONST_221 = 221;
    public const int CONST_222 = 222;
    public const int CONST_223 = 223;
    public const int CONST_224 = 224;
    public const int CONST_225 = 225;
}
PerformanceTester クラス

次に、実行時間測定用に PerformanceTester というクラスを用意した。

using System;
using System.Diagnostics;

public static class PerformanceTester
{
    static Stopwatch stopwatch = new Stopwatch();

    public static double Test(Action action, long times)
    {
        stopwatch.Restart();
        for (var counter = 0L; counter < times; counter++)
            action();
        stopwatch.Stop();
        return stopwatch.ElapsedMilliseconds / 1000.0;
    }
}
IfPerformanceTestProgram クラス

最後に Main を含んだクラスだ。

このクラスには、次の3種の「int の値を渡すと定数名を返す」メソッドがある。

  1. tweet されていた書籍にあったように、if と else で書いた IfSample
  2. switch - case で書いた SwitchSample
  3. Dictionary で書いた ReflectionAndDictionarySample

最後の Dictionary で書いたものは、初期化の部分にリフレクションを使った ConstantsTable クラスを使っている。

毎回リフレクションで取得することも可能だが、そうすると、3桁位実行速度が落ちてしまう。

そこで、最初にリフレクションで、フィールドの値と名前の一覧を取得し、値をキーとして Dictionary に格納する。

その Dictionary を使って、値から文字列を得る仕組みだ。

Test メソッドは、「渡されたメソッドを使って1~255の値を定数名に変換する」処理を 100,000回実行する時間を測り、それを表示する。

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

class IfPerformanceTestProgram
{
    static string IfSample(int value)
    {
        string result;
        if      (value == Sample.CONST_001) result = "CONST_001";
        else if (value == Sample.CONST_002) result = "CONST_002";
        else if (value == Sample.CONST_003) result = "CONST_003";
        else if (value == Sample.CONST_004) result = "CONST_004";
        else if (value == Sample.CONST_005) result = "CONST_005";
        else if (value == Sample.CONST_006) result = "CONST_006";
        else if (value == Sample.CONST_007) result = "CONST_007";
        else if (value == Sample.CONST_008) result = "CONST_008";
        else if (value == Sample.CONST_009) result = "CONST_009";
        else if (value == Sample.CONST_010) result = "CONST_010";
        else if (value == Sample.CONST_011) result = "CONST_011";
        else if (value == Sample.CONST_012) result = "CONST_012";
        else if (value == Sample.CONST_013) result = "CONST_013";
        else if (value == Sample.CONST_014) result = "CONST_014";
        else if (value == Sample.CONST_015) result = "CONST_015";
        else if (value == Sample.CONST_016) result = "CONST_016";
        else if (value == Sample.CONST_017) result = "CONST_017";
        else if (value == Sample.CONST_018) result = "CONST_018";
        else if (value == Sample.CONST_019) result = "CONST_019";
        else if (value == Sample.CONST_020) result = "CONST_020";
        else if (value == Sample.CONST_021) result = "CONST_021";
        else if (value == Sample.CONST_022) result = "CONST_022";
        else if (value == Sample.CONST_023) result = "CONST_023";
        else if (value == Sample.CONST_024) result = "CONST_024";
        else if (value == Sample.CONST_025) result = "CONST_025";
        else if (value == Sample.CONST_026) result = "CONST_026";
        else if (value == Sample.CONST_027) result = "CONST_027";
        else if (value == Sample.CONST_028) result = "CONST_028";
        else if (value == Sample.CONST_029) result = "CONST_029";
        else if (value == Sample.CONST_030) result = "CONST_030";
        else if (value == Sample.CONST_031) result = "CONST_031";
        else if (value == Sample.CONST_032) result = "CONST_032";
        else if (value == Sample.CONST_033) result = "CONST_033";
        else if (value == Sample.CONST_034) result = "CONST_034";
        else if (value == Sample.CONST_035) result = "CONST_035";
        else if (value == Sample.CONST_036) result = "CONST_036";
        else if (value == Sample.CONST_037) result = "CONST_037";
        else if (value == Sample.CONST_038) result = "CONST_038";
        else if (value == Sample.CONST_039) result = "CONST_039";
        else if (value == Sample.CONST_040) result = "CONST_040";
        else if (value == Sample.CONST_041) result = "CONST_041";
        else if (value == Sample.CONST_042) result = "CONST_042";
        else if (value == Sample.CONST_043) result = "CONST_043";
        else if (value == Sample.CONST_044) result = "CONST_044";
        else if (value == Sample.CONST_045) result = "CONST_045";
        else if (value == Sample.CONST_046) result = "CONST_046";
        else if (value == Sample.CONST_047) result = "CONST_047";
        else if (value == Sample.CONST_048) result = "CONST_048";
        else if (value == Sample.CONST_049) result = "CONST_049";
        else if (value == Sample.CONST_050) result = "CONST_050";
        else if (value == Sample.CONST_051) result = "CONST_051";
        else if (value == Sample.CONST_052) result = "CONST_052";
        else if (value == Sample.CONST_053) result = "CONST_053";
        else if (value == Sample.CONST_054) result = "CONST_054";
        else if (value == Sample.CONST_055) result = "CONST_055";
        else if (value == Sample.CONST_056) result = "CONST_056";
        else if (value == Sample.CONST_057) result = "CONST_057";
        else if (value == Sample.CONST_058) result = "CONST_058";
        else if (value == Sample.CONST_059) result = "CONST_059";
        else if (value == Sample.CONST_060) result = "CONST_060";
        else if (value == Sample.CONST_061) result = "CONST_061";
        else if (value == Sample.CONST_062) result = "CONST_062";
        else if (value == Sample.CONST_063) result = "CONST_063";
        else if (value == Sample.CONST_064) result = "CONST_064";
        else if (value == Sample.CONST_065) result = "CONST_065";
        else if (value == Sample.CONST_066) result = "CONST_066";
        else if (value == Sample.CONST_067) result = "CONST_067";
        else if (value == Sample.CONST_068) result = "CONST_068";
        else if (value == Sample.CONST_069) result = "CONST_069";
        else if (value == Sample.CONST_070) result = "CONST_070";
        else if (value == Sample.CONST_071) result = "CONST_071";
        else if (value == Sample.CONST_072) result = "CONST_072";
        else if (value == Sample.CONST_073) result = "CONST_073";
        else if (value == Sample.CONST_074) result = "CONST_074";
        else if (value == Sample.CONST_075) result = "CONST_075";
        else if (value == Sample.CONST_076) result = "CONST_076";
        else if (value == Sample.CONST_077) result = "CONST_077";
        else if (value == Sample.CONST_078) result = "CONST_078";
        else if (value == Sample.CONST_079) result = "CONST_079";
        else if (value == Sample.CONST_080) result = "CONST_080";
        else if (value == Sample.CONST_081) result = "CONST_081";
        else if (value == Sample.CONST_082) result = "CONST_082";
        else if (value == Sample.CONST_083) result = "CONST_083";
        else if (value == Sample.CONST_084) result = "CONST_084";
        else if (value == Sample.CONST_085) result = "CONST_085";
        else if (value == Sample.CONST_086) result = "CONST_086";
        else if (value == Sample.CONST_087) result = "CONST_087";
        else if (value == Sample.CONST_088) result = "CONST_088";
        else if (value == Sample.CONST_089) result = "CONST_089";
        else if (value == Sample.CONST_090) result = "CONST_090";
        else if (value == Sample.CONST_091) result = "CONST_091";
        else if (value == Sample.CONST_092) result = "CONST_092";
        else if (value == Sample.CONST_093) result = "CONST_093";
        else if (value == Sample.CONST_094) result = "CONST_094";
        else if (value == Sample.CONST_095) result = "CONST_095";
        else if (value == Sample.CONST_096) result = "CONST_096";
        else if (value == Sample.CONST_097) result = "CONST_097";
        else if (value == Sample.CONST_098) result = "CONST_098";
        else if (value == Sample.CONST_099) result = "CONST_099";
        else if (value == Sample.CONST_100) result = "CONST_100";
        else if (value == Sample.CONST_101) result = "CONST_101";
        else if (value == Sample.CONST_102) result = "CONST_102";
        else if (value == Sample.CONST_103) result = "CONST_103";
        else if (value == Sample.CONST_104) result = "CONST_104";
        else if (value == Sample.CONST_105) result = "CONST_105";
        else if (value == Sample.CONST_106) result = "CONST_106";
        else if (value == Sample.CONST_107) result = "CONST_107";
        else if (value == Sample.CONST_108) result = "CONST_108";
        else if (value == Sample.CONST_109) result = "CONST_109";
        else if (value == Sample.CONST_110) result = "CONST_110";
        else if (value == Sample.CONST_111) result = "CONST_111";
        else if (value == Sample.CONST_112) result = "CONST_112";
        else if (value == Sample.CONST_113) result = "CONST_113";
        else if (value == Sample.CONST_114) result = "CONST_114";
        else if (value == Sample.CONST_115) result = "CONST_115";
        else if (value == Sample.CONST_116) result = "CONST_116";
        else if (value == Sample.CONST_117) result = "CONST_117";
        else if (value == Sample.CONST_118) result = "CONST_118";
        else if (value == Sample.CONST_119) result = "CONST_119";
        else if (value == Sample.CONST_120) result = "CONST_120";
        else if (value == Sample.CONST_121) result = "CONST_121";
        else if (value == Sample.CONST_122) result = "CONST_122";
        else if (value == Sample.CONST_123) result = "CONST_123";
        else if (value == Sample.CONST_124) result = "CONST_124";
        else if (value == Sample.CONST_125) result = "CONST_125";
        else if (value == Sample.CONST_126) result = "CONST_126";
        else if (value == Sample.CONST_127) result = "CONST_127";
        else if (value == Sample.CONST_128) result = "CONST_128";
        else if (value == Sample.CONST_129) result = "CONST_129";
        else if (value == Sample.CONST_130) result = "CONST_130";
        else if (value == Sample.CONST_131) result = "CONST_131";
        else if (value == Sample.CONST_132) result = "CONST_132";
        else if (value == Sample.CONST_133) result = "CONST_133";
        else if (value == Sample.CONST_134) result = "CONST_134";
        else if (value == Sample.CONST_135) result = "CONST_135";
        else if (value == Sample.CONST_136) result = "CONST_136";
        else if (value == Sample.CONST_137) result = "CONST_137";
        else if (value == Sample.CONST_138) result = "CONST_138";
        else if (value == Sample.CONST_139) result = "CONST_139";
        else if (value == Sample.CONST_140) result = "CONST_140";
        else if (value == Sample.CONST_141) result = "CONST_141";
        else if (value == Sample.CONST_142) result = "CONST_142";
        else if (value == Sample.CONST_143) result = "CONST_143";
        else if (value == Sample.CONST_144) result = "CONST_144";
        else if (value == Sample.CONST_145) result = "CONST_145";
        else if (value == Sample.CONST_146) result = "CONST_146";
        else if (value == Sample.CONST_147) result = "CONST_147";
        else if (value == Sample.CONST_148) result = "CONST_148";
        else if (value == Sample.CONST_149) result = "CONST_149";
        else if (value == Sample.CONST_150) result = "CONST_150";
        else if (value == Sample.CONST_151) result = "CONST_151";
        else if (value == Sample.CONST_152) result = "CONST_152";
        else if (value == Sample.CONST_153) result = "CONST_153";
        else if (value == Sample.CONST_154) result = "CONST_154";
        else if (value == Sample.CONST_155) result = "CONST_155";
        else if (value == Sample.CONST_156) result = "CONST_156";
        else if (value == Sample.CONST_157) result = "CONST_157";
        else if (value == Sample.CONST_158) result = "CONST_158";
        else if (value == Sample.CONST_159) result = "CONST_159";
        else if (value == Sample.CONST_160) result = "CONST_160";
        else if (value == Sample.CONST_161) result = "CONST_161";
        else if (value == Sample.CONST_162) result = "CONST_162";
        else if (value == Sample.CONST_163) result = "CONST_163";
        else if (value == Sample.CONST_164) result = "CONST_164";
        else if (value == Sample.CONST_165) result = "CONST_165";
        else if (value == Sample.CONST_166) result = "CONST_166";
        else if (value == Sample.CONST_167) result = "CONST_167";
        else if (value == Sample.CONST_168) result = "CONST_168";
        else if (value == Sample.CONST_169) result = "CONST_169";
        else if (value == Sample.CONST_170) result = "CONST_170";
        else if (value == Sample.CONST_171) result = "CONST_171";
        else if (value == Sample.CONST_172) result = "CONST_172";
        else if (value == Sample.CONST_173) result = "CONST_173";
        else if (value == Sample.CONST_174) result = "CONST_174";
        else if (value == Sample.CONST_175) result = "CONST_175";
        else if (value == Sample.CONST_176) result = "CONST_176";
        else if (value == Sample.CONST_177) result = "CONST_177";
        else if (value == Sample.CONST_178) result = "CONST_178";
        else if (value == Sample.CONST_179) result = "CONST_179";
        else if (value == Sample.CONST_180) result = "CONST_180";
        else if (value == Sample.CONST_181) result = "CONST_181";
        else if (value == Sample.CONST_182) result = "CONST_182";
        else if (value == Sample.CONST_183) result = "CONST_183";
        else if (value == Sample.CONST_184) result = "CONST_184";
        else if (value == Sample.CONST_185) result = "CONST_185";
        else if (value == Sample.CONST_186) result = "CONST_186";
        else if (value == Sample.CONST_187) result = "CONST_187";
        else if (value == Sample.CONST_188) result = "CONST_188";
        else if (value == Sample.CONST_189) result = "CONST_189";
        else if (value == Sample.CONST_190) result = "CONST_190";
        else if (value == Sample.CONST_191) result = "CONST_191";
        else if (value == Sample.CONST_192) result = "CONST_192";
        else if (value == Sample.CONST_193) result = "CONST_193";
        else if (value == Sample.CONST_194) result = "CONST_194";
        else if (value == Sample.CONST_195) result = "CONST_195";
        else if (value == Sample.CONST_196) result = "CONST_196";
        else if (value == Sample.CONST_197) result = "CONST_197";
        else if (value == Sample.CONST_198) result = "CONST_198";
        else if (value == Sample.CONST_199) result = "CONST_199";
        else if (value == Sample.CONST_200) result = "CONST_200";
        else if (value == Sample.CONST_201) result = "CONST_201";
        else if (value == Sample.CONST_202) result = "CONST_202";
        else if (value == Sample.CONST_203) result = "CONST_203";
        else if (value == Sample.CONST_204) result = "CONST_204";
        else if (value == Sample.CONST_205) result = "CONST_205";
        else if (value == Sample.CONST_206) result = "CONST_206";
        else if (value == Sample.CONST_207) result = "CONST_207";
        else if (value == Sample.CONST_208) result = "CONST_208";
        else if (value == Sample.CONST_209) result = "CONST_209";
        else if (value == Sample.CONST_210) result = "CONST_210";
        else if (value == Sample.CONST_211) result = "CONST_211";
        else if (value == Sample.CONST_212) result = "CONST_212";
        else if (value == Sample.CONST_213) result = "CONST_213";
        else if (value == Sample.CONST_214) result = "CONST_214";
        else if (value == Sample.CONST_215) result = "CONST_215";
        else if (value == Sample.CONST_216) result = "CONST_216";
        else if (value == Sample.CONST_217) result = "CONST_217";
        else if (value == Sample.CONST_218) result = "CONST_218";
        else if (value == Sample.CONST_219) result = "CONST_219";
        else if (value == Sample.CONST_220) result = "CONST_220";
        else if (value == Sample.CONST_221) result = "CONST_221";
        else if (value == Sample.CONST_222) result = "CONST_222";
        else if (value == Sample.CONST_223) result = "CONST_223";
        else if (value == Sample.CONST_224) result = "CONST_224";
        else if (value == Sample.CONST_225) result = "CONST_225";
        else                                result = ""         ;
        return result;
    }

    static string SwitchSample(int value)
    {
        string result;
        switch (value) {
            case Sample.CONST_001: result = "CONST_001"; break;
            case Sample.CONST_002: result = "CONST_002"; break;
            case Sample.CONST_003: result = "CONST_003"; break;
            case Sample.CONST_004: result = "CONST_004"; break;
            case Sample.CONST_005: result = "CONST_005"; break;
            case Sample.CONST_006: result = "CONST_006"; break;
            case Sample.CONST_007: result = "CONST_007"; break;
            case Sample.CONST_008: result = "CONST_008"; break;
            case Sample.CONST_009: result = "CONST_009"; break;
            case Sample.CONST_010: result = "CONST_010"; break;
            case Sample.CONST_011: result = "CONST_011"; break;
            case Sample.CONST_012: result = "CONST_012"; break;
            case Sample.CONST_013: result = "CONST_013"; break;
            case Sample.CONST_014: result = "CONST_014"; break;
            case Sample.CONST_015: result = "CONST_015"; break;
            case Sample.CONST_016: result = "CONST_016"; break;
            case Sample.CONST_017: result = "CONST_017"; break;
            case Sample.CONST_018: result = "CONST_018"; break;
            case Sample.CONST_019: result = "CONST_019"; break;
            case Sample.CONST_020: result = "CONST_020"; break;
            case Sample.CONST_021: result = "CONST_021"; break;
            case Sample.CONST_022: result = "CONST_022"; break;
            case Sample.CONST_023: result = "CONST_023"; break;
            case Sample.CONST_024: result = "CONST_024"; break;
            case Sample.CONST_025: result = "CONST_025"; break;
            case Sample.CONST_026: result = "CONST_026"; break;
            case Sample.CONST_027: result = "CONST_027"; break;
            case Sample.CONST_028: result = "CONST_028"; break;
            case Sample.CONST_029: result = "CONST_029"; break;
            case Sample.CONST_030: result = "CONST_030"; break;
            case Sample.CONST_031: result = "CONST_031"; break;
            case Sample.CONST_032: result = "CONST_032"; break;
            case Sample.CONST_033: result = "CONST_033"; break;
            case Sample.CONST_034: result = "CONST_034"; break;
            case Sample.CONST_035: result = "CONST_035"; break;
            case Sample.CONST_036: result = "CONST_036"; break;
            case Sample.CONST_037: result = "CONST_037"; break;
            case Sample.CONST_038: result = "CONST_038"; break;
            case Sample.CONST_039: result = "CONST_039"; break;
            case Sample.CONST_040: result = "CONST_040"; break;
            case Sample.CONST_041: result = "CONST_041"; break;
            case Sample.CONST_042: result = "CONST_042"; break;
            case Sample.CONST_043: result = "CONST_043"; break;
            case Sample.CONST_044: result = "CONST_044"; break;
            case Sample.CONST_045: result = "CONST_045"; break;
            case Sample.CONST_046: result = "CONST_046"; break;
            case Sample.CONST_047: result = "CONST_047"; break;
            case Sample.CONST_048: result = "CONST_048"; break;
            case Sample.CONST_049: result = "CONST_049"; break;
            case Sample.CONST_050: result = "CONST_050"; break;
            case Sample.CONST_051: result = "CONST_051"; break;
            case Sample.CONST_052: result = "CONST_052"; break;
            case Sample.CONST_053: result = "CONST_053"; break;
            case Sample.CONST_054: result = "CONST_054"; break;
            case Sample.CONST_055: result = "CONST_055"; break;
            case Sample.CONST_056: result = "CONST_056"; break;
            case Sample.CONST_057: result = "CONST_057"; break;
            case Sample.CONST_058: result = "CONST_058"; break;
            case Sample.CONST_059: result = "CONST_059"; break;
            case Sample.CONST_060: result = "CONST_060"; break;
            case Sample.CONST_061: result = "CONST_061"; break;
            case Sample.CONST_062: result = "CONST_062"; break;
            case Sample.CONST_063: result = "CONST_063"; break;
            case Sample.CONST_064: result = "CONST_064"; break;
            case Sample.CONST_065: result = "CONST_065"; break;
            case Sample.CONST_066: result = "CONST_066"; break;
            case Sample.CONST_067: result = "CONST_067"; break;
            case Sample.CONST_068: result = "CONST_068"; break;
            case Sample.CONST_069: result = "CONST_069"; break;
            case Sample.CONST_070: result = "CONST_070"; break;
            case Sample.CONST_071: result = "CONST_071"; break;
            case Sample.CONST_072: result = "CONST_072"; break;
            case Sample.CONST_073: result = "CONST_073"; break;
            case Sample.CONST_074: result = "CONST_074"; break;
            case Sample.CONST_075: result = "CONST_075"; break;
            case Sample.CONST_076: result = "CONST_076"; break;
            case Sample.CONST_077: result = "CONST_077"; break;
            case Sample.CONST_078: result = "CONST_078"; break;
            case Sample.CONST_079: result = "CONST_079"; break;
            case Sample.CONST_080: result = "CONST_080"; break;
            case Sample.CONST_081: result = "CONST_081"; break;
            case Sample.CONST_082: result = "CONST_082"; break;
            case Sample.CONST_083: result = "CONST_083"; break;
            case Sample.CONST_084: result = "CONST_084"; break;
            case Sample.CONST_085: result = "CONST_085"; break;
            case Sample.CONST_086: result = "CONST_086"; break;
            case Sample.CONST_087: result = "CONST_087"; break;
            case Sample.CONST_088: result = "CONST_088"; break;
            case Sample.CONST_089: result = "CONST_089"; break;
            case Sample.CONST_090: result = "CONST_090"; break;
            case Sample.CONST_091: result = "CONST_091"; break;
            case Sample.CONST_092: result = "CONST_092"; break;
            case Sample.CONST_093: result = "CONST_093"; break;
            case Sample.CONST_094: result = "CONST_094"; break;
            case Sample.CONST_095: result = "CONST_095"; break;
            case Sample.CONST_096: result = "CONST_096"; break;
            case Sample.CONST_097: result = "CONST_097"; break;
            case Sample.CONST_098: result = "CONST_098"; break;
            case Sample.CONST_099: result = "CONST_099"; break;
            case Sample.CONST_100: result = "CONST_100"; break;
            case Sample.CONST_101: result = "CONST_101"; break;
            case Sample.CONST_102: result = "CONST_102"; break;
            case Sample.CONST_103: result = "CONST_103"; break;
            case Sample.CONST_104: result = "CONST_104"; break;
            case Sample.CONST_105: result = "CONST_105"; break;
            case Sample.CONST_106: result = "CONST_106"; break;
            case Sample.CONST_107: result = "CONST_107"; break;
            case Sample.CONST_108: result = "CONST_108"; break;
            case Sample.CONST_109: result = "CONST_109"; break;
            case Sample.CONST_110: result = "CONST_110"; break;
            case Sample.CONST_111: result = "CONST_111"; break;
            case Sample.CONST_112: result = "CONST_112"; break;
            case Sample.CONST_113: result = "CONST_113"; break;
            case Sample.CONST_114: result = "CONST_114"; break;
            case Sample.CONST_115: result = "CONST_115"; break;
            case Sample.CONST_116: result = "CONST_116"; break;
            case Sample.CONST_117: result = "CONST_117"; break;
            case Sample.CONST_118: result = "CONST_118"; break;
            case Sample.CONST_119: result = "CONST_119"; break;
            case Sample.CONST_120: result = "CONST_120"; break;
            case Sample.CONST_121: result = "CONST_121"; break;
            case Sample.CONST_122: result = "CONST_122"; break;
            case Sample.CONST_123: result = "CONST_123"; break;
            case Sample.CONST_124: result = "CONST_124"; break;
            case Sample.CONST_125: result = "CONST_125"; break;
            case Sample.CONST_126: result = "CONST_126"; break;
            case Sample.CONST_127: result = "CONST_127"; break;
            case Sample.CONST_128: result = "CONST_128"; break;
            case Sample.CONST_129: result = "CONST_129"; break;
            case Sample.CONST_130: result = "CONST_130"; break;
            case Sample.CONST_131: result = "CONST_131"; break;
            case Sample.CONST_132: result = "CONST_132"; break;
            case Sample.CONST_133: result = "CONST_133"; break;
            case Sample.CONST_134: result = "CONST_134"; break;
            case Sample.CONST_135: result = "CONST_135"; break;
            case Sample.CONST_136: result = "CONST_136"; break;
            case Sample.CONST_137: result = "CONST_137"; break;
            case Sample.CONST_138: result = "CONST_138"; break;
            case Sample.CONST_139: result = "CONST_139"; break;
            case Sample.CONST_140: result = "CONST_140"; break;
            case Sample.CONST_141: result = "CONST_141"; break;
            case Sample.CONST_142: result = "CONST_142"; break;
            case Sample.CONST_143: result = "CONST_143"; break;
            case Sample.CONST_144: result = "CONST_144"; break;
            case Sample.CONST_145: result = "CONST_145"; break;
            case Sample.CONST_146: result = "CONST_146"; break;
            case Sample.CONST_147: result = "CONST_147"; break;
            case Sample.CONST_148: result = "CONST_148"; break;
            case Sample.CONST_149: result = "CONST_149"; break;
            case Sample.CONST_150: result = "CONST_150"; break;
            case Sample.CONST_151: result = "CONST_151"; break;
            case Sample.CONST_152: result = "CONST_152"; break;
            case Sample.CONST_153: result = "CONST_153"; break;
            case Sample.CONST_154: result = "CONST_154"; break;
            case Sample.CONST_155: result = "CONST_155"; break;
            case Sample.CONST_156: result = "CONST_156"; break;
            case Sample.CONST_157: result = "CONST_157"; break;
            case Sample.CONST_158: result = "CONST_158"; break;
            case Sample.CONST_159: result = "CONST_159"; break;
            case Sample.CONST_160: result = "CONST_160"; break;
            case Sample.CONST_161: result = "CONST_161"; break;
            case Sample.CONST_162: result = "CONST_162"; break;
            case Sample.CONST_163: result = "CONST_163"; break;
            case Sample.CONST_164: result = "CONST_164"; break;
            case Sample.CONST_165: result = "CONST_165"; break;
            case Sample.CONST_166: result = "CONST_166"; break;
            case Sample.CONST_167: result = "CONST_167"; break;
            case Sample.CONST_168: result = "CONST_168"; break;
            case Sample.CONST_169: result = "CONST_169"; break;
            case Sample.CONST_170: result = "CONST_170"; break;
            case Sample.CONST_171: result = "CONST_171"; break;
            case Sample.CONST_172: result = "CONST_172"; break;
            case Sample.CONST_173: result = "CONST_173"; break;
            case Sample.CONST_174: result = "CONST_174"; break;
            case Sample.CONST_175: result = "CONST_175"; break;
            case Sample.CONST_176: result = "CONST_176"; break;
            case Sample.CONST_177: result = "CONST_177"; break;
            case Sample.CONST_178: result = "CONST_178"; break;
            case Sample.CONST_179: result = "CONST_179"; break;
            case Sample.CONST_180: result = "CONST_180"; break;
            case Sample.CONST_181: result = "CONST_181"; break;
            case Sample.CONST_182: result = "CONST_182"; break;
            case Sample.CONST_183: result = "CONST_183"; break;
            case Sample.CONST_184: result = "CONST_184"; break;
            case Sample.CONST_185: result = "CONST_185"; break;
            case Sample.CONST_186: result = "CONST_186"; break;
            case Sample.CONST_187: result = "CONST_187"; break;
            case Sample.CONST_188: result = "CONST_188"; break;
            case Sample.CONST_189: result = "CONST_189"; break;
            case Sample.CONST_190: result = "CONST_190"; break;
            case Sample.CONST_191: result = "CONST_191"; break;
            case Sample.CONST_192: result = "CONST_192"; break;
            case Sample.CONST_193: result = "CONST_193"; break;
            case Sample.CONST_194: result = "CONST_194"; break;
            case Sample.CONST_195: result = "CONST_195"; break;
            case Sample.CONST_196: result = "CONST_196"; break;
            case Sample.CONST_197: result = "CONST_197"; break;
            case Sample.CONST_198: result = "CONST_198"; break;
            case Sample.CONST_199: result = "CONST_199"; break;
            case Sample.CONST_200: result = "CONST_200"; break;
            case Sample.CONST_201: result = "CONST_201"; break;
            case Sample.CONST_202: result = "CONST_202"; break;
            case Sample.CONST_203: result = "CONST_203"; break;
            case Sample.CONST_204: result = "CONST_204"; break;
            case Sample.CONST_205: result = "CONST_205"; break;
            case Sample.CONST_206: result = "CONST_206"; break;
            case Sample.CONST_207: result = "CONST_207"; break;
            case Sample.CONST_208: result = "CONST_208"; break;
            case Sample.CONST_209: result = "CONST_209"; break;
            case Sample.CONST_210: result = "CONST_210"; break;
            case Sample.CONST_211: result = "CONST_211"; break;
            case Sample.CONST_212: result = "CONST_212"; break;
            case Sample.CONST_213: result = "CONST_213"; break;
            case Sample.CONST_214: result = "CONST_214"; break;
            case Sample.CONST_215: result = "CONST_215"; break;
            case Sample.CONST_216: result = "CONST_216"; break;
            case Sample.CONST_217: result = "CONST_217"; break;
            case Sample.CONST_218: result = "CONST_218"; break;
            case Sample.CONST_219: result = "CONST_219"; break;
            case Sample.CONST_220: result = "CONST_220"; break;
            case Sample.CONST_221: result = "CONST_221"; break;
            case Sample.CONST_222: result = "CONST_222"; break;
            case Sample.CONST_223: result = "CONST_223"; break;
            case Sample.CONST_224: result = "CONST_224"; break;
            case Sample.CONST_225: result = "CONST_225"; break;
            default              : result = ""         ; break;
        }
        return result;
    }

    static class ConstantsTable
    {
        static Dictionary<int, string> constantsDictionary = new Dictionary<int, string>();

        static ConstantsTable()
        {
            var fields = typeof(Sample)
                            .GetFields(BindingFlags.Public | BindingFlags.Static);
            fields.Where(field => field.IsLiteral && field.FieldType.Equals(typeof(int)))
                    .ToList()
                    .ForEach(field => constantsDictionary.Add((int)field.GetValue(null), field.Name));
        }

        public static string GetConstantName(int value)
        {
            string name;
            return constantsDictionary.TryGetValue(value, out name) ? name : "";
        }
    }

    static string ReflectionAndDictionarySample(int value)
    {
        return ConstantsTable.GetConstantName(value);
    }

    static void Test(Func<int, string> sampleFunction, string sampleName)
    {
        const long times = 100000L;
        string     result;
        var        time  = PerformanceTester.Test(
                                () => Enumerable
                                        .Range(1, 225)
                                        .ToList()
                                        .ForEach(number => result = sampleFunction(number)),
                                times
                            );
        Console.WriteLine("{0}の場合\t: {1:F3}/{2}sec.", sampleName, time, times);
    }

    static void Main()
    {
        Test(IfSample                     , "if"                    );
        Test(SwitchSample                 , "switch"                );
        Test(ReflectionAndDictionarySample, "ReflectionとDictionary");
    }
}
実行結果

実行してみると、次のようになった。

ifの場合        : 2.575/100000sec.
switchの場合    : 0.864/100000sec.
ReflectionとDictionaryの場合    : 1.152/100000sec.

現状、比較が 200 以上も続く上記のようなケースでは、if と else で書いた方が遅いようだ。

2014年08月06日

[Event] Hokuriku.NET Vol.15 in FUKUI

Hokuriku.NET Vol.15 in FUKUI

アトラシアン エバンジェリスト長沢 智治さん、関西の Microsoft MVP 3名が来福! 参加者募集中!

『Hokuriku.NET Vol.15』
日時 2014年8月30日(土)
13時00分~17時30分
会場 福井市地域交流プラザ 研修室603 (AOSSA 6階) (福井県福井市手寄1-4-1)
参加費 500円 (イベントのみ), 5,500円 (イベント+懇親会)
内容/申込み http://hokurikunet.doorkeeper.jp/events/13746

2014年08月07日

[Event] こみゅぷらす Tech Aid 2014 (8/23)

こみゅぷらす Tech Aid 2014

こみゅぷらす夏恒例の飲み食いしながらの勉強会イベント!

マイクロソフト荒井省三さんと過ごす COM の1日、こみゅぷらすメンバーのセッション等。参加者受付開始!

『こみゅぷらす Tech Aid 2014』
日時 2014年8月23日(土)
11時00分~18時00分
会場 新宿の居酒屋にて (参加申し込みをいただいた方にのみご連絡いたします)
参加費 3,500円 (飲食代です)
内容/申込み http://comuplus.doorkeeper.jp/events/14019

2014年08月30日

[Event] こみゅぷらす Tech Aid 2014 を開催

こみゅぷらす Tech Aid 2014

※ 「[Event] こみゅぷらす Tech Aid 2014 (8/23)」の続き。

こみゅぷらす夏恒例の飲み食いしながらの勉強会イベントを開催。

概要

『こみゅぷらす Tech Aid 2014』
日時 2014年8月23日(土)
11時00分~18時00分
会場 新宿の居酒屋
主催 こみゅぷらす (COMU+)
Webサイト こみゅぷらす Tech Aid 2014 - こみゅぷらす (COMU+) | Doorkeeper

セッションの様子

マイクロソフト エバンジェリスト 荒井省三さんの COM のお話 マイクロソフト エバンジェリスト 荒井省三さんの COM のお話
マイクロソフト エバンジェリスト 荒井省三さんの COM (Component Object Model) のお話
岩永 信之 さんの「.NET vNext」 岩永 信之 さんの「.NET vNext」 岩永 信之 さんの「.NET vNext」
岩永 信之 さんの「.NET vNext」

関連サイト

2014年08月31日

[Event] Hokuriku.NET Vol.15 in FUKUI 開催

Hokuriku.NET Vol.15 in FUKUI

※ 「[Event] Hokuriku.NET Vol.15 in FUKUI」の続き。

Hokuriku.NET の15回目の勉強会がJR福井駅前で開催された。

アトラシアン エバンジェリストの長沢 智治 さんや関西から3名の皆さんがスピーカーとしていらした。

概要

『Hokuriku.NET Vol.15』
日時 2014年8月30日(土)
13時00分~17時30分
会場 福井市地域交流プラザ 研修室603 (AOSSA 6階) (福井県福井市手寄1-4-1)
主催 Hokuriku.NET
Webサイト Hokuriku.NET vol.15 - Hokuriku.NET | Doorkeeper

詳細

会場の様子 会場の様子
会場の様子
司会の鈴木 孝明 さん
司会の鈴木 孝明 さん
長沢 智治 さんの「これからの開発現場が持つべき最低限の開発フロー」
「これからの開発現場が持つべき最低限の開発フロー」 長沢 智治 さん アトラシアン株式会社 / エバンジェリスト
「僕とハッカソン」 森理 麟 さん (@moririring) Microsoft MVP for Visual C#
「Introducing Windows Runtime」 遥 佐保 さん Microsoft MVP for Windows Platform Development
「酒の肴はC# vNext」 鈴木 孝明 さん Microsoft MVP for Visual C#、小島 富治雄 さん Microsoft MVP for Visual C#
「JavascriptのPromiseのイケてないところ」 丸山和秀 さん
「Friendly の紹介」 鈴木 孝明 さん
懇親会
懇親会

関連サイト

2014年09月01日

[C#][Roslyn] C# 6.0 プレビューのスライド公開

C#

※ 「[Event] Hokuriku.NET Vol.15 in FUKUI 開催」の続き。

スライド

Hokuriku.NET vol.15』2014-08-30(土) の「酒の肴はC# vNext」で発表した資料を公開。

関連記事

2014年10月14日

[C#] 『ラムダ式でステップアップ! C#のプログラムから汎用的なアルゴリズムを切り出すことで、LINQについての理解を深めよう』 - CodeZine

algorithm.png

[C++] 『ラムダ式でステップアップ! C++のプログラムから汎用的なアルゴリズムを切り出し利用してみよう』 - CodeZine の続き。

C# によるプログラミングの記事を投稿。

「本稿では、C#のプログラムをリファクタリングして、汎用的なアルゴリズムを切り出し、利用する流れをステップバイステップで解説します。また、C# 3.0で採用されたラムダ式がそれを利用するのに、いかに便利なのか、どういう仕組みなのか、を紹介します。それにより、LINQ(Language INtegrated Query:統合言語クエリ)についての理解も深めていただけると思います。」

2014年10月26日

[C#][式木][LINQ] Hokuriku.NET C# 勉強会『C# 式木』(2014-10-26、金沢) のスライド公開

C#

Hokuriku.NET C# 勉強会『C# 式木』(2014-10-26、金沢) で発表した資料に大幅に加筆して公開。

2014年11月17日

MVP ComCamp -2nd Round- 開催中

MVP ComCamp -2nd Round- Nov 17 - Nov 21, 2014 が開催されている。

初日に開発者向けセッションを担当 (トラック 2 - Day 1 (2014/11/17): Learn フェイズ)。

発表した資料を公開。

関連記事

2014年12月12日

[C#][式木][LINQ] IQueryable な Twitter のタイムライン クラスと LINQ プロバイダー

C#

C# Advent Calendar 2014」の12日目の記事。

前の記事 ← → 次の記事

以前、「[C#][式木][LINQ] Hokuriku.NET C# 勉強会『C# 式木』(2014-10-26、金沢) のスライド公開」で、IQueryable な LINQ について解説した。

  1. LINQ to Objects 復習
  2. IQueryable<T>
  3. 式木 (Expression Tree)
  4. 式木メタ プログラミング
  5. LINQ プロバイダー

本記事では、その中の IQueryable なサンプルを補足する。

IQueryable な LINQ の中はどのようになっているのだろうか。

試しに少し実装してみることで、LINQ について理解を深めよう。

IEnumerable と IQueryable

[C#][ラムダ式][LINQ][式木] 匿名メソッドとラムダ式の違い」で紹介したように、匿名メソッドは delegate としてしか使えないが、ラムダ式は delegate としても式木としても使うことができる。

[C#][ラムダ式][式木] Expression として扱えるラムダ式と扱えないラムダ式」で紹介したように、ラムダ式であれば必ず式木として使うことができるわけではない。

※ クエリ構文は、「式木として扱えるラムダ式」の糖衣構文。つまり、式木を扱うことになる。

参考: LINQ でのクエリ構文とメソッド構文 (C#) - MSDN

LINQ の中には、次の二つの種類のライブラリがある。

  1. delegate を引数にしたもの
    • 例. IEnumerable<T>.Where(匿名メソッド)
  2. 式木を引数にしたもの
    • 例. IQueryable<T>.Where(ラムダ式)

LINQ to Objects などは前者で処理され、LINQ to SQLLINQ to Entities などは後者だ。

IQueryable なものを作ってみよう

今回は、IQueryable な Twitter のライムライン クラスを作ろうとしてみる。

先ずは IQueryable なクラス QueryableTweets。

※ IQueryable なだけでは OrderBy の対象となることができないので、ここでは IQueryable からの派生で OrderBy 可能な IOrderedQueryable を用いることにする。

// QueryableTweets.cs

using System.Linq;

// IOrderedQueryable<string> な QueryableTweets
// まだ IOrderedQueryable インタフェイスを実装してないのでコンパイル エラー
public class QueryableTweets<TElement> : IOrderedQueryable<TElement>
{}

ここから、Visual Studio でインタフェイスの実装を行うと次のようになる。

// QueryableTweets.cs

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

// IOrderedQueryable<string> な QueryableTweets
// Visual Studio でインタフェイスを実装した直後
public class QueryableTweets<TElement> : IOrderedQueryable<TElement>
{
    public Type ElementType
    {
        get { throw new NotImplementedException(); }
    }

    public Expression Expression
    {
        get { throw new NotImplementedException(); }
    }

    public IQueryProvider Provider
    {
        get { throw new NotImplementedException(); }
    }

    public IEnumerator<TElement> GetEnumerator()
    { throw new NotImplementedException(); }

    IEnumerator IEnumerable.GetEnumerator()
    { throw new NotImplementedException(); }
}

IQueryable は IEumerable から派生している。そのため IEumerable のメンバーである GetEnumerator() を実装する必要がある。

その他に、ElementType、Expression、Provider というプロパティを実装しなければならない。

 実装を進めていこう。このクラスの実装はそれほど大変ではない。

Provider プロパティのために IQueryProvider インタフェイスを持つクラスを用意する必要があるが、ここでは、それを仮に TwitterQueryProvider クラスとしておこう。 TwitterQueryProvider クラスは後述する。

// QueryableTweets.cs

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

// IOrderedQueryable<string> な QueryableTweets
public class QueryableTweets<TElement> : IOrderedQueryable<TElement>
{
    public Type ElementType
    {
        get { return typeof(TElement); }
    }

    public Expression     Expression { get; set; }
    public IQueryProvider Provider   { get; set; }

    public QueryableTweets()
    {
        Provider = new TwitterQueryProvider(); // IQueryProvider インタフェイスを実装したクラス。後述。
        Expression = Expression.Constant(this);
    }

    public IEnumerator<TElement> GetEnumerator()
    { return ((IEnumerable<TElement>)Provider.Execute(Expression)).GetEnumerator(); }

    IEnumerator IEnumerable.GetEnumerator()
    { return GetEnumerator(); }
}

IQueryProvider なもの (LINQ プロバイダー) を作ろうとしてみよう

続いて、上記 QueryableTweets で使うための、IQueryProvider なものの実装だ。

これは、LINQ プロバイダーと呼ばれるもので、式木としてのクエリーを解釈する。

こちらの実装は大変だ。

クラス名を TwitterQueryProvider として、IQueryProvider を実装していこう。

// TwitterQueryProvider.cs

using System.Linq;

// LINQ プロバイダーの実験用
// まだ IQueryProvider インタフェイスを実装してないのでコンパイル エラー
public class TwitterQueryProvider : IQueryProvider
{}

ここから、Visual Studio でインタフェイスの実装を行うと次のようになる。

// TwitterQueryProvider.cs

using System;
using System.Linq;
using System.Linq.Expressions;

// LINQ プロバイダーの実験用
// Visual Studio でインタフェイスを実装した直後
public class TwitterQueryProvider : IQueryProvider
{
    public IQueryable CreateQuery(Expression expression)
    { throw new NotImplementedException(); }

    public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
    { throw new NotImplementedException(); }

    public object Execute(Expression expression)
    { throw new NotImplementedException(); }

    public TResult Execute<TResult>(Expression expression)
    { throw new NotImplementedException(); }
}

少し実装を進めてみる。

// TwitterQueryProvider.cs

using System;
using System.Linq;
using System.Linq.Expressions;

// LINQ プロバイダーの実験用
public class TwitterQueryProvider : IQueryProvider
{
    IQueryable IQueryProvider.CreateQuery(Expression expression)
    { return null; }

    public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
    { return new QueryableTweets<TElement> { Provider = this, Expression = expression }; }

    public TResult Execute<TResult>(Expression expression)
    { return default(TResult); }

    public object Execute(Expression expression)
    {
        // ここで式木を解釈して、コレクションを作って返す
        return null; // とりあえずは仮に null を返すだけにしておく
    }
}

この中で、ポイントとなるのは Execute メソッドだ。

この Execute メソッドには、式木が渡ってくる。この式木を解釈してやって、そこからコレクションとしての結果を返してやれば良い。

ここは後で実装することにして、とりあえずは null を返すだけにしておく。

ExpressionVisitor の派生クラスで Visitor パターンによる式木の解釈

LINQ プロバイダーの Execute メソッドでの式木を解釈だが、それには、ExpressionVisitor というクラスが使える。

ExpressionVisitor から派生することで、Visitor パターンによる解析が可能となる。

参考: ExpressionVisitor クラス - MSDN

LINQ のための式木をきちんと解釈するのは、かなり大変なことだ。

ここでは、ごく一部の構文にだけ注目して、そこのみに対応することにする。

取り敢えずの最低限のサンプル コード、Where(text => text.Contains("C#")) の形にのみ対応してみる。

尚、この中では、Twitter のタイムラインを取得する TwitterTimeline クラスを使っているが、 TwitterTimeline クラスは後述する。

// TwitterExpressionVisitor.cs

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

// Visitor パターンで式木を解析して検索用文字列を取り出し、それを使って Twitter のタイムラインを取得する
public class TwitterExpressionVisitor : ExpressionVisitor
{
    public IEnumerable<string> Statuses { get; private set; }

    // 取り敢えずの最低限のサンプル コード
    // Where(text => text.Contains("C#")) の形にのみ対応してみる
    protected override Expression VisitMethodCall(MethodCallExpression expression)
    {
        // もし Where メソッドを呼ぶ式だったら
        if (expression.Method.Name == "Where") {
            // Where メソッドの第二引数であるラムダ式を取り出す
            var lambdaExpression = (LambdaExpression)((UnaryExpression)(expression.Arguments[1])).Operand;
            // そのラムダ式の Body 部を取り出す
            var bodyExpression = lambdaExpression.Body as MethodCallExpression;
            // もし Contains メソッドを呼ぶ式で
            if (bodyExpression != null && bodyExpression.Method.Name == "Contains") {
                // その引数が定数式だったら
                var constantExpression = bodyExpression.Arguments[0] as ConstantExpression;
                if (constantExpression != null) {
                    // その定数の値を検索文字列とし
                    var searchText = constantExpression.Value as string;
                    if (searchText != null)
                        // TwitterTimeline クラス (後述) を使って、タイムラインからその検索文字列にあたる Status を取得しておく
                        Statuses = new TwitterTimeline().Filter(searchText).Select(status => status.Text);
                }
            }
        }
        return base.VisitMethodCall(expression);
    }
}

TwitterTimeline クラスによる Twitter タイムラインの取得

次に、Twitter のタイムラインを取得するためのダミー クラス TwitterTimeline を用意する。

実際に Twitter のタイムラインを取得するコードを用意すれば良いわけだが、今回は説明の簡略化のために CoreTweet というライブラリとダミー コードを用いることにする。

CoreTweet は、Visual Studio から NuGet でインストールできる。

NuGet で CoreTweet のインストール
NuGet で CoreTweet のインストール

ダミー コードは次の通り。

// TwitterTimeline.cs

using CoreTweet; // Twitter のタイムライン取得用
using System.Collections;
using System.Collections.Generic;
using System.Linq;

// Twitter のタイムライン取得用
// CoreTweet ( https://github.com/CoreTweet/CoreTweet/wiki/Home(%E6%97%A5%E6%9C%AC%E8%AA%9E) ) を利用
// NuGet でインストールできる
// Twitterの開発者向けサイト "Twitter Developers" ( https://dev.twitter.com ) にアプリケーションの登録をし、
// Consumer Key、Consumer Secret、Access Token、Access Secret を取得するなどすれば、
// 実際に Twitter のタイムラインから取得することも可能
class TwitterTimeline : IEnumerable<Status>
{
    public IEnumerable<Status> Filter(string searchText)
    {
        // ダミー実装
        // 実際には、ここで searchText にマッチする Status のみを取ってくるのが良い
        return this.Where(status => status.Text.Contains(searchText));
    }

    public IEnumerator<Status> GetEnumerator()
    {
        // "Twitter Developers" に登録し、キーやトークンを取得すれば、実際に Twitter のタイムラインを取れる
        //var tokens = CoreTweet.Tokens.Create("[Your Consumer Key]", "[Your Consumer Secret]",
        //                                     "[Your Access Token]", "[Your Access Secret]");
        //return tokens.Statuses.HomeTimeline().GetEnumerator();

        // ダミー実装
        // 実際には Twitter のタイムラインを取ってくる
        yield return new Status { Text = "C# で CoreTweet を使って Twitter のタイムラインを取得してみた" };
        yield return new Status { Text = "式木いじりは茨の道" };
        yield return new Status { Text = "C# で LINQ を使う" };
        yield return new Status { Text = "Hokuriku,NET C# 式木" };
    }

    IEnumerator IEnumerable.GetEnumerator()
    { return GetEnumerator(); }
}

LINQ プロバイダー TwitterQueryProvider への組み込み

では、TwitterQueryProvider に TwitterExpressionVisitor を組み込んでみよう。

Execute メソッドの中で、TwitterExpressionVisitor の Visit を呼ぶ。 すると、TwitterExpressionVisitor が式木を解釈し、結果を Statuses に入れるので、それを返せば OK だ。

// TwitterQueryProvider.cs

using System;
using System.Linq;
using System.Linq.Expressions;

// LINQ プロバイダーの実験用
public class TwitterQueryProvider : IQueryProvider
{
    IQueryable IQueryProvider.CreateQuery(Expression expression)
    { return null; }

    public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
    { return new QueryableTweets<TElement> { Provider = this, Expression = expression }; }

    public TResult Execute<TResult>(Expression expression)
    { return default(TResult); }

    public object Execute(Expression expression)
    {
        // ここで式木を解釈して、コレクションを作って返す
        var expressionVisitor = new TwitterExpressionVisitor();
        expressionVisitor.Visit(expression);
        var statuses = expressionVisitor.Statuses;
        return statuses.AsQueryable<string>();
    }
}

テスト

使ってみよう。

コンソール アプリケーションの Main から使用してみる。

// Program.cs

using System;
using System.Linq;

class Program
{
    static void Main()
    {
        IQueryable<string> query1 = new QueryableTweets<string>();
        IQueryable<string> query2 = query1.Where(text => text.Contains("C#"));

        // ここまでは、式木をつくっているだけ
        Console.WriteLine(query2.Expression);
        Console.WriteLine();

        // 下の foreach 中で実際に値を item に取り出そうとすると、
        // 1. TwitterQueryProvider の Execute にその式木が渡され、
        // 2. TwitterExpressionVisitor でそれが解析される中で、
        // 3. TwitterTimeline がタイムラインの取得を行う

        foreach (var item in query2)
            Console.WriteLine(item);
    }
}

実行結果は、次の通りだ。

value(QueryableTweets`1[System.String]).Where(text => text.Contains("C#"))

C# で CoreTweet を使って Twitter のタイムラインを取得してみた
C# で LINQ を使う
Hokuriku,NET C# 式木

始めに式木が表示され、その後で、クエリーの結果 "C#" が含まれる Status が三行表示された。

このサンプルコードの範囲ではうまく動いたようだ。

■ 今回のまとめ

今回は、Hokuriku.NET C# 勉強会『C# 式木』 で説明した内容を補足した。

IQueryable なものを書き LINQ に対応させるのはかなり大変だが、その意味するところだけでも理解していけば、LINQ について理解を深めることができるように思う。

2014年12月15日

[C#] マイクロソフト キャンペーンの「C丼」を作って実食してみた

C丼キャンペーン

マイクロソフトが「C丼キャンペーン」というのを始めようとしているようだ。

手元に C丼のストラップがある。

C丼ストラップ
C丼ストラップ

プログラミング言語 C# (シーシャープ) の "#" の文字は実は、井桁 (number sign: 番号記号) であり、♯ (sharp、嬰記号) ではない、というのは有名だが、こちらは丼だ。

それって美味しいの?

ところで、IT 技術者というのは、「それって美味しいの?」という台詞をよく口にする。

エンジニアリングでは、理論的なことはともかく、「それは実際にモノづくりの役に立つのか?」を追求する。 エンジニアは、「それは美味しいのか (実践的に役立つのか)? 」を気にするのだ。

「C丼」? それって美味しいのか?

実際に作って食べてみることにした。

C丼を作って実食しよう

「C丼」部分は色から考えて、ニンジンで作るのが良いだろう。

包丁と細かい作業が出来る刃物を用意。

ニンジンを切って「C丼」部分を作る
ニンジンを切って「C丼」部分を作る

できた。曲線部分や丼の井のところが難しいかと思ったが、割と簡単だった。

細工が完成した「C丼」部分 (※ ニンジンの残りの部分は、スタッフが美味しくいただきました)
細工が完成した「C丼」部分

これを、出汁醤油で煮てみる。

煮られる「C丼」部分
煮られる「C丼」部分

煮物は冷めるときに味がしみ込む。

味がしみ込みつつある「C丼」部分
味がしみ込みつつある「C丼」部分

さて、ご飯を用意しよう。

件のストラップを再度確認しよう。

何やら解説が書いてある。

「XAML: ザーサイ&ナムル」? 「RICE: Rich Imagination Construction Engine」?

ちょっと何言ってるか分からないが、ともかく真似してみよう。

にご飯をよそう。

福井県産のコシヒカリだ。炊き立て。

丼によそわれたご飯
丼によそわれたご飯

そこに、ザーサイ&ナムルを半々にのせる。

ストラップではナムル部分がもっと緑だが、まあ良しとしよう。

ザーサイとナムルがのったご飯
ザーサイとナムルがのったご飯

その上に、「C丼」部分を飾れば完成だ!

完成した「C丼」
完成した「C丼」

さて食べよう。

「C丼」を食べよう
「C丼」の食べよう

これは!

そもそも「ダジャレ」ベースでデザインされた丼なので、全く期待していなかったが、うまいではないか。

ザーサイとナムルはご飯に合うし、一緒に食べてもうまい。

(ザーサイとナムルの味に思い切り依存するが)

「C丼」の実食
「C丼」の実食

完食!

「C丼」完食
「C丼」完食

結論

「C丼」は美味しい。お試しあれ。

2015年01月19日

[Event] MVP Community Camp 2015 (2015年1月31日) ~北陸は富山でやります~

MVP Community Camp

MVP Community Camp 2015

MVP Community Camp がアジア パシフィック地域で開催されています。

今年 (2015年) の 1 月 31 日は全国 8 箇所で、オフラインの勉強会が開催されます。

MVP Community Camp 2015 北陸会場

北陸は、今年は富山での開催です。

今回は、.NET と Java の合同勉強会となっており、 2つの部屋にて、それぞれのセッションが同時進行されます。

.NET 側が MVP Community Camp 2015 北陸会場となります。

同じ会場で、絶品寒ブリのしゃぶしゃぶなどを楽しめる懇親会も開催され、その後の宿泊も可能です。温泉もあります。

是非ご参加ください。

富山合同勉強会 .NET & Java - MVP Community Camp 2015 北陸会場 -
会期 2015年1月31日(土)
会場 富山県 呉羽ハイツ
参加費 無料
主催 Hokuriku.NET
北陸エンジニアグループ
協力 日本マイクロソフト
詳細/お申込み 富山合同勉強会 .NET & Java - MVP Community Camp 2015 北陸会場 | connpass
MVP Community Camp 2015 北陸会場 リーフレット

豪華講師陣などの詳しい内容が確認できる MVP Community Camp 2015 北陸会場の リーフレットがダウンロードできます (PDF): こちら

参考: 昨年までのイベント

2015年01月31日

[Event] 「富山合同勉強会 .NET & Java - MVP Community Camp 2015 北陸会場 -」が開催されました

※ 「MVP Community Camp 2015 (2015年1月31日) ~北陸は富山でやります~」の続き。

1月31日に、「富山合同勉強会 .NET & Java - MVP Community Camp 2015 北陸会場 -」が開催されました。

資料を公開します。

.NET MVP によるドキドキ・ライブコーディング! 小島の分 (アニメーションが有効な slideboom 版)

MVP Community Camp 2015 リンク

MVP Community Camp 2015 北陸会場について

富山県の旅館の 2 つの部屋で、それぞれ .NET と Java のセッションが行われました。

懇親会では、寒ブリのしゃぶしゃぶなどが楽しまれました。

富山合同勉強会 .NET & Java - MVP Community Camp 2015 北陸会場 -
会期 2015年1月31日(土)
会場 富山県 呉羽ハイツ
主催 Hokuriku.NET
北陸エンジニアグループ
協力 日本マイクロソフト

会場の様子

「富山ぶりしゃぶ勉強会へようこそ!」
「富山ぶりしゃぶ勉強会へようこそ!」
Hokuriku.NET のロゴ ステッカーと Microsoft MVP 飴、Microsoft 飴
Hokuriku.NET のロゴ ステッカーと Microsoft MVP 飴、Microsoft 飴
セッションの様子
セッションの様子
懇親会の様子
懇親会の様子

他の方の発表資料

関連記事

2015年05月01日

Microsoft Build 2015 関連記事へのリンク

BUILD

最大の開発者向けカンファレンスである Build が今年も開催されており (今年は 4.29-5.1 PDT サンフランシスコ)、様々な人に沢山の Web 記事等が書かれているのでまとめてみたい。

※ 随時更新。

全体

Windows 10

Microsoft Edge

Office

HoloLens

Continuum

Azure/データベース

iOS/AndroidアプリのWindows 10への移植

Visual Studio Code

Visual Studio

その他

2015年06月12日

Microsoft Virtual Academy の紹介

microsoft_ITHero_banners_02-3.jpg

Microsoft Virtual Academy (MVA) についてご紹介します。

Microsoft Virtual Academy (MVA) は Microsoft が提供するオンライン トレーニングです。

特長

  • 全コース無償
  • 開発者向けや IT プロ向けの様々な学習コースが充実
  • Visual Studio、SQL Server、Windows、Office など、マイクロソフト技術について幅広く学べる
  • de:code 2015 のキーノートなど、マイクロソフトのカンファレンスやイベントのセッションを視聴できる
  • 映像やパワーポイントの資料による分かりやすい説明
  • 映像やパワーポイントの資料はダウンロード可能
  • 日本語の映像の視聴に加えて、英語の映像も (日本語字幕付きで) 視聴できる
  • いつでも好きな時に受講できる
  • ダッシュボードで、学習の計画を建てたり、進み具合をチェックできる
  • 学習が進むにつれてポイントがたまっていき、ランキングを競うことができる

使い方

Microsoft Virtual Academy (MVA) は 5 回までは会員登録なしで、すぐに授業動画の視聴と資料のダウンロードが可能です。
さっそく授業を閲覧して、使い方を確認してみましょう。

  1. コースのURLをクリックします。
    例. Visual Studio 2015 における統合開発環境の進化
  2. 動画を再生してみましょう。
  3. 資料をダウンロードしてみましょう。
  4. Microsoft Virtual Academy (MVA) に登録。
    Microsoft Virtual Academy (MVA) の会員登録は無料です。 登録すると Microsoft Virtual Academy (MVA) に掲載されているすべての授業動画や資料を閲覧できます。 さらに学習に便利な学習履歴の閲覧や、受講証の発行、学習ランキングを使用することができます。
    マイクロソフトアカウントがある方はマイクロソフトアカウントでサインイン、ない方は「Sign up now」 からアカウントを作成してください。

開発者向け3コース

それでは、多くの学習コースの中から、開発者向けの3つをご紹介します。

各コース名をクリックすることで、それぞれを受講することができます。

コース名 説明 コース内容
Visual Studio 2015 における統合開発環境の進化 リリース間近な Visual Studio 2015 の最新の開発環境の説明。
  1. 開発ツールの強化 ~ Visual Studio 2015
  2. 開発基盤の強化 ~ Team Foundation Server 2015
Unity を使用した Windows 用の 2D および 3D ゲーム開発ジャンプ スタート C# と Unity を使用した Windows 用の Unity ゲームを開発する方法。ビデオは英語だが、日本語字幕付き。
  1. 概要とアーキテクチャ
  2. 2D ゲーム開発
  3. 2D および 3D アセットの作成
  4. 3D ゲーム開発
  5. Windows プラットフォームの構築
  6. ゲームの最適化
  7. Unity 内のアプリケーション ライフサイクル管理 (ALM)
  8. マーケティングと収益化
  9. Prime[31] および Azure モバイル サービス
  10. 最後のしあげの追加
  11. 公開形式の Q&A ログ
Microsoft Azure の基礎 Microsoft Azure の最初のコース。
  1. シリーズの概要
  2. Microsoft Azure をお勧めする理由
  3. Microsoft Azure サービスの範囲
  4. Azure へのアクセスに使用するツールと API
  5. Azure ポータルとプレビュー ポータルの操作
  6. 組織アカウントとサブスクリプションの作成
  7. サブスクリプションとディレクトリの理解
  8. How Do I: ディレクトリの管理
  9. How Do I: マネージャ ユーザー、サブスクリプションの役割、およびディレクトリの役割
  10. How Do I: サブスクリプションとサービス管理者の管理
  11. ジオ (主要地域)、地域、およびデータセンターの理解
  12. 参考情報

2015年06月24日

『C#実践開発手法』レビュー

『C#実践開発手法』

『C#実践開発手法』

C#実践開発手法』という本を監訳された長沢 智治さんにいただいた。 レビューしたい。

タイトル C#実践開発手法
サブタイトル等 デザインパターンとSOLID原則によるアジャイルなコーディング (マイクロソフト公式解説書)
著者・監訳・翻訳 Gary McLean Hall (著), 長沢 智治 (監訳), クイープ (翻訳)
出版社 日経BP社
発売日 2015年6月4日
本体価格 ¥5,000+税
頁数 440頁
本の種類 単行本Kindle

原書 (英語) は次の通り。

タイトル Adaptive Code via C#
サブタイトル Agile coding with design patterns and SOLID principles
著者 Gary McLean Hall
出版社 Microsoft Press
発売日 2014年10月19日
頁数 448頁
本の種類 ペーパーバックKindle
内容

次のような内容となっている。

  • 開発手法であるスクラム
  • モジュールの依存関係の原則
  • インタフェイスを使った Adaptive な (適応力のある) 設計方法
  • ユニットテスト (単体テスト)リファクタリング (内部構造の改善)
  • SOLID の原則 (単一責務の原則、開放/閉鎖の原則、リスコフの置換原則、インタフェイス分離の原則、依存性反転の原則)
  • 開発のサンプル
  • Git などのツールの紹介

何故プログラマーには本書が重要か

ソフトウェアは変化し続ける

ソフトウェア開発は、常に追加・変更し続けるのが特徴だ。 一気に全体が出来上がる訳ではない。 最初の1行を書いてから、段々と機能が足され、また、改良されていく。 変更し続けるのが特徴なので、なるべく変化を受け入れやすい作り方をする方が良い。

本書には、そういう「Adaptive Code (適応力のあるコード)」を書くための原則とそれを実践するための方法としてベストプラクティスが書かれている。

原則は重要

ソフトウェア開発において、原則は重要だ。

C# 等のプログラミング言語の文法を覚えればそれだけでソフトウェアが書けるかというと、そうではない。 また、いつもいつも日進月歩の開発技術を一から習得しなおさないといけないかというと、それも違う。 経験があり優秀な技術者は、新しいプログラミング言語、新しい開発基盤でも実力を発揮するものだ。

そこには、個々の開発技術にそれほど依存せず、新しい技術が出てきたときにも使える知恵があるだろう。 つまり、プログラミング言語や技術のトレンド等の枠を超えて重要な、開発手法や原則、パターン、アンチパターン、ベストプラクティスというものがあるのだ。

それは、偉大な先人の知恵が集まったものだ。経験のあるプログラマーが長年掛かって身に付ける暗黙知のような技術を、先人が形式知として習得しやすくしてくれたのだ。 そうした知恵は広くソフトウェア開発に応用が効き、また、ドッグイヤーと言われるソフトウェア開発の技術革新の中でも古びない。 また、プログラマー同士がコミュニケーションするときの語彙としてもずっと重要なものだ。 プログラマーとしては是非とも知っておきたい。

「原則だのパターンだのは、実務では役に立たない」というのを聞いたことがある。 それは違うと思う。 パターンをどこにでも適用しさえすれば良い、ということではない。 サンプルコードそのものをコピー & ペーストして使え、という話でも断じてない。 原則やパターン等は、「守破離」でいう「守」だ。基礎なのだ。

確かに、単にそれらの開発手法や原則、パターン、アンチパターン、ベストプラクティスを並べただけでは、中々理解が難しい場合もある。 実務への応用が易しくない場合もあるだろう。抽象的な知識は、広く使えて古びないが、具体的な知識と違って、深く理解しないと現場で活かしづらい。

本書は、単なる原則論にとどまっておらず、豊富な C# のサンプルコードや Visual Studio 等のツールの使い方の例によって、具体的に実践方法をあげている。 ここが、とても重要なところだと思う。

プログラマーの必読書

過去にこうした知恵が書かれたプログラマーの必読書としては、次のようなものがあった:

本書『C#実践開発手法』は、C#プログラマーにとって、そうした本に並ぶものだと思う。 より新しい内容も含まれているし、何より Visual Studio と C# による実装レベルにまで具体的に噛み砕いている。 中堅のC#プログラマーに必読だろう。

『日経ソフトウェア 2015年8月号』 【特集1】「最新Visual Studioで、Windowsアプリを作ろう」のPart 1-3を執筆

日経ソフトウェア 2015年8月号

『日経ソフトウェア 2015年8月号』で、【特集1】「最新Visual Studioで、Windowsアプリを作ろう」のPart 1-3を執筆しました。

『日経ソフトウェア 2015年8月号』
発売日 2015年6月24日
出版社 日経BP社
Amazon.co.jp 雑誌
Kindle版

Visual Studio の最新版 Visual Studio 2015 のこの夏の登場を前に、無料で使える Visual Studio Community 2015 RC を使った Windows アプリケーションの作り方の記事を書きました。

Visual Studio を使ったことがない人でも分かるように書きましたが、Visual Studio 2015 の新しい機能も紹介していますので、既に Visual Studio をお使いの方もぜひどうぞ。

日経ソフトウエアのサイト冒頭を読むことができます。

次のような内容です。

  • Part 1. Visual Studio を始めよう
    Visual Studio 2015 のインストール方法と、何ができて、どう便利か、新機能とともに紹介しています。
  • Part 2. キッチンタイマーを作ってみよう
    Visual Basic + Windows フォームで、デジタル時計 → ストップウォッチ → キッチンタイマーと段階的に機能を追加していくチュートリアルです。
  •  Part 3. アナログ時計アプリを作ってみよう
    C# + WPF で、デジタル時計 → アナログ時計と段階的に機能を追加していくチュートリアルです。
    WPF の標準的な作り方として、Model-View-ViewModel に分けた設計方法をご紹介しています。

尚、Part 4 は日本マイクロソフトによる「Visual Studio 2015 の新機能」です。

関連ツイート

2015年07月02日

Microsoft MVP for .NET を受賞しました

MVP_Logo_Horizontal_Preferred_Cyan300_CMYK_72ppi.png

Microsoft MVP Award を再受賞しました。11年目になります。

1月から、Visual C#, Visual Basic, Visual F# が .NET に統合されたため、今回は .NET という技術専門分野での受賞になりました。

いくつかの IT 系の大企業が、このような表彰制度を用意して、技術者を (かなり本気で) 褒めてくださいます。 大人になると、中々褒められる機会がないので、こうして褒められるとかなり嬉しいものです。 ありがたいことです。

子供はもちろんのこと、大人であっても褒める、ということの大切さを改めて感じます。

そして、お世話になっている皆様に感謝です。ありがとうございます。

※ もし、執筆や技術系セッションなどご要望があれば、よろしくお願いします (こちら等へ)。

2015年12月10日

Visual Studio Code の拡張機能「Twitter Client」を試してみる

Visual Studio Code の拡張機能「Twitter Client」を試してみる

Visual Studio / Visual Studio Code Advent Calendar の12月10日の記事。

Visual Studio Code の拡張機能

Microsoft の開発者向けテキスト エディターである Visual Studio Code には、インテリセンスやデバッグ機能などの Visual Studio の素晴らしい機能があり、Windows 以外にも Linux や OS X で使うことができる。オープンソースだ。

豊富な拡張機能も魅力だ。これにより、様々な機能を使うことができる。また、C# や JavaScript のみならず、Go、D Language、Haskell 等の沢山のプログラミング言語にも対応できる。

どんな拡張機能があるかは、次のサイトで見ることができる。

Twitter Client 拡張機能

この記事では、Visual Studio Code の拡張機能の一つ、"Twitter Client" を紹介したい。

この拡張機能を使うことによって、テキスト エディターから出ることなく、Twitter のタイムラインを見たり、呟いたりすることができる。

実際にインストールして使ってみよう。

以下で、その手順を示したい。

1. Visual Studio Code のインストール

Visual Studio Code (現在 Version 0.10.3) は、次のサイトからダウンロードしてインストールすることができる。

インストールが完了したら起動してみよう。

Visual Studio Code の起動画面 (Windows の場合)
Visual Studio Code の起動画面 (Windows の場合)
2. Twitter Client 拡張機能のインストール

次に、Visual Studio Code に Twitter Client 拡張機能をインストールする。

詳しい内容は、次のページで見ることができる (英語)。

Visual Studio Code で F1キーを押すと、上にコマンドを入力するテキスト ボックスが現れる。

ここに、"ext install" と入力し、Enter キーを押す。

拡張機能のインストール
拡張機能のインストール

暫く待つと、拡張機能がリストアップされてくる (インターネット接続が必要)。

拡張機能のリストアップ
拡張機能のリストアップ

ここで、"ext install " の後ろに "twitter" と入力する。"Twitter Client" が表示されるので、右側の雲のアイコンをクリックしてインストールする。

"Twitter Client" のインストール
"Twitter Client" のインストール

インストールが終わると、"Restart Now" というボタンが表示される。クリックして Visual Studio Code を再起動しよう。

"Twitter Client" のインストール完了
"Twitter Client" のインストール完了
3. Twitter Developer Account による Twitter App の作成

Twitter のクライアント アプリを作るためには、Twitter Developer Account によって Twitter App の作成を行う必要がある。

その手順は次のページで見ることができる (英語)。

"Twitter Client" には、このステップの為のウィザードが用意されている。見てみよう。

再び F1 キーを押して、コマンドとして "Twi Wizard" と入力する。

"Twitter Client" のウィザードの起動
"Twitter Client" のウィザードの起動

すると、"Twitter Client" のウィザードが起動し、Twitter App をセットアップする手順を教えてくれる。 "Continue" をクリックしよう。

"Twitter Client" のウィザードの開始画面
"Twitter Client" のウィザードの開始画面

すると、"https://apps.twitter.com" で Twitter App を作成するか訊いてくる。 "Continue" をクリックする。

"Twitter Client" のウィザード
"Twitter Client" のウィザード

Web ブラウザーで、"https://apps.twitter.com" が開かれる。 ここで Twitter App の作成を行う。Twitter にログインしよう (Twitter アカウントが必要)。

"Create New App" をクリックする。

Twitter App の作成
Twitter App の作成

Visual Studio Code 側では、ウィザードを進めていこう。

"Twitter Client" のウィザード
"Twitter Client" のウィザード
"Twitter Client" のウィザード
"Twitter Client" のウィザード

すると、Web ブラウザー側では、Twitter App を作成するための必要事項の入力を求められる。 入力しよう。

Twitter App を作成るための必要事項の入力
Twitter App を作成るための必要事項の入力

Twitter App が作成されたら、"Access level" が "Read & write" になっているのを確認しておこう (もし違っていたら、"modify app permissions" から変更しておく)。

Twitter App が作成されたら "Access level" の確認
Twitter App が作成されたら "Access level" の確認

次に、"Keys and Access Tokens" のタブをクリックして切り替える。

ここに表示されている "Consumer Key (API Key)" と "Consumer Secret (API Secret)" は、後で使うのでメモしておく。

そして、下部の "Create my access token" をクリックしよう。

"Keys and Access Tokens"
"Keys and Access Tokens"

"Access token" が表示される。 "Access Token" と "Access Token Secret" をメモっておこう。

"Access token"
"Access token"
4. Twitter Developer Account による Twitter App の作成

メモっておいたキーやトークンは、Visual Studio Code で入力する。

Visual Studio Code で、メニューから "File" - "Preferences" - "User Settings" を選ぶ。

Visual Studio Code で "User Settings" を開く
Visual Studio Code で "User Settings" を開く

"settings.json" というファイルが開く。 右側がユーザー用の設定ファイルだ。 次のように入力する。

"twitter.consumerkey": "xxxx", // Consumer Key (API Key)

"twitter.consumersecret": "xxxx", // Consumer Secret (API Secret)

"twitter.accesstokenkey": "xxxx", // Access Token

"twitter.accesstokensecret": "xxxx" // Access Token Secret
User Settings の編集
User Settings の編集
5. "Twitter Client" を使ってみよう

以上で準備は完了だ。 使ってみよう。

Visual Studio Code の下部に "Twitter" と書かれたボタンが表示されている。

クリックしてみよう。

すると、上の方に Twitter 用のコマンドが表示される。

Twitter のコマンド
Twitter のコマンド

例えば、"Home" をクリックしてみると、自分のタイムラインが表示される筈だ。

Home Timeline
Home Timeline

"User" を選んで自分の呟きだけに切り替え、"Post" を使って呟いてみると次のような感じだ。

自分の呟きを表示して呟いてみる
自分の呟きを表示して呟いてみる

まとめ

今回は、Visual Studio Code の拡張機能の一つ Twitter Client を試した。

Visual Studio Code には、他にも豊富な拡張機能がある。是非試してほしい。

2015年12月19日

[C#] 浮動小数点数型 double と誤差 ~double の内部表現~

IEEE 754 倍精度 浮動小数点形式

C# Advent Calendar 2015 の12月19日の記事。

C 等を使用している場合と異なり、C# では、それがメモリ上でどのような姿をしているのかを意識する機会は少なめだ。

C の場合、型とメモリ上のビットの並びを意識したプログラミングをする機会が多い。ビット演算も比較的良く使われる。 それに比べると、C# や Java などの言語だけを使っている場合は、そうした機会は割と少ない。

しかし、C# 等であっても、やはりそれぞれの型がメモリ上でどのようにして値を保持しているかを知っていることが有効な場合がある。 例えば、double の演算では誤差が生じることが多いが、double の内部表現を知ることで、その理由も腑に落ちやすいだろう。

本記事では、敢えて C# の double (System.Double) の内部の表現に焦点を当ててみたい。 後ろの方では、double の内部を見るクラスなども紹介する。

※ C# 6.0 を使用。

double を足したり引いたりすると誤差が生じる

C# で double や float のような浮動小数点数型を扱う場合には、誤差に注意する必要がある。

次の例では、↑ボタンを押す度に数値に 0.1 を足し、↓ボタンを押す度に数値から 0.1 を引いているのだが、何度も↑ボタンや↓ボタンを押した結果、誤差が生じている。

0.1を足したり引いたりすることで誤差が生じるサンプル (WPF)
0.1を足したり引いたりすることで誤差が生じるサンプル (WPF)

ちなみに、ソース コードは次のようなものだ。

UpDown.xaml.cs
using System.Windows;
using System.Windows.Controls;

namespace 浮動小数点数サンプル.WPF
{
    public partial class UpDown : UserControl
    {
        const double d = 0.1;

        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(double), typeof(UpDown), new PropertyMetadata(0.0));

        public double Value {
            get { return (double)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        public UpDown()
        { InitializeComponent(); }

        void OnUpButtonClick  (object sender, RoutedEventArgs e) => Value += d;
        void OnDownButtonClick(object sender, RoutedEventArgs e) => Value -= d;
    }
}
UpDown.xaml
<UserControl x:Class="浮動小数点数サンプル.WPF.UpDown"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:浮動小数点数サンプル.WPF"
             mc:Ignorable="d" 
             d:DesignHeight="80" d:DesignWidth="200">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
        <RepeatButton Content="↑" Click="OnUpButtonClick"/>
        <RepeatButton Grid.Row="1" Content="↓" Click="OnDownButtonClick"/>
        <TextBlock Grid.Column="1" Grid.RowSpan="2" Text="{Binding Value, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:UpDown}}}" HorizontalAlignment="Right" VerticalAlignment="Center" TextWrapping="Wrap"/>
    </Grid>
</UserControl>

問題点を絞るために、もっとシンプルなサンプルで見てみよう。

次の例では、コンソール アプリケーションで、0.1 を 20 回足した後に20回引いてみて、値の変化を表示している。

0.1 を 20 回足した後に20回引くコンソール アプリケーション
using System;

public static class Extension
{
    // 回数繰り返す
    public static void Repeat(this int times, Action action)
    {
        for (var counter = 0; counter < times; counter++)
            action();
    }
}

static class Program
{
    static void WriteLine(double value) => Console.WriteLine($"{value:f16}");

    static void Main()
    {
        // 0.0 から 0.1 ずつ20回増やしていき、0.1 ずつ20回減らしていく
        const int    times = 20;
        const double d     =  0.1;
        double       value =  0.0;
        WriteLine(value);
        times.Repeat(() => WriteLine(value += d));
        times.Repeat(() => WriteLine(value -= d));
    }
}

実行結果は次の通りだ。

最後の方で誤差が生じているのが分かる。

0.1 を 20 回足した後に20回引くコンソール アプリケーションの実行結果

0.0000000000000000
0.1000000000000000
0.2000000000000000
0.3000000000000000
0.4000000000000000
0.5000000000000000
0.6000000000000000
0.7000000000000000
0.8000000000000000
0.9000000000000000
1.0000000000000000
1.1000000000000000
1.2000000000000000
1.3000000000000000
1.4000000000000000
1.5000000000000000
1.6000000000000000
1.7000000000000000
1.8000000000000000
1.9000000000000000
2.0000000000000000
1.9000000000000000
1.8000000000000000
1.7000000000000000
1.6000000000000000
1.5000000000000000
1.4000000000000000
1.3000000000000000
1.2000000000000000
1.1000000000000000
1.0000000000000000
0.9000000000000000
0.8000000000000000
0.7000000000000000
0.6000000000000000
0.5000000000000000
0.4000000000000000
0.3000000000000000
0.2000000000000000
0.0999999999999998
-0.0000000000000002

どうしてこのような誤差が生じるのだろうか?

情報落ち

「情報落ち」と呼ばれる現象がある。

絶対値が大きな値と小さな値を足したり引いたりした場合、小さい方の数値の末尾の分の情報が失われる、というものだ。

例えば、十進数で 1111111111.11111 と 1.11111111112345 を足す場合を考えてみよう。 double の有効桁は十進数で15桁程度なので、そうした場合を想定してみる。

有効桁が15桁の場合、1111111111.11111 + 1.11111111112345 = 1111111112.22222 となり、足し算の結果が小さい方の数値の末尾の分小さくなっている。 このような演算を繰り返すと、どんどんこの分の誤差が積もっていくことになる。

今回の場合だが、果たして 0.1 を足したり引いたりするだけで、このような誤差が生じるのだろうか。 0.1 の足し算や引き算であれば、有効桁が15桁もあれば誤差は生じないのではなかろうか。

その鍵は、double の内部表現にある。

二進数としての小数

double の内部表現は二進数である。

そこで、二進数としての小数を考えてみよう。

例えば、十進数で 2.5 の場合、これを二進数で表現すると次のようになる。

2.5(10) = 2 + 0.5 = 10(2) + 1×2-1 = 10.1(2)

そして、十進数で 0.1 の場合、二進数で表現すると、次のように循環小数となるのだ。

0.1(10) = 0.0 0011 0011 0011…(2)

double 内部の二進数としての有効桁は、52 + 1桁である。十進数としての 0.1 を入れたつもりでも、実際には循環小数の全ての桁を格納できる訳ではないので、有効桁を超えた分の情報はなくなってしまい、僅かに 0.1 と異なる値が格納されることになる。

また、この場合、有効桁いっぱいまで使って 0.1 に近い値を表現しているので、絶対値がより大きい値との演算で情報落ちが起こることがある。

double の内部表現

では、double の内部表現をもっと詳しくみてみよう。

double は、次のような構造をしている。これは、「IEEE 754 倍精度 浮動小数点形式」と呼ばれる形式だ。

double の内部表現 (IEEE 754 倍精度 浮動小数点形式)
double の内部表現 (IEEE 754 倍精度 浮動小数点形式)

64ビットの中に二進数で、 符号部、指数部、仮数部の三つが格納されており、次のような値を表す。

-1符号部 × 1.仮数部 × 2指数部 ‐ 1023

部分 ビット数 意味 補足
符号部 1 ビット 負の数かどうかを表す 1: 負の数、0: 負の数ではない
指数部 11 ビット 指数 1023 足される
仮数部 52 ビット 仮数の絶対値 非 0 の場合 1.XXXXX… になるように指数部で調整され、最上位の 1 は省かれる

例えば、2.5(10) の場合だと次のようになる:

2.5(10)
= 10.1(2)
= 10.1(2)×20
= 1.01(2)×21 (1.XXXXX… になるように調整)

そして、それぞれの部分は次のようになる。

部分 補足
符号部 0 負の数でない
指数部 10000000000 1(10)+1023(10)=1024(10)=10000000000(2)
仮数部 0100000000000000000000000000000000000000000000000000 1.0100000000000000000000000000000000000000000000000000 の先頭の 1. を省いたもの

従って、2.5(10) を double に格納したときの全体の内部表現は、|0|10000000000|0100000000000000000000000000000000000000000000000000| となる。

そして、0.1(10) の場合だと次のようになる:

0.1(10)
= 0.0 0011 0011 0011 …(2) (循環小数となる)
= 1.1 0011 0011 …(2) × 2-4 (1.XXXXX… になるように調整)

それぞれの部分は次のようになる。

部分 補足
符号部 0 負の数でない
指数部 ‭01111111011 -4(10)+1023(10)=1019(10)=‭01111111011(2)
仮数部 1001100110011001100110011001100110011001100110011010 1.1 0011 0011 …(2) の整数部分の1を省略し、最後の桁の次の桁を「0捨1入」

つまり、0.1(10) を double に格納したときの全体の内部表現は、|0|‭01111111011|1001100110011001100110011001100110011001100110011010| となる。

尚、同じく浮動小数点数型である float に関しても同様だ。こちらは、4 バイト = 32 ビット (符号部 1 ビット、指数部 8 ビット、仮数部 23 ビット) となる。

C# で double の内部の値を見てみよう

最後に、C# で double の内部の値を見るクラスを作ってみよう。

先ず、double をバイト列にしたり、バイト列を十六進数表現に変換したり、十六進数表現を二進数表現に変換したりするメソッド群を用意した。

バイト列に変換するのには、System.BitConverter クラスが使用できる。 また、メモリ上での配置がリトル エンディアンになっている場合は、反転する必要があるが、これも System.BitConverter クラスで判定できる。

十六進数や二進数としての表現は、文字列として得られるようにした。

BinaryUtility.cs
using System;
using System.Linq;
using System.Text;

public static class BinaryUtility
{
    // バイト列をビッグ エンディアンに変換 (メモリ上での配置がリトル エンディアンになっている場合は反転)
    public static byte[] ToBigEndian(this byte[] bytes) =>
        BitConverter.IsLittleEndian ? bytes.Reverse().ToArray() : bytes;

    // double をバイト列に変換 (ビッグ エンディアン)
    public static byte[] ToBytes(this double value) =>
        BitConverter.GetBytes(value).ToBigEndian();

    // バイト列を十六進数に変換
    public static string ToHexadecimalNumber(this byte[] byteArray) =>
        BitConverter.ToString(byteArray).Replace("-", "");

    // 十六進数一桁を二進数に変換
    public static string HexadecimalNumberToBinaryNumber(char hexadecimalNumber) =>
        Convert.ToString(Convert.ToInt32(hexadecimalNumber.ToString(), 16), 2).PadLeft(4, '0');

    // 十六進数を二進数に変換
    public static string HexadecimalNumberToBinaryNumber(string hexadecimalString) =>
        hexadecimalString.Select(character => HexadecimalNumberToBinaryNumber(character))
                         .Aggregate(new StringBuilder(), (stringBuilder, text) => stringBuilder.Append(text))
                         .ToString();

    // 二進数一桁を int に変換
    public static int BinaryNumberToInteger(char binaryNumber) =>
        binaryNumber == '0' ? 0 : 1;
}

次に、これを利用して、double 即ち IEEE 754 倍精度 浮動小数点形式から各部分の値を取り出すクラスを作ってみよう。

IEEE754Double.cs
using System;

// IEEE 754 倍精度 浮動小数点形式 (8バイト)
// 符号部 1bit   マイナス符号があるとき1
// 指数部 11bits 指数 + 2^10−1 (2^10−1 = 1024 - 1 = 1023)
// 仮数部 52bits 整数部分の1を省略
// ※ 仮数部は整数部分が1のみになるように調整
public class IEEE754Double
{
    const int       exponentBias = 1024 - 1; // 指数のバイアス: 2^10−1 = 1024 - 1 = 1023
    readonly double value;                   // double としての値
    readonly string hexadecimalNumber;       // 十六進数
    readonly string binaryNumber;            // ニ進数

    public IEEE754Double(double value)
    {
        this.value        = value;
        hexadecimalNumber = value.ToBytes().ToHexadecimalNumber();
        binaryNumber      = BinaryUtility.HexadecimalNumberToBinaryNumber(HexadecimalNumber);
    }

    // double としての値
    public double Value => value;

    // 十六進数
    public string HexadecimalNumber => hexadecimalNumber;

    // ニ進数
    public string BinaryNumber => binaryNumber;

    // 符号部の二進数 (マイナス符号があるかないか: 符号部 1bit)
    public char SignBinaryNumber => binaryNumber[0];

    // 指数部の二進数 (指数部 11bits)
    public string ExponentBinaryNumber => binaryNumber.Substring(1, 11);

    // 仮数部の二進数 (仮数部 52bits : 整数部分の1を省略した小数部分)
    public string DigitsBinaryNumber => binaryNumber.Substring(1 + 11, 64 - 1 - 11);

    // 符号 (マイナス符号があるかないか: 符号部 1bit から取得)
    public bool Sign => SignBinaryNumber == '1';

    // 指数 (「指数部 11bits: 指数 + 指数のバイアス」から取得)
    public int Exponent => Convert.ToInt32(ExponentBinaryNumber, 2) - exponentBias;

    // 仮数
    public double Digits
    {
        get {
            var wholeDigitsBinaryNumber = "1" + DigitsBinaryNumber; // 二進数
            // 二進数を double に変換
            var result = 0.0;
            for (var index = wholeDigitsBinaryNumber.Length - 1; index >= 0; index--)
                result = result / 2.0 + BinaryUtility.BinaryNumberToInteger(wholeDigitsBinaryNumber[index]);
            return result;
        }
    }

    public override string ToString() => $"{SignString}{Digits} * 2^{Exponent}";

    // 符号文字列 (- または +)
    string SignString => Sign ? "-" : "+";
}

これらを使って、2.5(10) と 0.1(10) の内部の値をみてみよう。

Program.cs
using System;

static class Program
{
    static void WriteLine(double value)
    {
        var i3eDouble = new IEEE754Double(value);
        Console.WriteLine($"{i3eDouble.Value:f16}, {i3eDouble.HexadecimalNumber}, {i3eDouble.BinaryNumber}, {i3eDouble.ToString()}");
    }

    static void Main()
    {
        WriteLine(2.5);
        WriteLine(0.1);
    }
}

実行結果は次のようになる。 先に考察したのと同じ結果だ。


2.5000000000000000, 4004000000000000, 0100000000000100000000000000000000000000000000000000000000000000, +1.25 * 2^1
0.1000000000000000, 3FB999999999999A, 0011111110111001100110011001100110011001100110011001100110011010, +1.6 * 2^-4

試しに、double.MaxValue、double.MinValue、double.PositiveInfinity、double.NegativeInfinity、double.NaN を出してみると次のようになった。


179769313486232000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0000000000000000, 7FEFFFFFFFFFFFFF, 0111111111101111111111111111111111111111111111111111111111111111, +2 * 2^1023
-179769313486232000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0000000000000000, FFEFFFFFFFFFFFFF, 1111111111101111111111111111111111111111111111111111111111111111, -2 * 2^1023
∞, 7FF0000000000000, 0111111111110000000000000000000000000000000000000000000000000000, +1 * 2^1024
-∞, FFF0000000000000, 1111111111110000000000000000000000000000000000000000000000000000, -1 * 2^1024
NaN, FFF8000000000000, 1111111111111000000000000000000000000000000000000000000000000000, -1.5 * 2^1024

double.PositiveInfinity、double.NegativeInfinity、double.NaN に関しては最後の部分は無用だが、それぞれの内部表現は確認できた。

では、始めの方で行った、0.1 を足したり引いたりする例について試してみよう。

Program.cs
using System;

public static class Extension
{
    // 回数繰り返す
    public static void Repeat(this int times, Action action)
    {
        for (var counter = 0; counter < times; counter++)
            action();
    }
}

static class Program
{
    static void WriteLine(double value)
    {
        var i3eDouble = new IEEE754Double(value);
        Console.WriteLine($"{i3eDouble.Value:f16}, {i3eDouble.HexadecimalNumber}, {i3eDouble.BinaryNumber}, {i3eDouble.ToString()}");
    }

    static void Main()
    {
        // 0.0 から 0.1 ずつ20回増やしていき、0.1 ずつ20回減らしていく
        const int    times = 20;
        const double d     =  0.1;
        double       value =  0.0;
        WriteLine(value);
        times.Repeat(() => WriteLine(value += d));
        times.Repeat(() => WriteLine(value -= d));
    }
}

実行結果は次のようになった。 二進数の演算で誤差が生じている箇所が確認できるだろうか。


0.0000000000000000, 0000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, +1 * 2^-1023
0.1000000000000000, 3FB999999999999A, 0011111110111001100110011001100110011001100110011001100110011010, +1.6 * 2^-4
0.2000000000000000, 3FC999999999999A, 0011111111001001100110011001100110011001100110011001100110011010, +1.6 * 2^-3
0.3000000000000000, 3FD3333333333334, 0011111111010011001100110011001100110011001100110011001100110100, +1.2 * 2^-2
0.4000000000000000, 3FD999999999999A, 0011111111011001100110011001100110011001100110011001100110011010, +1.6 * 2^-2
0.5000000000000000, 3FE0000000000000, 0011111111100000000000000000000000000000000000000000000000000000, +1 * 2^-1
0.6000000000000000, 3FE3333333333333, 0011111111100011001100110011001100110011001100110011001100110011, +1.2 * 2^-1
0.7000000000000000, 3FE6666666666666, 0011111111100110011001100110011001100110011001100110011001100110, +1.4 * 2^-1
0.8000000000000000, 3FE9999999999999, 0011111111101001100110011001100110011001100110011001100110011001, +1.6 * 2^-1
0.9000000000000000, 3FECCCCCCCCCCCCC, 0011111111101100110011001100110011001100110011001100110011001100, +1.8 * 2^-1
1.0000000000000000, 3FEFFFFFFFFFFFFF, 0011111111101111111111111111111111111111111111111111111111111111, +2 * 2^-1
1.1000000000000000, 3FF1999999999999, 0011111111110001100110011001100110011001100110011001100110011001, +1.1 * 2^0
1.2000000000000000, 3FF3333333333333, 0011111111110011001100110011001100110011001100110011001100110011, +1.2 * 2^0
1.3000000000000000, 3FF4CCCCCCCCCCCD, 0011111111110100110011001100110011001100110011001100110011001101, +1.3 * 2^0
1.4000000000000000, 3FF6666666666667, 0011111111110110011001100110011001100110011001100110011001100111, +1.4 * 2^0
1.5000000000000000, 3FF8000000000001, 0011111111111000000000000000000000000000000000000000000000000001, +1.5 * 2^0
1.6000000000000000, 3FF999999999999B, 0011111111111001100110011001100110011001100110011001100110011011, +1.6 * 2^0
1.7000000000000000, 3FFB333333333335, 0011111111111011001100110011001100110011001100110011001100110101, +1.7 * 2^0
1.8000000000000000, 3FFCCCCCCCCCCCCF, 0011111111111100110011001100110011001100110011001100110011001111, +1.8 * 2^0
1.9000000000000000, 3FFE666666666669, 0011111111111110011001100110011001100110011001100110011001101001, +1.9 * 2^0
2.0000000000000000, 4000000000000001, 0100000000000000000000000000000000000000000000000000000000000001, +1 * 2^1
1.9000000000000000, 3FFE666666666668, 0011111111111110011001100110011001100110011001100110011001101000, +1.9 * 2^0
1.8000000000000000, 3FFCCCCCCCCCCCCE, 0011111111111100110011001100110011001100110011001100110011001110, +1.8 * 2^0
1.7000000000000000, 3FFB333333333334, 0011111111111011001100110011001100110011001100110011001100110100, +1.7 * 2^0
1.6000000000000000, 3FF999999999999A, 0011111111111001100110011001100110011001100110011001100110011010, +1.6 * 2^0
1.5000000000000000, 3FF8000000000000, 0011111111111000000000000000000000000000000000000000000000000000, +1.5 * 2^0
1.4000000000000000, 3FF6666666666666, 0011111111110110011001100110011001100110011001100110011001100110, +1.4 * 2^0
1.3000000000000000, 3FF4CCCCCCCCCCCC, 0011111111110100110011001100110011001100110011001100110011001100, +1.3 * 2^0
1.2000000000000000, 3FF3333333333332, 0011111111110011001100110011001100110011001100110011001100110010, +1.2 * 2^0
1.1000000000000000, 3FF1999999999998, 0011111111110001100110011001100110011001100110011001100110011000, +1.1 * 2^0
1.0000000000000000, 3FEFFFFFFFFFFFFD, 0011111111101111111111111111111111111111111111111111111111111101, +2 * 2^-1
0.9000000000000000, 3FECCCCCCCCCCCCA, 0011111111101100110011001100110011001100110011001100110011001010, +1.8 * 2^-1
0.8000000000000000, 3FE9999999999997, 0011111111101001100110011001100110011001100110011001100110010111, +1.6 * 2^-1
0.7000000000000000, 3FE6666666666664, 0011111111100110011001100110011001100110011001100110011001100100, +1.4 * 2^-1
0.6000000000000000, 3FE3333333333331, 0011111111100011001100110011001100110011001100110011001100110001, +1.2 * 2^-1
0.5000000000000000, 3FDFFFFFFFFFFFFC, 0011111111011111111111111111111111111111111111111111111111111100, +2 * 2^-2
0.4000000000000000, 3FD9999999999996, 0011111111011001100110011001100110011001100110011001100110010110, +1.6 * 2^-2
0.3000000000000000, 3FD3333333333330, 0011111111010011001100110011001100110011001100110011001100110000, +1.2 * 2^-2
0.2000000000000000, 3FC9999999999993, 0011111111001001100110011001100110011001100110011001100110010011, +1.6 * 2^-3
0.0999999999999998, 3FB999999999998C, 0011111110111001100110011001100110011001100110011001100110001100, +1.6 * 2^-4
-0.0000000000000002, BCAC000000000000, 1011110010101100000000000000000000000000000000000000000000000000, -1.75 * 2^-53

まとめ

以上、double の内部表現について、C# を使って説明してみた。

2015年12月22日

[Event] 「Hokuriku ComCamp 2016 powered by MVPs」 (2016年2月20日)が開催されます

昨年の様子

この度、石川県金沢市で、毎年恒例のマイクロソフトの技術に関する勉強会 ComCamp を開催します。 是非ご参加ください。

Japan ComCamp 2016 powered by MVPs の北陸会場であるこのイベントでは、日本マイクロソフトの人気エバンジェリスト西脇 資哲さんにドローンとマイクロソフトのテクノロジーについて語っていただきます。
その他、恒例の北信越の C# 大好き MVP 3名による、やらせなしライブ コーディングなど盛り沢山です。
ぜひご参加ください! 学生の方も大歓迎です。

前回

※ 前回の様子:

富山合同勉強会 .NET & Java ~ぶりしゃぶで休もう~ - MVP Community Camp 2015 北陸会場 -

開催概要

Hokuriku ComCamp 2016 powered by MVPs
日時 2016/2/20(土) 11:00-18:10
(受付 10:30-、終了後に近くで懇親会)
※ 開始・終了時刻は前後する可能性があります。
会場 ITビジネスプラザ武蔵 6F 交流室2
石川県金沢市武蔵町14番31号
主催 Hokuriku.NET
福井情報技術者協会[FITEA]
協力 日本マイクロソフト
詳細/お申込み http://hokurikucomcamp.connpass.com/event/23628/

スケジュール

※セッションの順番は変更される可能性があります。

時間 セッション スピーカー
11:00~11:10 オープニング
11:10~12:00 「C# 大好き MVP による、C# ドキドキ・ライブコーディング!!」

北信越の Visual Studio and Development Technologies MVP 3名が C# の魅力について語ります。LT や コーディング ライブ。
石野 光仁 氏
Microsoft MVP for Visual Studio and Development Technologies
コミュニティ: AILight

鈴木 孝明 氏
Microsoft MVP for Visual Studio and Development Technologies
ブログ: xin9le note

小島 富治雄 氏
Microsoft MVP for Visual Studio and Development Technologies
ブログ: プログラミング C# - 翔ソフトウェア (Sho's)
13:00~13:40 「Windows 10 楽しい使い方」

Windows 10には、今までのWindowsと違う、新しい機能がたくさん盛り込まれました。Windows 10の新しい機能で楽しくパソコンを使う方法を紹介します。
さくしま たかえ 氏
Microsoft MVP for Windows Experience
ブログ: 世の中は不思議なことだらけ
Microsoft Online Services 検証の館
著書: Windows 10 完全制覇パーフェクト
13:50~15:00 「エンジニアよ興味を持ち続けろ! ドローンだってITだ」

日本マイクロソフトのエバンジェリストなのになぜか興味をもってしまったドローン。
しかしやがて、Microsoftテクノロジーと深い関係に。
興味を持ち続けることの重要性を伝えつつ、ドローンの現状と利活用の世界、さらにはドローンと Microsoft テクノロジーについて解説します。
軽量ドローンは持ち込んで飛ばしますよ~。
西脇 資哲 氏
日本マイクロソフト株式会社 業務執行役員・エバンジェリスト
15:10~15:50 「DataTableを徹底解剖しようぜ!」

DataTableクラスについて、色々といじったり語り合ったりとかどうでしょう?
片桐 継 氏
サイト
16:00~16:40 未定 (Windows Server 2016 Essentials 関連) 澤田 賢也 氏
Microsoft MVP for Cloud and Datacenter Management
ブログ: Windows Server Essentials を中心とした雑記
著書: 現場で役立つ Windows Server 2012 R2 Essentials 構築・運用ガイド
16:50~17:30 「馴染みの .NET Framework で CMS を使ってみよう」 矢後 比呂加 氏
Microsoft MVP for Visual Studio and Development Technologies
ブログ: miso_soup3 Blog
17:40~18:10 ライトニングトーク (LT) 金子 雄一氏
リモートデスクトップ関連
18:30-21:00 懇親会 こちらからお申込みください。

Japan ComCamp 2016 powered by MVPs リンク

Hokuriku.NET のロゴ ステッカーと Microsoft MVP 飴、Microsoft 飴

2016年01月31日

[Event] 『富山合同勉強会 2016 ~ぶりしゃぶ会~』 (2016/01/30)

富山合同勉強会 2016 ~ぶりしゃぶ会~』に参加してきた。

Babylon.js + TypeScript で簡単 3D プログラミング」という題で、ライトニングトーク (LT) を行った。

イベントの概要

富山合同勉強会 2016
開催日 2016/01/30
会場 富山大学 五福キャンパス
詳細 http://toyama-eng.connpass.com/event/24840/

イベントの写真

イベントの様子
懇親会は恒例のブリしゃぶ

当日の様子

当日のスライド (一部)

参加された方が書かれた記事

2016年02月22日

[Event] 「Hokuriku ComCamp 2016 powered by MVPs」 (2016年2月20日)を開催しました

※ 「C# 大好き MVP による、C# ドキドキ・ライブコーディング!!」でテトリス的なゲームを作った。(Hokuriku ComCamp 2016 - 2月20日)」に続きます。

石川県金沢市で、毎年恒例のマイクロソフトの技術に関する勉強会 ComCamp を開催しました。

53 名の方に参加していただきました。

Japan ComCamp 2016 powered by MVPs の北陸会場であるこのイベントでは、日本マイクロソフトのカリスマ エバンジェリスト西脇 資哲さんが会場でドローンを飛ばしながら、IT技術との関連を熱く語っていただいたり、恒例の北信越の C# 大好き MVP 3名による、C# ライブ コーディングなどおおいに盛り上がりました。

当日の盛り上がりは Twitter などで見ることができます。

セッションは、当日ストリーミング配信されました。後日 Channel 9 で公開される予定です。

開催概要

Hokuriku ComCamp 2016 powered by MVPs
日時 2016/2/20(土) 11:00-18:10 (終了後懇親会)
会場 ITビジネスプラザ武蔵 6F 交流室2
石川県金沢市武蔵町
主催 Hokuriku.NET
福井情報技術者協会[FITEA]
協力 日本マイクロソフト
詳細 http://hokurikucomcamp.connpass.com/event/23628/

スケジュール

セッション スピーカー  
オープニング 小島 富治雄 氏
Microsoft MVP for Visual Studio and Development Technologies
ブログ: プログラミング C# - 翔ソフトウェア (Sho's)
オープニング
「C# 大好き MVP による、C# ドキドキ・ライブコーディング!!」

北信越の Visual Studio and Development Technologies MVP 3名が C# の魅力について語ります。LT や コーディング ライブ。
石野 光仁 氏
Microsoft MVP for Visual Studio and Development Technologies
コミュニティ: AILight

鈴木 孝明 氏
Microsoft MVP for Visual Studio and Development Technologies
ブログ: xin9le note

小島 富治雄 氏
「C# 大好き MVP による、C# ドキドキ・ライブコーディング!!」 「C# 大好き MVP による、C# ドキドキ・ライブコーディング!!」 「C# 大好き MVP による、C# ドキドキ・ライブコーディング!!」
「Windows 10 楽しい使い方」

Windows 10 には、今までのWindowsと違う、新しい機能がたくさん盛り込まれました。Windows 10 の新しい機能で楽しくパソコンを使う方法を紹介します。
さくしま たかえ 氏
Microsoft MVP for Windows Experience
ブログ: 世の中は不思議なことだらけ
Microsoft Online Services 検証の館
著書: Windows 10 完全制覇パーフェクト
 
「DataTableを徹底解剖しようぜ!」

DataTableクラスについて、色々といじったり語り合ったりとかどうでしょう?
片桐 継 氏
サイト
 
「エンジニアよ興味を持ち続けろ! ドローンだってITだ」

日本マイクロソフトのエバンジェリストなのになぜか興味をもってしまったドローン。
しかしやがて、Microsoftテクノロジーと深い関係に。
興味を持ち続けることの重要性を伝えつつ、ドローンの現状と利活用の世界、さらにはドローンと Microsoft テクノロジーについて解説します。
軽量ドローンは持ち込んで飛ばしますよ~。
西脇 資哲 氏
日本マイクロソフト株式会社 業務執行役員・エバンジェリスト
「エンジニアよ興味を持ち続けろ! ドローンだってITだ」西脇 資哲 氏 「エンジニアよ興味を持ち続けろ! ドローンだってITだ」西脇 資哲 氏 「エンジニアよ興味を持ち続けろ! ドローンだってITだ」西脇 資哲 氏
「いまから始める Windows Server」

Windows Server を触ったことがない方や開発用のサーバーを構築する必要がある方、中小企業などでコンピューターやデータの管理を効果的に行いたい方などに最新の Windows Server を使って紹介をいたします。
澤田 賢也 氏
Microsoft MVP for Cloud and Datacenter Management
ブログ: Windows Server Essentials を中心とした雑記
著書: 現場で役立つ Windows Server 2012 R2 Essentials 構築・運用ガイド
 
「馴染みの .NET Framework で CMS を使ってみよう」 矢後 比呂加 氏
Microsoft MVP for Visual Studio and Development Technologies
ブログ: miso_soup3 Blog
「馴染みの .NET Framework で CMS を使ってみよう」矢後 比呂加 氏
ライトニングトーク (LT) 金子 雄一氏
リモートデスクトップ関連

むろほしりょうた 氏
「『var禁止』禁止」

山P 氏
「AzureAutomationを使ってみた話」
「『var禁止』禁止」むろほしりょうた 氏
クロージング じゃんけん大会 じゃんけん大会
懇親会   懇親会

関連サイト

資料公開
サンプル公開
参加レポート

2016年02月25日

「C# 大好き MVP による、C# ドキドキ・ライブコーディング!!」でテトリス的なゲームを作った。(Hokuriku ComCamp 2016 - 2月20日)

※ 「[Event] 「Hokuriku ComCamp 2016 powered by MVPs」 (2016年2月20日)を開催しました」の続きです。

Hokuriku ComCamp 2016 powered by MVPs」では、北信越の3人の Microsoft MVP (石野 光仁 さん、鈴木 孝明 さん、私) で毎年恒例/大好評の「C# 大好き MVP による、C# ドキドキ・ライブコーディング!!」というセッションを行いました。

セッションは、毎回石野 さんが考えてくださっています。 無茶ブリが楽しい素敵なセッションだと思います。

今回の石野さんからのお題は、「30分でテトリス ライクなゲームを作ろう!」というかなりチャレンジングなものでした。

「C# 大好き MVP による、C# ドキドキ・ライブコーディング!!」当日の様子

ライブ コーディングは毎回ドキドキものです。

交代で喋り、自分以外が喋っているときにコーディングさせてもらえる、という進行でした。

「C# 大好き MVP による、C# ドキドキ・ライブコーディング!!」の様子
「C# 大好き MVP による、C# ドキドキ・ライブコーディング!!」 「C# 大好き MVP による、C# ドキドキ・ライブコーディング!!」

私の分の資料

私の作った部分について説明してみます。

私の分の資料を下記で公開しました (slideshareDocs.com にそれぞれ)。

戦略やどのようにインチキ工夫したかを書いてみました。

私のテトリス ライクなゲームの構成

全体の構成は次のようになっています。

FTetris.Model ビューから分離したモデル部分の C#/.NET 版です。他の .NET のプログラムから参照されるクラス ライブラリ (DLL) です。  
FTetris.WPF WPF (Windows Presentation Foundation) で作ったゲームです。C# で書かれています。View と ViewModel からなっており、上記 Model を参照しています。つまり、WPF の標準的な設計方法である MVVM (Model - View - ViewModel) アーキテクチャーで作られています。
FTetris.WPF
FTetris.WinForm Windows Forms 版です。C# で書かれており、GDI+ で描画を行っています。セッション中のライブで主に作った部分です。
FTetris.WinForm
FTetris.Console コンソール アプリケーション版です。C# で書かれています。色がついています。
FTetris.Console
FTetris.Matrix セッションでモデルを説明するためだけに作った、映画マトリックスをイメージした緑色の数字が降ってくる版です。
FTetris.Matrix
FTetris.Win セッションで「インチキ」と称した、Windows Forms のメイン フォームに WebBrowser を貼って下記 TypeScript 版の URL を渡しただけの 1 分で作れる版です。
FTetris.Win
FTetris.TS 上の「インチキ」をやるために作っておいた Web アプリケーション版です。 TypeScript で書かれています。 モデル部分は、上の FTetris.Model から移植しました。 Three.js から WebGL を使っています。 Windows 以外の OS でも動作します (スマートフォンやタブレットでも動作可能ですが、操作にはキーボードが必要です)。
FTetris.TS

上記資料にも書きましたが、モデルを (WPF や Windows Forms、コンソール等の) ビューから分離しています。

ゲーム本体のロジックをモデルとして書くことで、ビュー側の実装に依存しない形にし、各プラットフォームで使えるようにしました。

FTetris.Model 等の設計

モデル部分の設計は次のようになりました。

このモデルは、先ず C# で実装し、それから TypeScript に移植しましたので、TypeScript 版もほぼ同じ設計です。

モデル部分 FTetris.Model のクラス図
モデル部分 FTetris.Model のクラス図

このモデルを使った例として、WPF 版の設計は次のようになりました。

MVVM (Model - View - ViewModel) になっています。

WPF 版ゲーム FTetris.WPF のクラス図
WPF 版ゲーム FTetris.WPF のクラス図

ソースコード

私の分の全体のソースコードは GitHub で公開しています。

TypeScript 版 (HTML/CSS + TypeScript + Three.js + WebGL)

TypeScript で書いた Web アプリケーション版は下のリンクから実際に遊ぶことができます。

くるくる回るようにしたので、通常のものよりだいぶ難しくなっています。

マウスのドラッグやホイールで視点 (カメラ位置) を変えられます。

キー操作

ゲームのキー操作は、どの版も共通で、次のようになっています。

キー 動作
左移動
右移動
回転 (時計回り)
回転 (反時計回り)
Space 落下
Enter 最初からプレイ

関連サイト

鈴木さんと石野さんも、ご自身の分のゲームを公開されています。

三者三様で興味深い内容です。

レポート

鈴木さんの記事はこちらです。

彼の作った部分について、アプリケーションやソースコード、戦略や設計なども公開されています。

アプリケーションやソースコードの公開

2016年03月31日

Build 2016 1日目の発表内容まとめ

マイクロソフトの最大の開発者向けイベント「Build 2016」がサンフランシスコで開催中。

Build 2016

内容は、一部ライブで観ることができる。

1日目に関する記事が、既にあちこちに上がっている。

1日目のキーノートに関する記事も沢山。

1日目の発表内容は以下の通りだ。

1. Windows 10 関連の発表

1.1. BashシェルをWindowsに搭載

Windows 10 で Linux のシェルのバイナリ―をそのまま動作できるようにするとのこと。

マイクロソフトは、最近 Windows 以外の OS との親和性をどんどんと高めており、その一環だろう。

1.2. Windows 10 の大型アップデートについて

2. 「Microsoft Bot Framework」を公開

クラウド上の「マシン ラーニング」機能の応用としての「人工知能」関連だ。

最近マイクロソフトや Google、Amazon などが取り組んでいることで話題になっている。

今回発表されたのは、Bot を作るためのフレームワーク。

3. Visual Studio 関連の発表

Visual Studio "15" Preview と Visual Studio 2015 Update 2 RTM が公開された。

Visual Studio 2015 Update 2 は「プログラムと機能」から Visual Studio 2015 の「変更」を選んでインストールするのが簡単だ。

また、MSDN からは Update 2  込みの Visual Studio 2015 がダウンロードできるようになっている。

4. その他

その他、以下のような発表があった。

4.1 Desktop App Converter

Win32/.NETアプリを UWP (*1) アプリに変換するツール。

(*1) UWP とは: ユニバーサル Windows プラットフォームの略。Windows 10 を積んだPC や Phone、XBox One、HoloLens、Surface Hub などで動作可能。

4.2 XBox Dev Mode

Visual Studio で XBox One 用に UWP アプリを開発可能に。Visual Studio  からリモートデバッグもできる。

4.3 HoloLens 関連

開発者エディションを提供開始。

4.4 Cortana 関連

様々なアップデートがあったようだ。

4.5 .NET Framework 4.6.2 Preview

2016年04月01日

Build 2016 2日目の発表内容まとめ

※ 「Build 2016 1日目の発表内容まとめ」の続き。

2日目のキーノートに関する記事。

2日目の発表内容は以下の通りだ。

1. Xamarin が Visual Studio に

マイクロソフトが買収した Xamarin (C# を使って iOS や Android などのネイティブ アプリが作れるツール) が、Visual Studio に含まれることになった。

Visual Studio の Enterprise エディションや Professional エディションの他、無償版の Community エディションでも追加料金なしで使うことができるようになる。

2. Microsoft Azure 関連

3. その他

2016年07月02日

Microsoft MVP を再受賞しました (My 12th MVP Award from Microsoft)

MVP_Logo_Horizontal_Preferred_Cyan300_CMYK_72ppi.png

Microsoft MVP Award を再受賞しました。12年目になります。

My MVP Profile

  • Microsoft MVP for Development Tools - Visual C# (Jul. 2005 - Dec. 2014)
  • Microsoft MVP for .NET (Jan. 2015 - Oct. 2015)
  • Microsoft MVP for Visual Studio and Development Technologies (Nov. 2015 - Jun. 2017)

いくつになっても、こうして褒められるのは嬉しいものです。 マイクロソフトのような大企業が本気で褒めてくれる。ありがたいことです。

沢山の素晴らしい方々と出会える機会をいただいております。 お世話になっている皆様に感謝です。いつもありがとうございます。

2016年12月16日

Visual Studio 2017 のライブ ユニット テスト機能による「車窓からの TDD」

Visual Studio Advent Calendar 2016 の12月16日の記事。

logo

コーディング機能やデバッグやテストの機能が大幅パワーアップ! 次期バージョン「Visual Studio 2017 RC」 | CodeZine という記事を書かせていただき、その中で、「Visual Studio 2017 RC」の「特におすすめの新機能」としてライブ ユニット テストに触れた。 もう少し具体的にライブ ユニット テストを紹介してみたい。

題材として、『ソフトウェア技術者サミット in 福井 2016』 (7月16日) の「テスト駆動開発ライブ 〜C#ペアプログラミング実況中継」というセッションの中で、平鍋健児氏と C# と Visual Studio 2015 を使っておこなったテスト駆動開発 (Test-Diven Development: TDD) のデモンストレーションを用いることにする。

「テスト駆動開発ライブ 〜C#ペアプログラミング実況中継」 | 『ソフトウェア技術者サミット in 福井 2016』
「テスト駆動開発ライブ 〜C#ペアプログラミング実況中継」 | 『ソフトウェア技術者サミット in 福井 2016』

このデモンストレーションは、北野弘治氏と平鍋健児氏による「車窓からの TDD | オブジェクト倶楽部」 を C# で再現したものだ。 テストを書きながら、スタックのクラスを作成していく。 順を追ってやってみよう。

プロジェクトの作成

最初に、Visual Studio 2017 RC を起動し、テストするプロジェクトとテストされるプロジェクトを作る。

  • テストするプロジェクトの作成
    • メニューから「ファイル > 新規作成 > プロジェクト > Visual C# > テスト > 単体テスト プロジェクト Visual C#」
単体テスト プロジェクト (C#) の作成
テストするプロジェクト – 単体テスト プロジェクト (C#) の作成
  • テストされるプロジェクトの作成
    • ソリューション エクスプローラでソリューション名を右クリックし、「追加 > 新しいプロジェクト > Visual C# > クラス ライブラリ (.NET Framework) Visual C#」
テストされるプロジェクト – クラス ライブラリ (C#) の作成

ライブ ユニット テストの開始

では、早速 Visual Studio 2017 の新機能であるライブ ユニット テストを開始しよう。 次のようにメニューから選ぶだけだ。

  • メニュー > テスト > Live Unit Testing > Start
ライブ ユニット テストの開始
ライブ ユニット テストの開始

最初のテスト

それでは、単体テスト プロジェクトに最初のテストを追加してみよう。 TDD では、テストされるコードよりテスト コードを先に書くことが多い。

最初のテスト: 「作ったら空」

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Shos.Collections.Test
{
    [TestClass]
    public class Stackのテスト
    {
        [TestMethod]
        public void 作ったら空()
        {
            var stack = new Stack<int>();
            Assert.IsTrue(stack.IsEmpty);
        }
    }
}

クラス名を「Stackのテスト」とし、「作ったら空」というテストを追加した。 まだ Stack クラスは作っていないので、静的なエラー (コンパイル時エラー) になり、エラー箇所に赤い波線が表示される。

最初のテストを作った直後
最初のテストを作った直後

バルブ アイコンをクリックするか、エラー箇所にカーソルがある状態で 'Ctrl+.' をタイプし、クラス ライブラリ プロジェクトの方に Stack クラスを作成しよう。

Stack クラスの作成: 新しい型の生成
Stack クラスの作成: 新しい型の生成
Stack クラスの作成: クラス ライブラリ プロジェクトの方に Stack クラスを生成
Stack クラスの作成: クラス ライブラリ プロジェクトの方に Stack クラスを生成

OK ボタンを押すと、クラス ライブラリ プロジェクトに Stack クラスが生成される。

生成された Stack クラス

namespace Shos.Collections
{
    public class Stack<T>
    {
        public Stack()
        {
        }
    }
}

テストの方では、まだ "stack.IsEmpty" の部分が静的エラーになっている。

バブル アイコンまたは 'Ctrl+.' から「プロパティ 'Stack.IsEmpty' を生成します」を選ぼう。

プロパティ 'IsEmpty' の生成
プロパティ 'Stack.IsEmpty' の生成

Stack クラスに、プロパティ "IsEmpty" が生成される。 このように、Visual Studio の助けを借りることで、スムーズに TDD を行うことができる。

ライブ ユニット テスト

"IsEmpty" が作られたことで、で静的エラーが消えた。

すると、ここでライブ ユニット テスト機能が自動で働く。 テストする側のクラス「Stackのテスト」の左に、テストが失敗したことを示す赤い×印が表示される。

ライブ ユニット テストの結果: テストする側のクラス
ライブ ユニット テストの結果: テストする側のクラス

そして、テストされる側のクラス Stack の左にも、テストが失敗したことを示す赤い×印があらわれる。

ライブ ユニット テストの結果: テストされる側のクラス
ライブ ユニット テストの結果: テストされる側のクラス

では、テストを成功させるために、Stack クラスを修正しよう。 テスト コード中の "IsEmpty" にカーソルがある状態で 'F12' を押す (または、"IsEmpty" を右クリックして「定義に移動」)。

ここでは、"IsEmpty" の実装を "public bool IsEmpty => true;" に変更してみる。

そうすると、ライブ ユニット テストによって自動でテストが通り、Stack クラスと「Stackのテスト」クラスに表示されていた赤い×印緑のチェックに変わる。

ライブ ユニット テストの結果 - テスト成功 - Stack クラス
ライブ ユニット テストの結果 – テスト成功 – Stack クラス
ライブ ユニット テストの結果 - テスト成功 「Stackのテスト」クラス
ライブ ユニット テストの結果 – テスト成功 「Stackのテスト」クラス

このように TDD では、次のようなステップで踏み、それを繰り返しながら、プログラミングを進めていく。

  1. テストを書く
  2. (コンパイラーによる静的エラー – 静的なテスト)
  3. テストされる側のコードを書き、テストを失敗させる (ライブ ユニット テストによる動的エラー – 動的なテスト – レッド)
  4. テストを成功させる分だけテストされる側のコードを修正し、テストを成功させる (グリーン)
  5. 1 に戻る

テストされる側にコードを追加する場合は、テストを追加し一度テストを失敗させる。 そして、テストが通る分だけのコードをテストされる側に追加する。

テストによってコードのその時点でのあるべき姿を記述する。 つまり、メソッドなりクラスなりの外からみた仕様をテストで先に記述するわけだ。 このテストが通ったことをもって、コードがあるべき姿になったことにする。 これを繰り返すことで、あるべき姿を少しずつインクリメンタルに作っていくのだ。 メソッドやクラスが、その時点でのあるべき姿になったかどうかが、テストによってフィードバックされる。

そして、Visual Studio 2017 のライブ ユニット テスト機能を使えば、このフィードバックがリアルタイムに行われることになるのだ。 仕様を満たしていないコードがレッドで、仕様を満たしたコードがグリーンで、リアルタイムで可視化される。 とても効率の良いコーディング環境だ。

テスト「Pushしたら空じゃなくなる」の追加

さて、テストを追加してみよう。

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Shos.Collections.Test
{
    [TestClass]
    public class Stackのテスト
    {
        Stack<int> stack;

        [TestInitialize]
        public void 準備() => stack = new Stack<int>();

        [TestMethod]
        public void 作ったら空()
        {
            Assert.IsTrue(stack.IsEmpty);
        }

        [TestMethod]
        public void Pushしたら空じゃなくなる()
        {
            stack.Push(1);
            Assert.IsFalse(stack.IsEmpty);
        }
    }
}

"stack = new Stack<int>()" のコードが重複するのが嫌なので、"[TestInitialize] public void 準備() => stack = new Stack<int>();" というコードを追加した。 このメソッドは、個々のテストの実行前に一回ずつ呼ばれる。 この部分は、まだテストされていないが、このようなコードの左には、青い横棒が表示される。 テストしていないコードが可視化されるわけだ。 これにより、常にカバレッジが確認できる。

テストされていないコードの左には青い横棒
テストされていないコードの左には青い横棒

テストを追加した直後は、"stack.Push(1)" の "Push" が静的エラーになる。 Stack.Push メソッドがまだないためだ。 バブル アイコンまたは 'Ctrl+.' から「メソッド 'Stack.Push' を生成します」を選ぼう。 また動的テストが自動で走り、ちゃんと失敗する。

テスト「Pushしたら空じゃなくなる」の失敗
テスト「Pushしたら空じゃなくなる」の失敗

テストが通るように Stack クラスの Push メソッドを修正しよう。 テスト コード中の "Push" にカーソルがある状態で 'F12' を押す (または、"Push" を右クリックして「定義に移動」)。 "public void Push(int v) { throw new NotImplementedException(); }" の左には赤い×印が表示されているはずだ。

Stack クラスを、例えば次のように変更すると、テストが通って、緑のチェックに変わる。

Stack クラスを修正するとテストが通った
Stack クラスを修正するとテストが通った

TDD のリズム

ではまた、テストを追加しよう。 静的エラーになる (赤い波線)。

テスト「PushしてTopをみたらPushしたやつ」の追加
テスト「PushしてTopをみたらPushしたやつ」の追加

'Ctrl+.' から「プロパティ 'Stack.Top' を生成します」を選択。 また動的テストが自動で走り、ちゃんと失敗 (赤い×印)。

テスト「PushしてTopをみたらPushしたやつ」がちゃんと失敗
テスト「PushしてTopをみたらPushしたやつ」がちゃんと失敗

'F12' を押して、Stack クラスを修正。 また動的テストが自動で走って成功 (緑のチェック)。

Stack クラスを修正するとテストが成功 - Stack クラス
Stack クラスを修正するとテストが成功 – Stack クラス
Stack クラスを修正するとテストが成功 - 「Stackのテスト」クラス
Stack クラスを修正するとテストが成功 – 「Stackのテスト」クラス

今度は、テスト「PushせずにTopをみたら例外」の追加。失敗。

テスト「PushせずにTopをみたら例外」を追加すると失敗
テスト「PushせずにTopをみたら例外」を追加すると失敗

Stack クラスを修正するとテストが成功。

Stack クラスを修正するとテストが成功 - Stack クラス
Stack クラスを修正するとテストが成功 – Stack クラス
Stack クラスを修正するとテストが成功 - 「Stackのテスト」クラス
Stack クラスを修正するとテストが成功 – 「Stackのテスト」クラス

TDD ではレッドグリーンの繰り返しのリズムが大切だと思う。 テストのための無駄な作業を省いてくれるライブ ユニット テスト機能が、このリズムを心地よいものにし、プログラミングをより楽しくしてくれることだろう。

この例では、なるべく小さなステップで進めるようにしているので、テストが通る最小限の修正にとどめているが、コードが明らかであるような場合は、もっと大きな修正をしても構わないだろう。

リファクタリングについて

今回は出てきていないが、TDD では、テストが通った後にリファクタリング (外から見た仕様を変えずに内部構造を改善すること) を行うことが多い。Visual Studio 2017 のライブ ユニット テスト機能を使うと、このリファクタリングの際も常に自動で静的テストと動的テストによって、仕様どおりであり続けていることがチェックされることになる。 リファクタリング中に誤ってバグを作ってしまってもすぐに気付けることだろう。

リファクタリングでは、なるべくエラーが起きている時間を短くすることが肝要だが、こうしたエラーの可視化によるフィードバックが常に起こることは、エラー時間の最小化につながるだろう。

また、リファクタリング中の継続的なフィードバックによって、なるべくレッドにならないような手順でリファクタリングをするような良い習慣が生まれるのではないだろうか。

もちろん、Visual Studio 2017 で追加された多くの新たなリファクタリング機能も安全なリファクタリングに役立ってくれることだろう。

まとめ

Visual Studio 2017 RC の新たな機能であるライブ ユニット テストは、機能を使うことに手間を取られない。 機能を使うことに振り回されて、コーディングのリズムを狂わされることがない。 つまり、Visual Studio 2017 RC を使うと TDD が楽しく快適になる。

2016年12月23日

[C#] Tips: interface と partial class で横断的関心事を分離

C丼

C# Advent Calendar 2016 の12月23日の記事。

以前、「C# Tips: interface を 抽象クラス (abstract class) とどう使い分けるか」という記事を書いた。 その中で、「アスペクトの実装を便宜上 (言語の都合上) interface で行う」というイディオムについて触れた。 この記事はその続きだ。 より具体的にこのイディオムを紹介する。

分割攻略と疎結合/高凝集

ソフトウェア開発というものは往々にして複雑さとの戦いになるものだが、プログラムの設計において複雑さに立ち向かうための基礎となる考え方に、分割攻略 (Divide and Conquer、分割統治) というものがある。 大きく複雑な問題をそのまま解くのは大変なので、より小さくシンプルな問題に分けることで、全体としての複雑さを下げて解こう、という戦略だ。

大きく複雑な問題をそのまま解く
大きく複雑な問題をそのまま解く

上の図のようにすると大変なので、たとえば次のようにする。 10倍の大きさの問題の複雑さは10倍ではきかない、というのが基本的なアイディアだ。

分割攻略: 大きく複雑な問題を複数のより小さくシンプルな問題に分けて解く
分割攻略: 大きく複雑な問題を複数のより小さくシンプルな問題に分けて解く

分割するときに重要となるのが、「どう分けるか」だ。 一般的には次のようになれば良いとされている。

  • 疎結合 (low coupling): 分割されたもの同士の結び付きを少なく/弱く・
  • 高凝集 (high cohesion): 一つの分割単位の中が一つの関心事だけになり、かつ、その関心事がその分割単位の中だけにある

ソフトウェアの設計では、疎結合で高凝集になるように分割することが大切、ということだ。 いくら小さな単位に分けても、問題が互いにからみあっていたり、シンプルな単位に分かれていなかったりすると、うまく複雑さが減ってくれないからだ。

そして、これを実現するために、様々な設計の考え方がある。 オブジェクト指向という考え方を使っても、ある程度これを行うことができる。 オブジェクト指向では、クラスやオブジェクトなどという単位に分けることでこれを行うわけだ。

オブジェクト指向と横断的関心事

最初の CAD の設計

ところが、オブジェクト指向といっても万能なわけではなく、クラスやオブジェクトなどに分けるというだけでは、必ずしも高凝集にならない。

例をみてみよう。 以下は、C# による CAD (Computer-Aided Design) の設計の例だ。

CAD のクラス図の例
CAD のクラス図の例

図の中の赤いクラスがモデル (Model) で、抽象図形クラス Figure と、そこから継承した複数の具象図形クラス LineFigure・EllipseFigure がある。

青いクラスがビュー (View) で、モデルの IEnumerable<Figure> というインタフェイスを使ってモデルとデータバインドし、個々の図形を描画する。

C# によるソース コードは、たとえば次のようなものになる (説明に必要のない部分は省略)。

MiniCad.cs
using System;
using System.Collections;
using System.Collections.Generic;

abstract class Figure
{
    public abstract void Draw();
}

class LineFigure : Figure
{
    public override void Draw() => Console.WriteLine("Line!"); // 仮実装
}

class EllipseFigure : Figure
{
    public override void Draw() => Console.WriteLine("Ellipse!"); // 仮実装
}

class CadModel : IEnumerable<Figure>
{
    public IEnumerator<Figure> GetEnumerator()
    {
        yield return new LineFigure   ();
        yield return new LineFigure   ();
        yield return new EllipseFigure();
        yield return new LineFigure   ();
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

class CadView
{
    public IEnumerable<Figure> DataSource {
        set { value.ForEach(figure => figure.Draw()); }
    }
}

class Program
{
    static void Main()
    {
        new CadView().DataSource = new CadModel();
    }
}

static class EnumerableExtensions
{
    public static void ForEach<TElement>(this IEnumerable<TElement> @this, Action<TElement> action)
    {
        foreach (var element in @this)
            action(element);
    }
}

この実装では仮にコンソールに文字列を出力しているだけなので、実行結果は次のようになる。

Line!
Line!
Ellipse!
Line!

この段階ではまだ単純な設計であるため、オブジェクト指向を使うことでそこそこ高凝集になっている。

CAD の設計の変更

さて、ここまで作った後で、ファイルなどに保存するときのためにシリアライズ機能を付けたくなったとする。 CAD は一般的に様々なフォーマットをサポートすることが多いものだが、ここでは仮に SVG (Scalable Vector Graphics) 形式と独自のバイナリ―形式をサポートするものとしよう。

このために、それぞれの図形クラスに、2つの形式のシリアライズのための SvgSerialize と BinarySerialize という2つのメソッドを追加してみるとどうなるだろうか。

シリアライズ機能を付けた CAD のクラス図の例
シリアライズ機能を付けた CAD のクラス図の例

C# によるソース コードは、たとえば次のように変わるだろう。

MiniCad.cs
using System;
using System.Collections;
using System.Collections.Generic;

abstract class Figure
{
    public abstract void Draw();
    public abstract void SvgSerialize();
    public abstract void BinarySerialize();
}

class LineFigure : Figure
{
    public override void Draw() => Console.WriteLine("Line!"); // 仮実装
    public override void SvgSerialize() => Console.WriteLine("SvgSerialize line!"); // 仮実装
    public override void BinarySerialize() => Console.WriteLine("BinarySerialize line!"); // 仮実装
}

class EllipseFigure : Figure
{
    public override void Draw() => Console.WriteLine("Ellipse!"); // 仮実装
    public override void SvgSerialize() => Console.WriteLine("SvgSerialize ellipse!"); // 仮実装
    public override void BinarySerialize() => Console.WriteLine("BinarySerialize ellipse!"); // 仮実装
}

class CadModel : IEnumerable<Figure>
{
    public void SvgSerialize() => this.ForEach(figure => figure.SvgSerialize());
    public void BinarySerialize() => this.ForEach(figure => figure.BinarySerialize());

    public IEnumerator<Figure> GetEnumerator()
    {
        yield return new LineFigure   ();
        yield return new LineFigure   ();
        yield return new EllipseFigure();
        yield return new LineFigure   ();
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

class CadView
{
    public IEnumerable<Figure> DataSource {
        set { value.ForEach(figure => figure.Draw()); }
    }
}

class Program
{
    static void Main()
    {
        var model = new CadModel();
        new CadView().DataSource = model;
        model.SvgSerialize   ();
        model.BinarySerialize();
    }
}

static class EnumerableExtensions
{
    public static void ForEach<TElement>(this IEnumerable<TElement> @this, Action<TElement> action)
    {
        foreach (var element in @this)
            action(element);
    }
}

そして、これが実行結果だ。

Line!
Line!
Ellipse!
Line!
SvgSerialize line!
SvgSerialize line!
SvgSerialize ellipse!
SvgSerialize line!
BinarySerialize line!
BinarySerialize line!
BinarySerialize ellipse!
BinarySerialize line!

しかし、この設計では、高凝集になっていない部分がでてきてしまっているのがお分かりだろうか。

図形クラスという部分に関しては、ある程度高凝集になっている。 各図形に関する仕事 (それぞれの図形の描画やシリアライズ) は、ちゃんと各図形でやっているように見える。

ところが、SVG シリアライズ、あるいはバイナリー シリアライズという関心事に目を向けてみると、そちらはどうなっているだろう。

たとえば、SVG シリアライズに関するメソッドは Figure、LineFigure、EllipseFigure、CadModel という複数のクラスにまたがってしまっている。 バイナリー シリアライズについても同様だ。 これでは、高凝集、すなわち「一つの分割単位の中が一つの関心事だけになり、かつ、その関心事がその分割単位の中だけにある」とは言えないだろう。

このように、オブジェクト指向では複数のクラスをまたがる関心事というものがでてきて設計に困ることがある。 この場合だと、それぞれの図形への関心事とシリアライズという関心事が直交してしまっている。 そのため、一方の視点での分割によるかたまりが、他方の視点での分割をまたがってしてしまうのだ。 このような関心事は、横断的関心事 (crosscutting concern) と呼ばれる。

この例では、はじめは図形のクラス設計を行っていて、後からSVG シリアライズやバイナリー シリアライズといった横断的関心事がでてきてしまったのだ。

interface と partial class で横断的関心事を分離

なんとか、それまでのクラスの設計をこわすことなく、この横断的関心事を分離できないものだろうか。

この記事では、C# の interface と partial class を使ったイディオムをご紹介したい。

先のコードのようにいきなりシリアライズが必要なそれぞれのクラスの中に SvgSerialize メソッドと BinarySerialize メソッドを追加するのではなく、先ず partial class とだけしてみる。

MiniCad.cs
using System;
using System.Collections;
using System.Collections.Generic;

abstract partial class Figure
{
    public abstract void Draw();
}

partial class LineFigure : Figure
{
    public override void Draw() => Console.WriteLine("Line!"); // 仮実装
}

partial class EllipseFigure : Figure
{
    public override void Draw() => Console.WriteLine("Ellipse!"); // 仮実装
}

partial class CadModel : IEnumerable<Figure>
{
    public IEnumerator<Figure> GetEnumerator()
    {
        yield return new LineFigure   ();
        yield return new LineFigure   ();
        yield return new EllipseFigure();
        yield return new LineFigure   ();
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

class CadView
{
    public IEnumerable<Figure> DataSource {
        set { value.ForEach(figure => figure.Draw()); }
    }
}

class Program
{
    static void Main()
    {
        var model = new CadModel();
        new CadView().DataSource = model;
        model.SvgSerialize   ();
        model.BinarySerialize();
    }
}

static class EnumerableExtensions
{
    public static void ForEach<TElement>(this IEnumerable<TElement> @this, Action<TElement> action)
    {
        foreach (var element in @this)
            action(element);
    }
}

この Main メソッドでは、SvgSerialize メソッドを BinarySerialize メソッドを使っているが、この時点ではどこにも実装がない。

次に、SvgSerialize メソッドを持つという interface である ISvgSerializable を作り、それを各モデル クラスで実装する。そして、 この実装を partial class の機能を用いて別ファイルで行うことにする。 こんな具合だ。

ISvgSerializable.cs
using System;

interface ISvgSerializable
{
    void SvgSerialize();
}

partial class Figure : ISvgSerializable
{
    public abstract void SvgSerialize();
}

partial class LineFigure
{
    public override void SvgSerialize() => Console.WriteLine("SvgSerialize line!"); // 仮実装
}

partial class EllipseFigure
{
    public override void SvgSerialize() => Console.WriteLine("SvgSerialize ellipse!"); // 仮実装
}

partial class CadModel : ISvgSerializable
{
    public void SvgSerialize() => this.ForEach(figure => figure.SvgSerialize());
}

こうすると、SVG シリアライズという責務を記述するコードがすべてこのファイルに集まることになる。

BinarySerialize メソッドの方も、同様に別ファイルを用意する。 そこで IBinarySerializable という interface を作り、それを各モデル クラスで実装する。

IBinarySerializable.cs
using System;

interface IBinarySerializable
{
    void BinarySerialize();
}

partial class Figure : IBinarySerializable
{
    public abstract void BinarySerialize();
}

partial class LineFigure
{
    public override void BinarySerialize() => Console.WriteLine("BinarySerialize line!"); // 仮実装
}

partial class EllipseFigure
{
    public override void BinarySerialize() => Console.WriteLine("BinarySerialize ellipse!"); // 仮実装
}

partial class CadModel : IBinarySerializable
{
    public void BinarySerialize() => this.ForEach(figure => figure.BinarySerialize());
}

実行結果は変わらない。 クラス図も ISvgSerializable と IBinarySerializable という2つのインタフェイスが加わっただけだ。

インタフェイス追加後の CAD のクラス図の例
インタフェイス追加後の CAD のクラス図の例

これは、オブジェクト指向で横断的関心事を分離したわけではない。 オブジェクト指向にも限界がある。 ここでは、横断的関心事をクラスにマッピングすることではうまく分離できなかった。

そのため、ここでは C# の機能を使い、それまでの関心事に直交した新たな関心事をファイルにマッピングすることで分離した、ということだ。

2017年07月01日

Microsoft MVP を再受賞しました (My 13th MVP Award from Microsoft)

MVP_Logo_Horizontal_Preferred_Cyan300_CMYK_72ppi.png

Microsoft MVP Award を再受賞しました。13年目になります。

My MVP Profile

  • Microsoft MVP for Development Tools - Visual C# (Jul. 2005 - Dec. 2014)
  • Microsoft MVP for .NET (Jan. 2015 - Oct. 2015)
  • Microsoft MVP for Visual Studio and Development Technologies (Nov. 2015 - Jun. 2018)

素晴らしい機会とこの機会によって関われた皆様に感謝です。

2017年07月12日

[C#][.NET] Shos.CsvHelper (CSV を読み書きするためのシンプルなライブラリー)

CSV Icon

csv (Comma-Separated Values または Character-Separated Values) を読み書きするためのシンプルなライブラリーを書いてみた。

csv 形式のファイルは Excel などで表示/編集することができ、便利なので、そこそこ必要になる。 csv を読み書きするライブラリーは既に他にあるが、よりシンプルな使い勝手を目指してみた。

このライブラリーを使うと、プレーンなオブジェクトのコレクション (IEnumerable<Something>) を csv 形式で読み書きすることができる。

使い方

使い方の概要

まず、何か IEnumerable<TElement> なものを用意する:

    // 何か IEnumerable<TElement> なもの
    IEnumerable<ToDo> toDoes = new ToDoList();

例えばこんなクラス:

    class ToDoList : IEnumerable<ToDo> // サンプル コレクション
    {
        public IEnumerator<ToDo> GetEnumerator()
        {
            yield return new ToDo { Id = 1, Title = "filing tax returns", Deadline = new DateTime(2018, 12, 1) };
            yield return new ToDo { Id = 2, Title = "report of a business trip", Detail = "\"ASAP\"", DaySpan = new DaySpan(3), Priority = Priority.High };
            yield return new ToDo { Id = 3, Title = "expense slips", Detail = "book expenses: \"C# 6.0 and the .NET 4.6 Framework\",\"The C# Programming\"", Priority = Priority.Low, Done = true };
            yield return new ToDo { Id = 4, Title = " wish list ", Detail = " \t (1) \"milk\"\n \t (2) shampoo\n \t (3) tissue ", Priority = Priority.High };
        }

        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    }

これを、次のように書きこめる:

    const string csvFileName = "todo.csv";
    await toDoes.WriteCsvAsync(csvFileName);

出来上がった csv ファイルは次のようになる:

  • 値がカンマや区切り文字、改行(<CR>、<LF>)、ダブルクォーテーションを含んでいる場合は、ダブルクォーテーションで囲まれる
  • 値の中のダブルクォーテーションは、ダブルクォーテーション2つに置き換わる
Id,Title,Deadline,Done,Priority,Details,DaySpan
1,filing tax returns,2018/12/01 0:00:00,False,Middle,,0
2,report of a business trip,2017/07/12 13:13:01,False,High,"""ASAP""",3
3,expense slips,2017/07/12 13:13:01,True,Low,"book expenses: ""C# 6.0 and the .NET 4.6 Framework"",""The C# Programming""",0
4, wish list ,2017/07/12 13:13:01,False,High," 	 (1) ""milk""
 	 (2) shampoo
 	 (3) tissue ",0

作られた csv ファイルを Excel で 開いた様子
作られた csv ファイルを Excel で 開いた様子

読みこみは次のような感じ:

    IEnumerable<ToDo> newToDoes = await CsvSerializer.ReadCsvAsync<ToDo>(csvFileName);

読み書きするもの

コレクションの要素の "get" と "set" の両方を持つ public なプロパティが csv ファイルに読み書きされる。

書きこみ時

書きこみ時には、型を問わず、"ToString()" メソッドで文字列に変換される。

読みこみ時

読みこみ時には、文字列型はそのまま、enum (列挙型) も文字列の通りの値として読みこまれる。 それ以外の型のときは、"TryParse" か "Parse" を使って文字列を値に変更しようとする。 このいずれもできない型は読みこまれない。

"get" と "set" の両方を持っていて、かつ、次のいずれかの型であるプロパティが読みこまれる。

  • 文字列型
  • enum
  • デフォルト コンストラクターを持ち "TryParse" か "Parse" ができる型 (int などの基本的な数値型や DateTime、"TryParse" か "Parse" を持つユーザー定義型)
その他のルール
  • [CsvIgnore()] 属性が付けられたプロパティは、読み書きされない
  • [ColumnName("列名")] 属性が付けられたプロパティは、csv ファイルでの列名が指定されたものに変更される

例えば、上記 ToDo クラスはこんな型:

    // 要素の型のサンプル
    // 〇 の付いたプロパティ: 読み書きされる
    // × の付いたプロパティ: 読み書きされない
    class ToDo
    {
        public int      Id       { get; set; }                     // 〇
        public string   Title    { get; set; } = "";               // 〇
        public DateTime Deadline { get; set; } = DateTime.Now;     // 〇
        public bool     Done     { get; set; }                     // 〇
        public Priority Priority { get; set; } = Priority.Middle;  // 〇 ユーザー定義 enum 
        [ColumnName("Details")]
        public string   Detail   { get; set; } = "";               // 〇 [ColumnName("Details")] で csv ファイルでの列名が "Details" に変更される
        public DaySpan  DaySpan  { get; set; }                     // 〇 "Parse" を持つユーザー定義型 ("TryParse" は持たない)
        [CsvIgnore()]
        public string   Option   { get; set; } = "";               // × [CsvIgnore()] が付けられているので無視される
        public string   Version => "1.0";                          // × get しかできないプロパティなので無視される
    }

上記 ToDo クラスで使われているユーザー定義型は次の通り:

    // ユーザー定義の enum の例
    enum Priority { High, Middle, Low }

    // "Parse" を持つユーザー定義型 ("TryParse" は持たない) の例
    struct DaySpan
    {
        public int Value { get; private set; }

        public DaySpan(int value) => Value = value;
        public static DaySpan Parse(string text) => new DaySpan(int.Parse(text));
        public override string ToString() => Value.ToString();
    }

ヘッダーの有無

csv の読み書きは、ヘッダー部分 (例えば、上記 csv ファイルの一行目) がない場合でも可能になっている。

ただし、ヘッダー部分がある場合は、列が入れ替わっていてもヘッダー部を照合することで読みこみ可能だが、ヘッダー部分がない場合は、列を入れ替えると読みこむことができない。

ヘッダー部分無しでの書きこみの例:

    await toDoes.WriteCsvAsync(csvFilePathName: csvFileName, hasHeader: false);

出来上がった csv ファイル:

1,filing tax returns,2018/12/01 0:00:00,False,Middle,,0
2,report of a business trip,2017/07/06 18:08:13,False,High,"""ASAP""",3
3,expense slips,2017/07/06 18:08:13,True,Low,"book expenses: ""C# 6.0 and the .NET 4.6 Framework"",""The C# Programming""",0
4, wish list ,2017/07/12 13:13:01,False,High," 	 (1) ""milk""
 	 (2) shampoo
 	 (3) tissue ",0

作られた csv ファイルを Excel で 開いた様子
作られた csv ファイルを Excel で 開いた様子

こうしたヘッダー部分の無い csv ファイルを読みこむときは次のようにする:

    IEnumerable<ToDo> newToDoes = await CsvSerializer.ReadCsvAsync<ToDo>(csvFilePathName: csvFileName, hasHeader: false);    

その他の指定方法

文字コードは変更可能 (デフォルトは UTF8)。

    CsvSerializer.Encoding = Encoding.GetEncoding(0);

区切り文字も変更可能 (デフォルトは ',')。

    CsvSerializer.Separator = '\t';

ファイル名の代わりに stream も指定できる。

    using (var stream = new FileStream(csvFileName, FileMode.Create))
        await collection.WriteCsvAsync(stream);

leaveOpen を指定することで、読み書きの後に stream を開いたままにすることもできる。

    using (var stream = new FileStream(csvFileName, FileMode.Create))
        await collection.WriteCsvAsync(stream: stream, bufferSize: 1024, leaveOpen:true, hasHeader: true);

非同期メソッド以外に同期メソッドもある。

    toDoes.WriteCsv(csvFileName);

NuGet と GitHub

各ライブラリーは NuGet に公開されているので、Visual Studio からインストールできる。

ソースコードは次の場所で公開:

含まれるプロジェクトは次の通り:

Shos.CsvHelper

  • Csv ライブラリー
  • .NET Standard ライブラリー版
  • .NET Standard 1.3 以降でビルドできる
  • .NET Network 4.6 以降、または .NET Core 1.1 以降対象
  • NuGet パッケージとしてインストールできる: NuGet Gallery | Shos.CsvHelper

Shos.CsvHelper.NetFramework

Shos.CsvHelperSample.NetCore

  • Shos.CsvHelper を利用する .NET Core コンソール アプリケーションのサンプル

Shos.CsvHelperSample.NetFramework

  • Shos.CsvHelper.NetFramework を利用する .NET Framework コンソール アプリケーションのサンプル

2017年10月31日

[Event] 『仙台IT文化祭』に参加してきた (2017/10/28-29)

仙台IT文化祭 2017』に参加してきた。

※ 『C# 大好き MVP による、C# ドキドキ・ライブコーディング!! (出張編)』で登壇。

仙台IT文化祭 2017
日時 2017/10/28(土) - 29日(日)
場所 東北大学川内南キャンパス (C19) 文科系総合講義棟1F&2F (仙台市青葉区川内27-1)
仙台IT文化祭2017 ポスター
仙台IT文化祭2017 ポスター
仙台IT文化祭2017 受付
仙台IT文化祭2017 受付

仙台へ。

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

仙台に到着。

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

会場に到着。 #sendaiitfes

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

昼は学食。安い。 #sendaiitfes

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

#sendaiitfes 2日目に参加中。

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

今日のランチも学食。 #sendaiitfes

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

‪抽選会 #sendaiitfes‬

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

懇親会で食べた牛タン
懇親会で食べた牛タン
懇親会で食べた牛タン
懇親会で食べた牛タン
懇親会で食べたセリ鍋
懇親会で食べたセリ鍋

仙台を出発。

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

小松に着いた。

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

仙台土産にずんだ餅。家族に好評。

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

関連記事

資料
ビデオ
Twitter
ブログ

2017年12月23日

[C#][Joke] Playing C♯

C丼

C# Advent Calendar 2017 の12月23日の記事。

今回は、C# で C♯ を演奏してみた、と言いたいだけの記事。

C♯

プログラミング言語である C# は、通常 "C#" と表記し "c sharp" と読む。

※ "C" を進化させた "C++" を更に進化させたという意味の "C++++" が由来、というのも有名な話だ。 これは、C# の父である Anders Hejlsberg 氏から直に聞いたので間違いない。

C++++

ご存知の方が多いと思うが、"C#" の # は通常「ナンバーサイン」とか「番号記号」とか「井桁」とか呼ばれるもので、英語圏でも "number" とか "pound" とか "hash" などと発音される。 音楽記号である(シャープ)とは別の文字だ。

そして、筆者はよく音楽の演奏をするのだが、C(シー)(シャープ) と書かれていれば、それは嬰ハ長調、または、嬰ハの音 (ハ長調のド) を表していることが多い。

嬰ハ長調
C♯

というわけで (謎)、C# で C♯ を演奏してみたい。

C# で C♯ を鳴らす

C# で音階を演奏するため、2つの方法を試してみる。

C# で C♯ を鳴らす (Beep)

一つ目は、System.Console.Beep(int frequency, int duration) を用いる方法だ。 このメソッドは、音の周波数と長さを渡して演奏するようになっている。

C# で 音名 C♯ の音を鳴らす (Beep)

音は1オクターブ上がると周波数が2倍になる。平均率の場合、音階の中の全部の半音は同じだけ音が上がっていく。 1オクターブは半音12個分だから、半音上がるごとに周波数は「2の1/12乗」倍になる計算だ。 全音上がるごとだと「2の1/6乗」倍。

なので、C# で音名 C♯ の四分音符を演奏すると、例えば次のようになる。

using System;

static class MusicalScale
{
    public static double A4Frequency { get; set; } = 440.0; // ベースとなる音 A4 (デフォルトは440Hz)

    // 平均律 (equal temperament) | Wikipedia
    // https://ja.wikipedia.org/wiki/%E5%B9%B3%E5%9D%87%E5%BE%8B
    // 1オクターブ (6音) で周波数が2倍になり、半音ごとに周波数は「2の1/12乗」倍になる
    public static double EqualTemperamentFrequency(double relativePositionInScale /* ベースとなる音 A4 から何音上か */)
        => A4Frequency * System.Math.Pow(2.0, relativePositionInScale * 2.0 / 12.0);
}

// 音符の長さ
enum Duration
{
    Whole     = 1600       , // 全音符
    Half      = Whole   / 2, // 二分音符
    Quarter   = Half    / 2, // 四分音符
    Eighth    = Quarter / 2, // 八分音符
    Sixteenth = Eighth  / 2, // 十六分音符
}

class Program
{
    const double relativePositionOfCSharp = -4.0; //  C♯(嬰ハ) は A(イ) より4音下

    static void Main()
    {
        PlayCSharpNote();

        Console.ReadKey();
    }

    // 音名  C♯ の音を演奏 (Beep で)
    static void PlayCSharpNote()
        => Beep(frequency: MusicalScale.EqualTemperamentFrequency(relativePositionOfCSharp), duration: Duration.Quarter);

    // 指定された周波数/長さの音を鳴らす
    static void Beep(double frequency, Duration duration)
        => Console.Beep(frequency: (int)System.Math.Round(frequency), duration: (int)duration);
}

実行すると、C♯ が聞こえてくる。

C# で C♯ の音階を平均率で演奏する (Beep)

この要領で、平均律で C♯ の音階 (嬰ハ長調のドレミファソラシド) を演奏してみよう。

using System;
using System.Collections.Generic;

static class EnumerableExtentions
{
    public static void ForEach<T>(this IEnumerable<T> @this, Action<T> action)
    {
        foreach (var element in @this)
            action(element);
    }
}

static class MusicalScale
{
    public static double A4Frequency { get; set; } = 440.0; // ベースとなる音 A4 (デフォルトは440Hz)

    // 平均律 (equal temperament) | Wikipedia
    // https://ja.wikipedia.org/wiki/%E5%B9%B3%E5%9D%87%E5%BE%8B
    // 1オクターブ (6音) で周波数が2倍になり、半音ごとに周波数は「2の1/12乗」倍になる
    public static double EqualTemperamentFrequency(double relativePositionInScale /* ベースとなる音 A4 から何音上か */)
        => A4Frequency * System.Math.Pow(2.0, relativePositionInScale * 2.0 / 12.0);
}

// 音符の長さ
enum Duration
{
    Whole     = 1600       , // 全音符
    Half      = Whole   / 2, // 二分音符
    Quarter   = Half    / 2, // 四分音符
    Eighth    = Quarter / 2, // 八分音符
    Sixteenth = Eighth  / 2, // 十六分音符
}

class Program
{
    const double relativePositionOfCSharp = -4.0; //  C♯(嬰ハ) は A(イ) より4音下

    static void Main()
    {
        //PlayCSharpNote();
        PlayCSharpEqualTemperament();

        Console.ReadKey();
    }

    //// 音名  C♯ の音を演奏 (Beep で)
    //static void PlayCSharpNote()
    //    => Beep(frequency: MusicalScale.EqualTemperamentFrequency(relativePositionOfCSharp), duration: Duration.Quarter);

    //  C♯ の音階を演奏 (平均律を Beep で)
    static void PlayCSharpEqualTemperament()
        => new[] { 0.0, 1.0, 2.0, 2.5, 3.5, 4.5, 5.5, 6.0 } // ドレミファソラシドの各音がドから何音離れているか
            .ForEach(note => Beep(frequency: MusicalScale.EqualTemperamentFrequency(note + relativePositionOfCSharp), duration: Duration.Quarter));

    // 指定された周波数/長さの音を鳴らす
    static void Beep(double frequency, Duration duration)
        => Console.Beep(frequency: (int)System.Math.Round(frequency), duration: (int)duration);
}
C# で C♯ の音階を純正率で演奏する (Beep)

純正率でもやってみよう。

純正率では各音が単純な整数比になるため、和音にしたときに平均律のようにわずかな周波数のずれからくるうなりが生ずることがなく、綺麗にハモれる。

詳細は省略するが、純正率では、ドレミファソラシドの周波数が、それぞれ、1/1、9/8、5/4、4/3、3/2、5/3、15/8、2/1 となる。

これを踏まえて、C# で書いてみる。

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

static class EnumerableExtentions
{
    public static void ForEach<T>(this IEnumerable<T> @this, Action<T> action)
    {
        foreach (var element in @this)
            action(element);
    }
}

static class MusicalScale
{
    public static double A4Frequency { get; set; } = 440.0; // ベースとなる音 A4 (デフォルトは440Hz)

    // 純正律の場合の各音 (ドレミファソラシド) の相対的な周波数の倍率
    readonly static double[] JustIntonationRelativeFrequencyScales = new[] {
            1.0 / 1.0, 9.0 / 8.0, 5.0 / 4.0, 4.0 / 3.0, 3.0 / 2.0, 5.0 / 3.0, 15.0 / 8.0, 2.0 / 1.0
        };

    // 平均律 (equal temperament) | Wikipedia
    // https://ja.wikipedia.org/wiki/%E5%B9%B3%E5%9D%87%E5%BE%8B
    // 1オクターブ (6音) で周波数が2倍になり、半音ごとに周波数は「2の1/12乗」倍になる
    public static double EqualTemperamentFrequency(double relativePositionInScale /* ベースとなる音 A4 から何音上か */)
        => A4Frequency * System.Math.Pow(2.0, relativePositionInScale * 2.0 / 12.0);

    // 純正律 (just intonation) - Wikipedia
    // https://ja.wikipedia.org/wiki/%E7%B4%94%E6%AD%A3%E5%BE%8B
    public static double JustIntonationFrequency(double keynoteRelativePositionInScale, int noteIndex /* ドレミファソラシドの各音のインデックス (0~7) */)
        => EqualTemperamentFrequency(keynoteRelativePositionInScale) * JustIntonationRelativeFrequencyScales[noteIndex];
}

// 音符の長さ
enum Duration
{
    Whole     = 1600       , // 全音符
    Half      = Whole   / 2, // 二分音符
    Quarter   = Half    / 2, // 四分音符
    Eighth    = Quarter / 2, // 八分音符
    Sixteenth = Eighth  / 2, // 十六分音符
}

class Program
{
    const double relativePositionOfCSharp = -4.0; //  C♯(嬰ハ) は A(イ) より4音下

    static void Main()
    {
        //PlayCSharpNote();
        //PlayCSharpEqualTemperament();
        PlayCSharpJustIntonation();

        Console.ReadKey();
    }

    //// 音名  C♯ の音を演奏 (Beep で)
    //static void PlayCSharpNote()
    //    => Beep(frequency: MusicalScale.EqualTemperamentFrequency(relativePositionOfCSharp), duration: Duration.Quarter);

    ////  C♯ の音階を演奏 (平均律を Beep で)
    //static void PlayCSharpEqualTemperament()
    //    => new[] { 0.0, 1.0, 2.0, 2.5, 3.5, 4.5, 5.5, 6.0 } // ドレミファソラシドの各音がドから何音離れているか
    //        .ForEach(note => Beep(frequency: MusicalScale.EqualTemperamentFrequency(note + relativePositionOfCSharp), duration: Duration.Quarter));

    //  C♯ の音階を演奏 (純正率を Beep で)
    static void PlayCSharpJustIntonation()
        => Enumerable.Range(start: 0, count: 8) // ドレミファソラシドの各音のインデックス (0~7)
                     .ForEach(noteIndex => Beep(frequency: MusicalScale.JustIntonationFrequency(relativePositionOfCSharp, noteIndex), duration: Duration.Quarter));

    // 指定された周波数/長さの音を鳴らす
    static void Beep(double frequency, Duration duration)
        => Console.Beep(frequency: (int)System.Math.Round(frequency), duration: (int)duration);
}
C# で C♯ を鳴らす (Microsoft.SmallBasic.Library.Sound.PlayMusic)

もう一つ試そう。 Small Basic の機能を使う方法だ。

そのために、まず Visual Studio から NuGet で "SmallBasicLib" をインストールする。

NuGet で "SmallBasicLib" をインストール
NuGet で "SmallBasicLib" をインストール

このライブラリーの機能の一つに、MML (Music Macro Language) を演奏するというものがある。 Microsoft.SmallBasic.Library.Sound.PlayMusic というメソッドを使うことでテキストで記述された旋律を演奏できる。

これを使って、平均律で C♯ の音階 (ドレミファソラシド) を演奏してみよう。 詳しい MML の記述方法は、下記ソースコードのコメントを見てほしい。

C# で C♯ の音階を平均律で演奏する (Microsoft.SmallBasic.Library.Sound.PlayMusic)
using Microsoft.SmallBasic.Library;
using System;

class Program
{
    static void Main()
    {
        PlayCSharpMusic();

        Console.ReadKey();
    }

    //  C♯ の音階を演奏 (平均率を Small Basic Library で)
    static void PlayCSharpMusic() =>
        //  C♯ (嬰ハ長調) | Wikipedia
        // https://ja.wikipedia.org/wiki/%E5%AC%B0%E3%83%8F%E9%95%B7%E8%AA%BF
        // シャープ7箇所(F, C, G, D, A, E, B)
        //  C♯ D♯ F F♯ G♯ A♯ C  C♯ (嬰ハ 嬰二 ヘ 嬰ヘ 嬰ト 嬰イ ハ 嬰ハ)
        Sound.PlayMusic(notes: "L4 O4 C+ D+ F F+ G+ A+ > C C+");

    // Sound.PlayMusic の notes
    //
    // C, D, E, F, G, A, B: 音名
    // R : 休符
    // C+: Cは音名、音を半音上げる (♯)
    // C-: Cは音名、音を半音下げる (♭)
    // Cn: n は音の長さ (1: 全音符、2: 二分音符、2.: 付点二分音符、4: 四分音符、4.: 付点四分音符、…)
    // Ln: n はデフォルトの音の長さ
    // On: n はオクターブ (デフォルトは4)
    // > : 1オクターブ上に
    // < : 1オクターブ下に
}
C# で C♯ の曲を演奏する (Microsoft.SmallBasic.Library.Sound.PlayMusic)

実は C♯、つまり嬰ハ長調の曲というのはそんなに多くないのだが、例えば、バッハ の「平均律クラヴィーア 第1巻 プレリュード 第3番 嬰ハ長調 BWV 848」や「前奏曲とフーガ 第3番 嬰ハ長調 BWV 872」などがそうだ。

例えば下記で聴くことができる (執筆時点)。

試しに、一部だけ C# で書いてみよう。 C♯ の響きを雰囲気だけでも味わっていただければ幸いだ。

using Microsoft.SmallBasic.Library;
using System;

class Program
{
    static void Main()
    {
        PlayBWV848();
        //PlayCSharpMusic();

        Console.ReadKey();
    }

    // バッハ 平均律クラヴィーア 第1巻 プレリュード 第3番 嬰ハ長調 BWV 848
    // 雰囲気だけ
    static void PlayBWV848() =>
        Sound.PlayMusic("L16 O5 E+ C+ > G+ < C+ E+ C+" +
                        "F+ C+ F+ C+ F+ C+" +
                        "G+ C+ G+ C+ G+ C+" +
                        "A+ C+ A+ C+ A+ C+" +
                        "G+ C+ G+ C+ G+ C+" +
                        "F+ E+ D+ E+ F+ D+" +
                        "E+ D+ C+ D+ E+ C+" +
                        "D+ E+ D+ C+ < B+ A+" +
                        "< B+ G+ D+ G+ B+ G+" +
                        "> C+ < G+ > C+ < G+ > C+ < G+" +
                        "> D+ < G+ > D+ < G+ > D+ < G+" +
                        "> E+ < G+ > E+ < G+ > E+ < G+" +
                        "> D+ < G+ > D+ < G+ > D+ < G+" +
                        "> C+ < B+ A+ B+ > C+ < A+" +
                        "B+ A+ G+ A+ B+ G+" +
                        "A+ B > > F+ E+ D+ E+" +
                        "F+ D+ < A+ > D+ F+ D+" +
                        "G+ D+ G+ D+ G+ D+" +
                        "A+ D+ A+ D+ A+ D+" +
                        "B D+ B D+ B D+" +
                        "A+ D+ A+ D+ A+ D+" +
                        "G+ F+ E+ F+ G+ E+" +
                        "F+ E+ D+ E+ F+ D+" +
                        "E+ F+ E+ D+ C+ < B+");

    ////  C♯ の音階を演奏 (平均率を Small Basic Library で)
    //static void PlayCSharpMusic() =>
    //    //  C♯ (嬰ハ長調) | Wikipedia
    //    // https://ja.wikipedia.org/wiki/%E5%AC%B0%E3%83%8F%E9%95%B7%E8%AA%BF
    //    // シャープ7箇所(F, C, G, D, A, E, B)
    //    //  C♯ D♯ F F♯ G♯ A♯ C  C♯ (嬰ハ 嬰二 ヘ 嬰ヘ 嬰ト 嬰イ ハ 嬰ハ)
    //    Sound.PlayMusic(notes: "L4 O4 C+ D+ F F+ G+ A+ > C C+");

    // Sound.PlayMusic の notes
    //
    // C, D, E, F, G, A, B: 音名
    // R : 休符
    // C+: Cは音名、音を半音上げる (♯)
    // C-: Cは音名、音を半音下げる (♭)
    // Cn: n は音の長さ (1: 全音符、2: 二分音符、2.: 付点二分音符、4: 四分音符、4.: 付点四分音符、…)
    // Ln: n はデフォルトの音の長さ
    // On: n はオクターブ (デフォルトは4)
    // > : 1オクターブ上に
    // < : 1オクターブ下に
}

2018年02月07日

[Event] 「BuriKaigi2018」 (2018年2月3日)を開催しました

BuriKaigi2018 集合写真

富山県氷見市で、毎年恒例の勉強会「BuriKaigi2018」を開催しました。

78名もの方に参加していただきました。 雪が多い季節にも関わらず、今年も遠方からも多くのスピーカーや参加者の方がいらしてくださいました。 本当にありがとうございました。

当日の盛り上がりは Twitter などで見ることができます。

開催概要

BuriKaigi2018
日時 2018/03/02(土) 13:00-18:30 (終了後懇親会)
会場
氷見いきいき元気館 (富山県氷見市)
会場となった氷見いきいき元気館
主催・共催 Hokuriku.NET
北陸エンジニアグループ
スポンサー Forkwell
詳細 BuriKaigi2018 | connpass
スピーカー陣
スピーカー陣
受付付近
受付付近

内容

.NET トラック
.NET側の会場の様子
.NET側の会場の様子
タイトル 概要 スピーカー
「Visual Studio 2017 新機能wrap up」 VS2017が登場しておよそ1年が経過したので、リリース後に追加された新機能をまとめてみましょう。 森 博之 氏 @hiroyuki_mori
Microsoft MVP for Visual Studio and Development Technologies
森 博之 氏
「Introduction to Xamarin.Forms for MacOS - あるいは UWP から Mac App への布石 -」 初めてXamarin.Formsを触ってみたのが2014年3月。
UWP、Android、iOSの他にPreviewでMacOSがあるらしい? ので、ちょっと触ってみたのゆるふわセッション。
蜜葉 優 氏 @mitsuba_yu
Microsoft MVP for Windows Development
蜜葉 優 氏
「ItemsStackPanelの気持ちに寄り添ってパフォーマンスを改善」 UWPのListViewは ItemsStackPanelがよしなに仮想化してくれます。
ところが、場合によってはうまく仮想化してもらえません。
ItemsStackPanelの気持ちの寄り添って、パフォーマンスを改善してみます。
@tmyt
Microsoft MVP for Windows Development
「コントロールベンダー視点での Command Line Interface (CLI)」 ここ数年?の CLIの隆盛?を UIコンポーネントベンターの一員として10年間在籍している中から考えます。 池原 大然 氏 @Neri78
池原 大然 氏
「デスクトップ アプリがこの先生きのこるには 2018」 ここ1年間、著名なデスクトップ アプリがWindowsストアに並ぶなど、UWP以外のWindowsアプリ開発を取り巻く環境が変化しました。
2017年を振り返りつつ、デスクトップ アプリ開発の立ち位置について考えてみます。
@Grabacr07
Microsoft MVP for Windows Development
Microsoft MVP for Visual Studio and Development Technologies
「Windows Serverの新しい管理ツールの紹介」 Windows Serverは、Server Coreで提供されます。
Server Coreは、コンパクトで身軽な反面デスクトップエクスペリエンスが提供されないため手軽なサーバー管理が難しくなります。
オンプレミスの環境であれば、RSATなどを利用できますが、クラウド環境では難しい状況あります。
これらの問題を解決する新しいWebベースのサーバー管理ツール (Honolulu プロジェクト) について紹介します。
澤田 賢也 氏 @masayasawada
Microsoft MVP for Cloud and Datacenter Management
澤田 賢也 氏
「見える化、言える化、やりきれる化! Dynamics365 北陸へ拡散」 吉島 良平 氏 @navisionaxapta
Microsoft MVP for Business Solutions

杉本 和也 氏 @sugimomoto
Microsoft MVP for Business Solutions
吉島 良平 氏と杉本 和也 氏
「C#大好きMVPによるドキドキ ライブ コーディング」 石野 光仁 氏 @ailight
Microsoft MVP for Visual Studio and Development Technologies

鈴木 孝明 氏 @xin9le
Microsoft MVP for Visual Studio and Development Technologies

小島 富治雄 氏 @Fujiwo
Microsoft MVP for Visual Studio and Development Technologies

室星 亮太 氏 @ryotamurohoshi
Microsoft MVP for Visual Studio and Development Technologies
石野 光仁 氏、小島 富治雄 氏、鈴木 孝明 氏、室星 亮太 氏 小島 富治雄 氏、鈴木 孝明 氏、室星 亮太 氏 小島 富治雄 氏、鈴木 孝明 氏、室星 亮太 氏 石野 光仁 氏、小島 富治雄 氏、鈴木 孝明 氏、室星 亮太 氏
Java 他トラック
タイトル 概要 スピーカー
「JavaOne 2017フィードバック - JDKリリースモデル変更とJava EEのEclipse Foundationへの移行」 昨年10月初旬に開催されたJavaOne 2017はJava SE 9とJava EE 8が大きなテーマでしたが、開催に合わせて重要な発表が2つありました。
このセッションでは、その2つの発表 - JDKリリースモデル変更とJava EEのEclipse Foundationへの移行、EE4Jプロジェクトの設立について、その内容をご説明します。
今後Javaの活用がどう変わるのかがわかります。
伊藤 敬 氏@itakash
Oracle
「モジュール移行の課題と対策」 Java SE 9で導入されたProject Jigsawにより、JARファイルの扱いの煩雑さから解放されました。
その一方でモジュールを使用していないアプリケーションであっても、モジュール導入の影響によりビルドできなかったり、動作しなかったりする問題があります。
また、モジュールを導入するにしても、現状ではモジュールに対応したライブラリと非モジュールのライブラリを併用しなくてはなりません。
本セッションではモジュールについて説明し、モジュールを使用する際の課題とそれに対する対応についても解説していきます。
櫻庭 祐一 氏@skrb
「Running Kubernetes on Azure(仮)」 Azure 上で Kubernetes の動かし方 Docker などのコンテナに興味のある方向け。 山本 誠樹 (やまもと まさき) 氏 @nnasaki
Microsoft MVP for Microsoft Azure
「日本語入力の落とし穴」 日本語を受け取るアプリケーションがおちいりがちな落とし穴について紹介する。
「テキスト入力をハンドリングするアプリケーションを作る人」「日本語入力がうまくできないアプリケーションを修正したい人」「独自の日本語入力を作りたい人」などに役に立つと思う。
@mzp
日本語入力の落とし穴 #burikaigi | みずぴー日記
「JetBrainsでもっと楽しくコーディング、ワークフロー」 IDEベンダとして有名なJetBrainsですが、IDEのみならずチームツールもリリースしています。
JetBrainsのIDEはもちろんのこと、10名までは無償で使えるチームツールを組み合わせたワークフローまでご紹介いたします。
山本ゆうすけ氏 @yusukey
株式会社サムライズム
「Excel芸2018」 Scalaは関数型プログラミングもできる、マルチパラダイムなプログラミング言語です。
本発表では、関数プログラミングによって何ができるようになるかを、SIerにとってなじみが深いであろう話題を交えて紹介します。
Excel絶対殺すマン @bleis
「Asciidocの紹介」 文書は気楽に記述でき、バージョン管理しやすいと便利です。
今回はソフトウェア開発者が記述するような文書のために開発されたAsciidocというツールを紹介します。
Javaさえ入っていれば使えるので、Windowsでも気軽に使うことができます。
セッションでは、環境構築から文法、そして便利な周辺ツールなどを紹介します。

Learning Object

Asciidocによる文書管理
Groovyによるハック
@kyon_mm
LT (ライトニングトーク)
タイトル スピーカー
スポンサービデオ Forkwell
スポンサーのForkwell様
リモートデスクトップエンジニアの生態2018 金子雄一 氏 @oskaneko
Microsoft MVP for Enterprise Mobility
金子雄一 氏
Office 365ってな~に?
最新のWindowsでOffice製品を適切に使うために知っていて欲しい3つのこと
さくしま たかえ 氏 @RamuMystery
Microsoft MVP for Windows and Devices for IT
Windows Insider MVP
さくしま たかえ 氏
データプラットフォーム最新情報をざっくり 山本 美穂 氏 @mihochannel
日本マイクロソフト株式会社
山本 美穂 氏
とあるマーケティング部隊とエンジニアとScalaの導入 @grimrose
とあるマーケティング部隊とエンジニアとScalaの導入 | GitHub

懇親会

刺身の船盛
刺身の船盛
ぶりしゃぶ
ぶりしゃぶ

翌日のUFOツアー

翌日の観光ツアーで行ったコスモアイル羽咋
翌日の観光ツアーで行ったコスモアイル羽咋

参加レポート

2018年05月22日

[C#][AI/ML] C# でニューラルネットワークをスクラッチで書いて機械学習の原理を理解しよう | de:code 2018

Microsoft の『de:code 2018 | 開発者をはじめとする IT に携わる全てのエンジニアのためのイベント』(2018/05/22-23, Tokyo) で、C#/AI 関連の発表をしてきた。

資料

私の資料はこちら。

de:code 2018 Online で公開された分

※ de:code の資料の多くは次の場所で見ることができる。

Slideshare
GitHub

詳しくは、GitHub にソースコードまたはチュートリアルで公開してある。

2018年12月03日

マイクロソフトの技術ドキュメントの日本語化活動

Microsoft MVP の有志で、下記のマイクロソフトの技術ドキュメントの日本語化を助ける活動を行っています。

オープンソースですので、日本語としておかしなドキュメントに気付いた場合は、ちょっとずつでも提案していくと良いような気がします。

2019年01月28日

[Event] 「BuriKaigi2019」 (2019年1月26日)を開催しました

BuriKaigi2019 集合写真

富山県黒部市宇奈月温泉で、毎年恒例の勉強会「BuriKaigi2019」を開催しました。

60名の方に参加していただきました。 雪が多い季節にも関わらず、今年も遠方からも多くのスピーカーや参加者の方がいらしてくださいました。 本当にありがとうございました。

当日の盛り上がりは Twitter などで見ることができます。

開催概要

BuriKaigi2019
会場案内の紙
日時 2019/01/26(土) 12:50-18:10 (終了後懇親会)
会場
黒部市宇奈月国際会館セレネ (富山県黒部市宇奈月温泉)
会場となった黒部市宇奈月国際会館セレネ
主催・共催 Hokuriku.NET
北陸エンジニアグループ
ゴールド スポンサー Forkwell
CData Software Japan
詳細 BuriKaigi2019 | connpass

内容

.NET トラック
.NET側の会場の様子
.NET側の会場の様子
タイトル 概要 スピーカー
キーノート「.NET の今 ~ 最新アップデートと 2019 年の展望」 VS2017が登場しておよそ1年が経過したので、リリース後に追加された新機能をまとめてみましょう。 井上 章 氏 井上 章 氏 @chack411
マイクロソフト コーポレーション グローバル ブラックベルト
井上 章 氏
「C# ドキドキ ライブ コーディング」 石野 光仁 氏 @ailight
鈴木 孝明 氏 @xin9le
Microsoft MVP for Developer Technologies

小島 富治雄 氏 小島 富治雄 氏 @Fujiwo
Microsoft MVP for Developer Technologies,

室星 亮太 氏 @ryotamurohoshi
ドキドキライブコーディング2019 #Burikaigi2019 | Speaker Deck
「.NET Core で見る Client Apps」 .NET Core 3.0で追加されるWindows Client Appsについての現状やマイグレーションのためのお役立ち情報を共有したいと思います。 森 博之 氏 森 博之 氏 @hiroyuki_mori
Microsoft MVP for Visual Studio and Development Technologies
森 博之 氏
「見える化、言える化、やりきれる化! Dynamics365 北陸へ拡散 Part 2」 デジタルビジネスの勝者になる為の肝、D365/PowerPlatformを活用したデータ戦略について解説します。 吉島良平(室長) 氏 吉島良平(室長) 氏 @navisionaxapta
Pacific Business Consulting,Inc. 取締役/戦略事業推進室、Microsoft MVP for Business Solutions 2015-2017、Microsoft MVP for Business Applications 2018

杉本 和也 氏 杉本 和也 氏 @sugimomoto
CData Software Japan Lead Engineer、Microsoft MVP for Business Solutions 2017-2019
吉島良平(室長) 氏と杉本 和也 氏
「App Center」 津守 優 氏 @tmyt
「Windows Server 2019」 Windows Server の推奨管理コンソールが Windows Server 2019 より Windows Admin Center へ変更になります。新しい管理コンソールの紹介を中心に Windows Server 2019をカスタマイズしていきます。 澤田 賢也 氏 @masayasawada
Microsoft MVP for Cloud and Datacenter Management
Java 他トラック
タイトル 概要 スピーカー
「Oracle Code One 2018 の概要」 JDKライセンスモデルの変更(最新版) 伊藤 敬 氏 @itakash
Oracle
「Java on Kubernetes 全部デモ」 今 Kubernetes は急速な勢いでエコシステムを拡大しています。マイクロサービス開発や、新機能追加、もしくは変更が多いようなシステムにおいて Kubernetes はその威力を大いに発揮します。本セッションでは Java アプリケーションの開発から Kubernetes へのデプロイまで、具体的にどのような形で開発を進めるていくのかを、デモを通じてご紹介します。 寺田 佳央 氏 Yoshio Terada @yoshioterada
日本 Java ユーザ・グループ / マイクロソフト・コーポレーション
「Project Loom - 軽量スレッドと限定継続」 これまで、Javaのスレッドはプラットフォームのスレッドを使用してきました。これに対し、Project LoomではJVMが管理する軽量スレッドのFiberを導入します。 Fiberを利用することで、今まで実現できなかったスケールのマルチスレッドを実現することが可能です。さらに、現敵的ではあるものの、継続も導入されます。本セッションではFiberや継続の機能を紹介し、それらの実現するための仕組みについても解説します。 櫻庭 祐一 氏@skrb
Project Loom - 軽量スレッドと限定継続 | Speaker Deck
「おれの書いたPHPがこんなに速いわけがない(PHP7.3比3倍)」 TruffleでPHPっぽい文法のスクリプト言語を書いてみたら、なんの努力をしなくても、かなり速くなったと言われてるPHP7.3の3倍速で動く処理系ができてしまいました。という話の詳細と、種明かしをします。 きしだ 氏 @kis
「Microsoft AzureではじめるJava開発」 大中浩行 氏 @setoazusa
Microsoft MVP for Developer Technologies
Microsoft AzureではじめるJava開発 | Slides
スポンサー セッション 「Java クライント実装におけるAPIスタイル頂上決戦! 野良REST vs GraphQL vs OData vs OpenAPI (Swagger)」 皆さん、API使っていますか!? 世界のAPI Management 市場は2022年までに現在の2倍(3,000億円)の市場規模になると予想されていて、今後今まで以上にAPI を使ったマッシュアップは重要性を増すようです! でも、一口にAPIと言っても、単純にRESTという切り口だけでは収まらなくなってきました。そんな多様化され、今後ますます増え続けるAPIのマッシュアップを迅速に行うために、知っておいて損は無いJava開発者のためのAPIスタイル・エコシステムの活用方法をお伝えします! CData Software Japan
杉本 和也 氏 杉本 和也 氏 @sugimomoto
CData Software Japan Lead Engineer、Microsoft MVP for Business Solutions 2017-2019
LT (ライトニングトーク)
LT の様子
LT の様子
タイトル スピーカー
スポンサー ビデオ Forkwell
「オンラインIDEで爆速オンボーディングと、サンプルコード共有」 池原 大然 氏 @Neri78
「2018年ダイジェスト」 金子雄一 氏 金子雄一 氏 @oskaneko
Microsoft MVP for Enterprise Mobility
「プログラミング教育必修化(仮)」 ToshiyukiKashio 氏
プログラミング教育必修化 | Speaker Deck
「公共交通でのオープンデータ活用(仮)」 TOMINARI_Takayuki 氏
「micro:bit さわってみた。」 蜜葉 優 氏 @mitsuba_yu
「Friendly と TestAssitantPro を使った Windows アプリテスト自動化」 石川 達也 氏 石川 達也 氏 @StoneGuitar777
Microsoft MVP for C#
「消費税軽減税率制度」 Hajime Mugishima 氏 @mugi_uno
消費税軽減税率制度 | Speaker Deck
某500万リツイートのお話。ツイッターAPI の話などなど。 杉本 和也 氏 杉本 和也 氏 @sugimomoto
CData Software Japan Lead Engineer、Microsoft MVP for Business Solutions 2017-2019

懇親会

歓迎 BURIKAIGI2019 御一行様
ぶりしゃぶ
ぶりしゃぶ

参加レポート

2019年01月31日

[C#] 目次 - C# 関連

C#

■ [C#][ラムダ式][LINQ][Expression][式木] 関連記事一覧

■ [C#][dynamic] 関連記事一覧

■ [C#][Design Pattern] C# による Observer パターンの実装 記事一覧

■ [C#][メタプログラミング][Reflection][Expression][式木][Roslyn][dynamic] 関連記事一覧

その他 C#関連

2019年03月23日

[Event] 『2019 Microsoft MVP Global Summit』に参加してきた (2019/03/17-21)

「『2019 Microsoft MVP Global Summit』に参加してきた | 出発前~ポートランド編 (2019/03/15-17)」の続き。

2019 MVP Global Summit』に参加してきた。

13回目の参加となる。

マイクロソフト(Microsoft) には、MVP (Microsoft Most Valuable Professional) というアワードがあるが、約1年に一度、世界中の MVP が米国ワシントン州レドモンド(Redmond, Washington)にあるマイクロソフト本社およびその近くに招待され、大規模な技術カンファレンスが行われる。

毎年2000名規模の MVP および Regional Directors が世界中から参加する。今回日本からは100名余りが参加した。

たくさんのセッションでマイクロソフトのプロダクト チームの方々から直に話を聴くことができた。 また、マイクロソフトの方や世界中の MVP と交流を持つことができた。 貴重な機会をいただいて、本当に感謝している。

4. ベルビュー(Bellevue) およびレドモンド (ワシントン州シアトル(Seattle, Washington)近郊)

ベルビューおよびレドモンド 一日目

シアトル・タコマ国際空港(Seattle-Tacoma International Airport) に到着。

View this post on Instagram

到着。

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

空港から滞在先の Hyatt Regency Bellevue に移動。

View this post on Instagram

#mvpsummit

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

View this post on Instagram

#mvpsummit

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

View this post on Instagram

ホテルに到着するもまだチェックイン出来ず。

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

Hyatt Regency Bellevue
Hyatt Regency Bellevue

Summit のレジストレーションを済ませる。

View this post on Instagram

Registration 完了。 #mvpsummit

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

バッジ
バッジ

近くで買い物。

ベルビュー スクエア(Bellevue Square) にある Microsoft StoreSurface Go 用のアクセサリーを購入した。

View this post on Instagram

Surface Go 用のアクセサリーを購入。

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

ホテル近くのスーパー マーケット「QFC (Quality Food Centers)」へ。

View this post on Instagram

QFC でいつものセットを買ってきた。

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

夜はアジアの MVP の皆さんとのパーティなど。

ベルビューおよびレドモンド 2日目

ホテルで朝食後、マイクロソフトが用意してくれたバスでマイクロソフト キャンパスへ。

View this post on Instagram

朝食。

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

マイクロソフト本社の広大な敷地内にあるカンファレンス センター(Conference Center)などで、たくさんの本格的な技術セッションが3日間に渡り、英語で行われる。 セッション内容は秘密保持契約(NDA: Non-Disclosure Agreement)の関係で公開できないため、以下は差しさわりのない食事の風景などに限られる。 技術的な内容に一切触れないので、ただ飲み食いして遊んでいるだけのように見えるかも知れないが、けしてそうではないのでご安心願いたい。

View this post on Instagram

#mvpsummit

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

会場内では、自由にコーヒーや炭酸飲料、軽食などをとることができる。

View this post on Instagram

#mvpsummit

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

軽食
軽食 軽食 軽食

ランチ。

View this post on Instagram

Lunch time.

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

マイクロソフト キャンパス内にある Company Store や Microsoft Visitor Center を訪れた。 ここには、マイクロソフトの最初のソフトウェアのソースコードや最初のハードウェアなども見ることができる。

View this post on Instagram

Microsoft 1st software.

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

View this post on Instagram

Microsoft’s earliest hardware products.

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

夜はパーティ。

ベルビューおよびレドモンド 3日目

この日もホテルで朝食後、マイクロソフト キャンパスへ。

View this post on Instagram

朝食。

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

今回は C# のプログラムマネージャの Mads Torgersen 氏と写真を撮らせてもらった。 彼は今回で14回目のサミットだと言っていた。

キャンパスでのランチは、毎年どんどん美味しくなっている。

View this post on Instagram

Lunch.

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

会場の近くの Microsoft Treehouse も見てきた。

View this post on Instagram

Microsoft Treehouse. #mvpsummit

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

View this post on Instagram

Microsoft Treehouse. #mvpsummit

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

夜はパーティの後、近所のレストランで夕食。

ベルビューおよびレドモンド 4日目

この日もホテルで朝食後、マイクロソフト キャンパスへ。 米国に来てだいぶ経つのに、時差ボケが残っている。

最後の夜もパーティ。 ほとんどの参加者が集まる最大のパーティーだった。

パーティ
パーティ

集合写真
集合写真
関連記事

※ 続きは、「『2019 Microsoft MVP Global Summit』に参加してきた | シアトル~帰国編 (2019/03/21-23)」

[Event] 『2019 Microsoft MVP Global Summit』に参加してきた | シアトル~帰国編 (2019/03/21-23)

「『2019 Microsoft MVP Global Summit』に参加してきた (2019/03/17-21)」の続き。

2019 MVP Global Summit』の後、ワシントン州シアトル(Seattle, Washington) ダウンタウンに移動して一泊したので、その様子を記しておきたい。

5. シアトル

MVP の尾崎さんと Uber でシアトルへ移動し、ホテルにチェックイン。

先ずは、定番の Pike Place Market へ。 現存するマーケットでは全米で最も歴史が長く、シアトルで一番の人気観光スポット。

View this post on Instagram

スターバックス一号店。

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

次に、シーフードが美味しい Ivar’s に向かった。 ここのクラムチャウダーは毎年外せない。とても美味しい。

View this post on Instagram

Ivar’s Clam Chowder.

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

T-Mobile Park (旧 Safeco Field) の壁には、この日引退したイチローの姿が。

T-Mobile Park の壁
T-Mobile Park の壁

市内では、あちこちで Lime の自転車や車を見かけた。 これは、スマートフォン アプリで借りられて、好きなところに乗り捨てできるサービスだ。

次に、Living Computers: Museum + Labs に行った。 ここは初めて訪れたが、歴史的な古いコンピューターが沢山置いてある。 Xerox の AltoApple ILisa などが、実際に動いていて、触ることもできるのが感動的だった。

View this post on Instagram

apple I

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

View this post on Instagram

Alto.

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

View this post on Instagram

NeXT.

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

View this post on Instagram

Cray-1.

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

ディジタル・イクイップメント・コーポレーション(Digital Equipment Corporation (DEC)) のメインフレームなども沢山動いていた。

メインフレーム
メインフレーム

Uwajimaya へ。 ここには、日本の食材が大量にある。

View this post on Instagram

Rice.

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

View this post on Instagram

Soy sauce.

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

ここには、紀伊国屋書店もある。

紀伊国屋書店
紀伊国屋書店

最後の晩の食事は、MVP の尾崎さん、亀川さん、杉本さんと Metropolitan Grill でステーキ。 今まで食べたステーキの中で一番だった。

View this post on Instagram

ここのステーキは、素晴らしかった。

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

6. 帰国

翌朝、シアトル・タコマ国際空港(Seattle-Tacoma International Airport)へ、シアトルの公共交通機関である SoundTransitLink light rail で移動。

空港の Ivar’s がなくなってしまったので、別のシーフード店で朝食。

View this post on Instagram

Salmon ‘n chips.

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

機内では、映画を観て過ごした。

View this post on Instagram

飛行機の中で観た映画。

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

View this post on Instagram

北陸へ。

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

[Event] 『2019 Microsoft MVP Global Summit』に参加してきた | 出発前~ポートランド編 (2019/03/15-17)

2019 MVP Global Summit』に参加する前に、オレゴン州ポートランド(Portland, Oregon)に行ってきた。

ポートランドには 2016年 MVP Global Summit のときから毎年訪れていて、今回で3回目。とても素敵な街だ。

アメリカ合衆国オレゴン州(Oregon)最大の都市で、全米一住みやすい都市と言われている。 シアトルに比べると都市部がこじんまりしている。

次のような特徴がある。

  • 緑にあふれた町並み
  • 住民がフレンドリー
  • 消費税0% (オレゴン州)
  • 飲み物 (珈琲、ビール、ワイン) が美味しい
  • オーガニック フードで有名

出国前からポートランド滞在までを記しておきたい。

1. 出発前

米国でネットに接続する方法は、フリーWiFi、海外Wi-Fiレンタル、国際ローミングなど、いくつかの方法があるが、今回は、MOST SIMT-Mobile 版 を日本の Amazon で購入し、iPhone 6s で使用した。 12日間使い放題で3千円台だった。

日本にいる間に業者にアクティベーションしてもらい、行きの機内で入れ替えた。 SIMカードを取り出すためのピンも付属していた。

特に設定もプロファイルの削除や設定も必要なく、そのまま快適に LTE で接続することができ、とても快適だった。 電話番号や SMS も使え、テザリングも問題なかったのでお薦めだ。

MOST SIM
MOST SIM

PC は、Surface Go を渡米前日にセットアップし、持って行った。 小さくて軽くてとても快適だった。

View this post on Instagram

おや? 何か届いたようだ。

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

View this post on Instagram

セットアップ完了。 明日の渡米に間に合った。

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

2. 渡米

出発。

View this post on Instagram

ポートランドへ向け出発。

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

View this post on Instagram

1週間日本を離れるので。

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

View this post on Instagram

今回は Delta。

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

3. ポートランド

ポートランド 1日目
View this post on Instagram

ポートランドに到着。

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

ポートランドは TrimetPortland Streetcar などの公共交通機関がとても便利で、縦横無尽に走った路面電車やバスなどが一日5ドルで乗り放題になっている (2時間なら2.5ドル)。 今回は、日本の Suica などにあたる hop カードを利用した。

ホテルにチェックインした後、「日本国外にある中でも最も美しく本格的な日本庭園」と言われている Portland Japanese Garden へ行った。

Dossier Hotel
Dossier Hotel
View this post on Instagram

ポートランドの徽軫灯籠 (ことじとうろう)。

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

その後は、市内を見て回ったり、スーパーで飲食物を買ったり、夕食に行ったりした。

POWELL'S BOOKS
POWELL'S BOOKS
ポートランド名物のフードカート
ポートランド名物のフードカート

世界のベストビール都市第1位に輝いたポートランドの市内には70以上のブルワリーがあって、その数も世界一だ。

DESCHUTES BREWERY
DESCHUTES BREWERY
DESCHUTES BREWERY
DESCHUTES BREWERY
Rogue Pearl Public House
DESCHUTES BREWERY
View this post on Instagram

クラフトビール王国ポートランドの地ビール。

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

ポートランド 2日目

翌朝、毎週土曜日に開催される ポートランド州立大学(Portland State University)Formers Market へ。 市内あちこちで開かれる Formers Market だが、ここのが一番大規模だ。 様々な地元のオーガニック フードなどが楽しめる。

Formers Market
Formers Market
View this post on Instagram

Portland Farmers Market.

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

View this post on Instagram

Portland State University.

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

次に Pioneer Place へ。 ここには、Microsoft StoreApple Store もある。

View this post on Instagram

Microsoft Store.

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

市内を散策。

View this post on Instagram

晴天。

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

夕方は、ブルワリーとディスティラリーの見学ツアーに参加。 自分以外はカナダ人4人だった。

View this post on Instagram

WESTWARD WHISKEY の試飲。

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

その後は、次の日から一緒に 2019 MVP Global Summit に参加する MVP の杉本さんと一緒に食事をした。

Rock Bottom
Rock Bottom
View this post on Instagram

杉本和也さんと Rock Bottom Brewery で合流。

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

ポートランド 3日目

シアトル(Seattle)行きに乗るべくポートランド国際空港(PDX)へ移動。

View this post on Instagram

さてシアトルへ。

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

空港でも DESCHUTES BREWERY のビールを楽しむことができる。

View this post on Instagram

DESCHUTES BREWERY.

Fujio Kojimaさん(@fujiwo)がシェアした投稿 -

※ 続きは、「『2019 Microsoft MVP Global Summit』に参加してきた (2019/03/17-21)」

2019年04月15日

[Event] C# ドキドキ・ライブコーディング対決 @ de:code

MW51 | C# ドキドキ・ライブコーディング対決 @ de:code

毎年恒例の BuriKaigi などで4人でやっている C# ライブ コーディングセッションを Microsoft の de:code 2019 でやります。

2019年05月07日

Microsoft Build 2019 リンク集 (2019/05/07)

全般

一日目

全般

Azure

Office 365

.NET

ML

Visual Studio

Windows Terminal

Windows の Linux

その他

2019年06月11日

[Event] de:code 2019 で C# ドキドキ・ライブコーディング対決をやってきました

MW51 | C# ドキドキ・ライブコーディング対決 @ de:code

毎年恒例の BuriKaigi などで4人でやっている C# ライブ コーディングセッションを Microsoft の de:code 2019 でやってきました。

いつもお世話になっているマイクロソフトの井上章 (@chack411) さんのお誘いで実現したものです。 おかげさまでとても素敵な体験になりました。 もちろん、毎回楽しい企画をしてくださる石野 (@AILight) さん、一緒に解答者として登壇した鈴木 (@xin9le) さん、室星 (@RyotaMurohoshi) さん、にも感謝です。

今回は、Blazor を使用したプログラミングがお題で、次の2つをやりました:

  1. C# ライブ コーディング: その場で出たお題「C# で令和表示」を C#/Blazor で
  2. 七並べプログラム対決: 予め用意した「七並べのAI」を当日マージして対戦

「七並べ」対決の私の作戦は次の通りです:

99名の皆様が聴きにきてくださり、大変に盛り上がってくださいました。 本当にありがとうございました!

「MW51 | C# ドキドキ・ライブコーディング対決 @ de:code」 関連記事

当日の盛り上がりの様子の一部は、以下のリンクで読むことができます。

de:code 2019 関連記事

de:code 2019 の動画や資料などはこちらから。 (ただし、我々のセッションのものはありません)

NSAT

アンケート結果の NSAT (Net Satisfaction) がとても良かったので、メモ。

NSAT
講師198
コンテンツ193
全体195

参考: NSAT の求め方
  • Positive の比率 (パーセンテージ) = P
    • 4段階評価、5段階評価の場合: 1番上の評価をした人の比率
    • 10段階評価の場合: 1番上と上から2番目の評価の比率の合計
  • Negative の比率 (パーセンテージ) = N
    • 4段階評価の場合: 下2つの合計
    • 5段階評価の場合: 下3つの合計
    • 10段階評価の場合: 下4つの合計
  • NSAT = P - N + 100 (200点満点、平均100点)

2019年07月02日

Microsoft MVP を再受賞しました (My 15th MVP Award from Microsoft)

MVP_Logo_Horizontal_Preferred_Cyan300_CMYK_72ppi.png

Microsoft MVP Award を再受賞しました。15年目になります。

My MVP Profile

  • Microsoft MVP for Development Tools - Visual C# (Jul. 2005 - Dec. 2014)
  • Microsoft MVP for .NET (Jan. 2015 - Oct. 2015)
  • Microsoft MVP for Visual Studio and Development Technologies (Nov. 2015 - Jun. 2018)
  • Microsoft MVP for Developer Technologies (Jul. 2018 - Jun. 2020)

いい歳の大人をこうして褒めてくれるマイクロソフトに感謝です。

Microsoft MVP となって、多くの素敵なITエンジニアの皆様との交流の機会が増えました。 皆様いつもありがとうございます。

2019年09月24日

.NET Core 3.0 正式版リリース

.NET Core 3.0 正式版リリース

昨日 (2019/09/23)、.NET Core 3.0 正式版がリリースされた。

現在これに関連して、.NET Conf 2019 というオンライン カンファレンスが開催されている。
一部は、YouTube で見ることができる。

.NET Core 3.0

Microsoft 製の新たな .NET である .NET Core だが、.NET Core 3.0 では、Windows デスクトップ アプリケーション (Windows Forms と WPF) がサポートされた。

.NET Core 3.0 のためのソフトウェアの開発には、現在 Visual Studio 2019 の最新版である Ver.16.3.0 が必要だ。
Visual Studio Installer からアップデートできる。

但し、現在 .NET Core の Windows Forms のデザイナーには未対応であるようだ。

(なお .NET Core は、最初の .NET である .NET Framework に比べて様々な最適化がなされているため、より高パフォーマンスとなっている)

.NET Core の Roadmap

  • 2019/11 .NET Core 3.1 (LTS : Long Term Support 版)
  • 2020/11 .NET 5

.NET 5 は、.NET Core 3 の次期バージョンだ。

現在 3つある Microsoft の .NET (.NET Framework, .NET Core, Xamarin) が 1 つに統合される予定。
これ以降、.NET Framework は保守のみとなり、新機能は追加されなくなる。

.NET Core 上の C++/CLI

C++/CLI は .NET Core 3.1 でサポートされる予定だ。

Blazor

新たな ASP.NET (.NET での Web アプリケーション プラットフォーム) のフレームワークである Blazor だが、サーバーサイドの Blazor は、既に .NET Core 3.0 でサポートされている。

サーバーサイドの Blazor は、C# と Razor 構文でコーディングでき、クライアント サイドとの同期はフレームワークによって行われる (内部的には JavaScript を用いて SignalR で通信) という技術だ。

クライアント サイド Blazor (Blazor WebAssembly) は、WebAssembly で .NET が動作し、C# のアプリケーションがクライアント サイドの Web ブラウザ上で動作するというユニークなものだが、こちらは、現在まだ Preview 版。
2020年5月のリリースが予定されている。

C#

C#8.0 がリリースされた。
null 許容参照型や非同期ストリームなどがサポートされる。
非同期ストリームでは、非同期型の IEnumerable である IAsyncEnumerable が使われる。

関連記事

最新の C#/.NET を使用するには (2019/09/25版)

※ 「.NET Core 3.0 正式版リリース」の続き。

先日 .NET Core 3.0 が正式にリリースされ、C# 8.0 が使えるようになった。
一方で、.NET Framework の最新版は 4.8 だ (2019/09/25 現在)。

現時点での、最新版の .NET を使用する方法をまとめてみよう。

.NET Framework 4.8 を使うには

下記からインストール。
※ Developer pack と Language pack の両方

.NET Core 3 で開発するには

Visual Studio Installer で Visual Studio 2019 Ver.16.3 以上にバージョンアップ

Blazor WebAssembly (クライアントサイド Blazor: プレビュー版) を試すには

最新版の Visual Studio に Blazor テンプレートのインストールが必要
次を参照

C# 8.0 で開発するには

プロジェクト ファイル (*.csproj など) に <LangVersion>8.0</LangVersion> を記述
(null 許容参照型も有効にするには <Nullable>enable</Nullable> も記述)

例.

  <?xml version="1.0" encoding="utf-8"?>
  <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      ...
      <PropertyGroup>
        ...
        <LangVersion>8.0</LangVersion>
        <Nullable>enable</Nullable>
        ...
    
 

※ .NET Core 3.0 アプリケーションのデフォルトの C# のバージョンは 8.0

参考資料

2019年10月16日

[C#][.NET] Shos.CsvHelper (simple library for reading and writing CSV)

CSV Icon

I wrote a simple library for reading and writing CSV (the Values Comma-Separated or Character-Separated Values).

The csv format file is sometimes necessary because it can be displayed / edited with Excel and is simple. There are other libraries that read and write csv already, but I tried to make it simpler.

With this library, you can read and write plain object collections (IEnumerable<Something>) in csv format.

How to use

Overview of how to use

First, prepare something IEnumerable<TElement>:

    An IEnumerable<TElement> something
    IEnumerable<ToDo> toDoes = new ToDoList();

For example this class:

    class ToDoList : IEnumerable<ToDo> // sample collection
    {
        public IEnumerator<ToDo> GetEnumerator()
        {
            yield return new ToDo { Id = 1, Title = "filing tax returns", Deadline = new DateTime(2018, 12, 1) };
            yield return new ToDo { Id = 2, Title = "report of a business trip", Detail = "\"ASAP\"", DaySpan = new DaySpan(3), Priority = Priority.High };
            yield return new ToDo { Id = 3, Title = "expense slips", Detail = "book expenses: \"C# 6.0 and the .NET 4.6 Framework\",\"The C# Programming\"", Priority = Priority.Low, Done = true };
            yield return new ToDo { Id = 4, Title = " wish list ", Detail = " \t (1) \"milk\"\n \t (2) shampoo\n \t (3) tissue ", Priority = Priority.High };
        }

        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    }

You can write this as:

    const string csvFileName = "todo.csv";
    await toDoes.WriteCsvAsync(csvFileName);

The resulting csv file looks like this:

  • If the value contains commas, delimiters, newlines, or double quotes, these are enclosed in double quotes.
  • Double quotations in the value are replaced by two double quotations.
Id,Title,Deadline,Done,Priority,Details,DaySpan
1,filing tax returns,2018/12/01 0:00:00,False,Middle,,0
2,report of a business trip,2017/07/12 13:13:01,False,High,"""ASAP""",3
3,expense slips,2017/07/12 13:13:01,True,Low,"book expenses: ""C# 6.0 and the .NET 4.6 Framework"",""The C# Programming""",0
4, wish list ,2017/07/12 13:13:01,False,High," 	 (1) ""milk""
 	 (2) shampoo
 	 (3) tissue ",0

Open the created csv file in Excel
Open the created csv file in Excel

You also can read a csv file like this:

    IEnumerable<ToDo> newToDoes = await CsvSerializer.ReadCsvAsync<ToDo>(csvFileName);

Things to read and write

Public properties with both "get" and "set" of each element in the collection are read and written to csv files.

When writing:

When writing, it is converted to a string with the "ToString()" method regardless of the type.

When reading:

When reading, the string type is left as is, and enum (enumeration type) is read as the value of the string. For other types, it tries to change the string to a value using "TryParse" or "Parse". Types that cannot do either of these will not be read.

Properties that have both "get" and "set" and are of one of the following types are read:

  • String type
  • enum
  • A type that has a default constructor and can be "TryParse" or "Parse" (Basic numeric types such as int, DateTime, user-defined types with "TryParse" or "Parse")
Other rules
  • Properties with the [CsvIgnore ()] attribute are not read or written.
  • Properties with the [ColumnName("Column name")] attribute will be changed to the one with the specified column name in the csv file.

For example, the above ToDo class is like this:

    // Sample element type
    // Properties marked with ✔ will be read / written
    // Properties marked with ✖ won't be read / written
    class ToDo
    {
        public int      Id       { get; set; }                     // ✔
        public string   Title    { get; set; } = "";               // ✔
        public DateTime Deadline { get; set; } = DateTime.Now;     // ✔
        public bool     Done     { get; set; }                     // ✔
        public Priority Priority { get; set; } = Priority.Middle;  // ✔ user-defined enum 
        [ColumnName("Details")]
        public string   Detail   { get; set; } = "";               // ✔ [ColumnName ("Details")] changes the column name in the csv file to "Details"
        public DaySpan  DaySpan  { get; set; }                     // ✔ User-defined type with "Parse" (without "TryParse")
        [CsvIgnore()]
        public string   Option   { get; set; } = "";               // ✖ Ignored because [CsvIgnore()] is attached
        public string   Version => "1.0";                          // ✖ Ignored because it is only a get property
    }

The user-defined types used in the above ToDo class are as follows:

    // User-defined enum example
    enum Priority { High, Middle, Low }

    // Example of a user-defined type with "Parse" (without "TryParse")
    struct DaySpan
    {
        public int Value { get; private set; }

        public DaySpan(int value) => Value = value;
        public static DaySpan Parse(string text) => new DaySpan(int.Parse(text));
        public override string ToString() => Value.ToString();
    }

With or without header

csv can be read and written even if it has no header part (eg the first line of the csv file above).

However, if there is a header part, it can be read by collating the header part even if the column is switched, but if there is no header part, it cannot be read if the column is switched.

Example of writing without a header:

    await toDoes.WriteCsvAsync(csvFilePathName: csvFileName, hasHeader: false);

Csv file created:

1,filing tax returns,2018/12/01 0:00:00,False,Middle,,0
2,report of a business trip,2017/07/06 18:08:13,False,High,"""ASAP""",3
3,expense slips,2017/07/06 18:08:13,True,Low,"book expenses: ""C# 6.0 and the .NET 4.6 Framework"",""The C# Programming""",0
4, wish list ,2017/07/12 13:13:01,False,High," 	 (1) ""milk""
 	 (2) shampoo
 	 (3) tissue ",0

Open the created csv file in Excel
Open the created csv file in Excel

To read a csv file without a header:

    IEnumerable<ToDo> newToDoes = await CsvSerializer.ReadCsvAsync<ToDo>(csvFilePathName: csvFileName, hasHeader: false);    

Other specification methods

The character code can be changed (default is UTF8).

    CsvSerializer.Encoding = Encoding.GetEncoding(0);

The delimiter can also be changed (default is ',').

    CsvSerializer.Separator = '\t';

You can also use stream instead of file name.

    using (var stream = new FileStream(csvFileName, FileMode.Create))
        await collection.WriteCsvAsync(stream);

You can leave the stream open after reading or writing by specifying leaveOpen.

    using (var stream = new FileStream(csvFileName, FileMode.Create))
        await collection.WriteCsvAsync(stream: stream, bufferSize: 1024, leaveOpen:true, hasHeader: true);

In addition to asynchronous methods, there are also synchronous methods.

    toDoes.WriteCsv(csvFileName);

NuGet and GitHub

These libraries are open to NuGet and can be installed from Visual Studio.

Source code is available at:

The projects included are:

Shos.CsvHelper

  • CSV library
  • .NET Standard Library version
  • You can build a.NET Standard 1.3 and later
  • .NET Network 4.6 or higher, or.NET Core 1.1 or later for
  • NuGet packages that can be installed: NuGet Gallery | Shos.CsvHelper

Shos.CsvHelper.NetFramework

Shos.CsvHelperSample.NetCore

  • .NET Core Console applications that use the Shos.CsvHelper sample

Shos.CsvHelperSample.NetFramework

  • Using Shos.CsvHelper.NetFramework.NET Framework console application sample

2020年01月16日

新たな Web アプリケーションフレームワーク「Blazor」 ‐ ネイティブ アプリケーション開発も可能に

日本時間で昨日、「.NET Conf: Focus on Blazor」というオンライン カンファレンスが開催され、Blazor に関する新たな発表がなされた。
現在、セッション動画を観ることができる。

Blazor は、C#/.NET で Web アプリケーションを開発できるプラットフォームとして、.NET Core 3 で正式リリースされた。
従来の ASP.NET ではサーバーサイドを C#/.NET、クライアントサイドを JavaScript (や TypeScript) で記述していたが、Blazor ではサーバーサイドとクライアントサイドを統一した言語 (C#) で記述できるのが大きなメリットだ。これにより、単一のコードでモデルが記述できるようになる。

今回、Blazor に関して、以下のように多くの新技術が発表になった:

  • Blazor Server (以前発表済み)
    • .NET Core 3 で正式版
    •  Web アプリケーション (SPA: Single Page Application) 用
    • サーバーサイドでC#/.NETのコードが動作
    • クライアントサイドも JavaScript でなく C# で記述
  •  Blazor WebAssembly
    • 2020年5月に正式版の予定
    • Web アプリケーション (SPA: Single Page Application) 用
    • クライアントサイドでC#/.NETのコードが動作
    • クライアントサイドも JavaScript でなく C# で記述
  • Blazor PWA
    • 正式版は未定
    • PWA (Progressive Web Apps: デスクトップ アプリケーションのように動作するWeb アプリケーション) 用
  • Blazor Hybrid
    • 正式版は未定
    • Electron や WebView を使った HTML ベースのデスクトップ アプリケーション用
  • Blazor Native
    • 正式版は未定
    • HTML を使わないデスクトップ アプリケーション用
  • Mobile Blazor Bindings
    • 正式版は未定
    • Blazor のコードから iOS や Android の機能を呼び出せるようにする仕組み
    • これにより Blazor で iOS や Android のネイティブ アプリケーションを開発できる

参考資料

2020年02月02日

[Event] BuriKaigi 2020 が開催されました

毎年冬に富山で開催されている BuriKaigi (*)。
今回も、90名もの方にご参加いただき、大いに盛り上がりました。

昨年までは2トラックでしたが、今回は3トラックになりました (ルームA, B, C)。
リンクなどをまとめておきます。

(*) BuriKaigi とは何かということは次に詳しく書かれています: Burikaigi という毎年冬のイベント | hikaruworld | Medium

開催概要

BuriKaigi 2020
日時2020年2月1日(土) 13:00〜19:00 (終了後懇親会)
会場富山県民会館 (懇親会会場: 割烹 扇)

イベントの様子

資料

ルームA
ルームB
ルームC
LT

写真・ビデオ

ブログ

2020年02月14日

[C#] C#の新機能勉強会 ~C#7、8の新機能を活用して速く安全なプログラムを書こう~

「C#の新機能勉強会 ~C#7、8の新機能を活用して速く安全なプログラムを書こう~」の資料をアップした。

C#7、8 では、struct (値型) 関連の便利な機能追加が多いので、その辺りに注力してみた。

資料

参考資料

2020年07月01日

[Event] Microsoft Build 2020 (2020/05/19-20)

マイクロソフトの最新の技術について、以下のようなオンラインイベントで情報が提供されました。

これらのイベントで公開された/公開予定の技術についてご紹介いたします。

■ .NET 関連

● One .NET (ひとつの .NET)
○ 現在の .NET
○ 次の .NET

以前から発表されている通り、次の .NET では次のようになります。

"One .NET" ということで、.NET が現在の .NET Core に統合され、.NET 5 となります。

※ .NET 5 に含まれないもの

  • WCF (Windows Communication Foundation)
  • WF (Workflow Foundation)
  • ASP.NET WebForms

ちなみに .NET Framework は 4.8 が最終バージョンです。今後は保守フェーズに入り、機能の新規追加はされなくなります。

.NET Framework のサポート自体は Windows 10 がサポートされている間は続くと思われます。

将来性から考えると、新規に .NET で開発する場合は、.NET Core を用いるべきだと考えられます。

○ .NET のロードマップ

上記のとおり、現在ある .NET Framework、.NET Core、Xamarin は一つの .NET に統合されます。

但し、2020 年 11 月の .NET 5 リリース時点では Xamarin の統合はプレビュー扱いです。

.NET 6 で完全に統合される見込みです。

.NET 6 は LTS (Long term support: 長期サポート) 版となる予定なので、新規開発に利用するのは、.NET 6 からが無難かも知れません。

● 新たなマルチプラットフォーム アプリケーション開発方法

Microsoft は既に Windows への囲い込みを行っていませんので、新たな開発方法としては Windows 以外の OS もサポートするマルチ プラットフォームなものを提供していくことになります。

注目すべきは、以下の二つです。

  • MAUI (Multi-platform App UI)
  • Blazor

将来的にはどちらもネイティブアプリケーションも開発できるものですが、MAUI は従来の Windows アプリケーション開発技術の延長にある技術、Blazor は Web アプリケーション開発技術の延長にあり、とりあえずは Web 開発用です。

ひとつずつ説明します。

○ MAUI (Multi-platform App UI)

MAUI というアプリケーション開発技術が発表されました。

MAUI の特徴は次の通りです :

  • iOS や Android、Windows など複数のプラットフォームに対応したネイティブなユーザーインターフェイスを実現できる UI フレームワーク
  • 従来の Xamarin Forms の技術の延長
  • .NET 6 Preview (2021 年 11 月頃リリース予定) でリリース予定
  • 全体を C#/.NET で開発
  • UI は XAML (WPF や UWP、Xamarin Forms の UI 言語) ベース

XAML (Extensible Application Markup Language):

  • WPF や UWP、Xamarin Forms の UI 記述用言語
  • 今までは、XAML は WPF や UWP、Xamarin Forms で統一されていなかった
○ Blazor

現在すでに Blazor Server という、C# だけで Web アプリケーションが開発できるサーバーサイド技術が .NET で使用できますが、この技術が拡張され、Web アプリケーションだけでなくネイティブアプリケーションも作成できるようになることが予定されています。

Blazor の特徴は次のとおりです:

  • Web 技術の延長
  • 全体を C#/.NET で開発
  • UI は Razor (ASP.NET MVC の UI 言語) ベース

今回新たにリリースされたのは、Blazor WebAssembly 3.2.0 (GA) です。

近い将来この Blazor が拡張され、ネイティブ アプリケーション (Web アプリケーションでない Windows アプリケーション/Mac アプリ /iOS アプリ/Android アプリ) も作れるようになります。

  • Blazor Server (既存)
    • サーバーサイドを C# で記述。 ASP.NET WebForms に置き換わるものとされる。
    • クライアントサイドとは、SignalR で通信し、リアルタイムに同期をとる。
  • Blazor WebAssembly (今回リリース)
    • Blazor Server と違い、クライアントサイドで C# が動作する Web アプリケーションを作成できる
    • クライアント サイドのプログラムを JavaScript ではなく、C# で記述。
    • クライアント サイド (Web ブラウザー) 上で、WebAssembly の .NET が動作する
  • Blazor PWA (Progressive Web Application) (将来)
    • Web アプリケーションが単独のアプリケーションとして動作する
  • Blazor Hybrid (将来)
    • Electron や WebView を用いてデスクトップアプリケーションを開発
  • Blazor Native (将来)
    • ネイティブアプリケーションを開発
○ 参考
● 新たな Windows アプリケーション開発方法

新たな Windows アプリケーション ライブラリーとして WinUI 3 が発表になりました。

  • WinUI 3 Preview 1
    • UWP と Win32 アプリケーションをサポート
    • C/C++、.NET 5、WebView2、Fluent Design Controls に対応

■ 開発ツール関連

○ GitHub Codespaces/Visual Studio Codespaces

以下が発表されました:

  • GitHub Codespaces
  • Visual Studio Codespaces

Web 版 (クラウド版) の Visual Studio Code/Visual Studio です。

Visual Studio Codespaces は、以前 Visual Studio Online と呼ばれていたものです。

ネイティブアプリケーションだった Office が Web 版になり、Microsoft 365 になったように、様々なツールがクラウド版になっていきます。

○ Visual Studio
  • Visual Studio 2019 の 16.6 と 16.7 Preview 1 が公開されました。
  • Live Share でボイスチャットがサポートされます。
  • Windows Forms Designer for .NET Core がリリースされました。
  • Surface Duo (2 画面) の開発なども行えるようになってきました。
  • ML.NET 開発用の Model Builder が組み込まれました。
○ 参考
○ C# 9.0

C# 9.0 が発表されました。

○ 参考

■ Azure 関連

○ Azure

Serverless や AI/Data などを中心にアップデートされています。

  • AKS (Azure Kubernetes Service)
  • Windows Server コンテナー
  • Private Link
  • Spot VM
○ Data 関連
  • Cosmos DB

Free Tier

AutoScale

Azure Synapse Link: データベース分析サービス

○ ML 関連
  • Cognitive Services
    • Personalizer
    • Speech Voice
  • Azure Machine Learning
    • AutoML 周り
    • Responsible ML
    • AI スーパー コンピューターの発表
○ 参考

■ Power Platform/Microsoft 365

○ Power Platform

Power Apps や Power Automate、Power BI、Dynamics 365、Microsoft 365 の新たな統合機能の発表

○ Microsoft 365 (旧名 : Office 365)

Microsoft Teams の多数のアップデートの予定が発表

■ Windows 関連

○ Terminal

・ Windows Terminal 1.0

次のような機能があります。

Microsoft Store からインストールできます。

  • タブ機能

タブごとに Windows Subsystem for Linux、コマンドプロンプトや PowerShell など

タブの内部をペインに分割する機能

○ WSL2

WSL は、Windows に組み込まれた virtual な Linux 環境です。

Microsoft Store からインストールできます。

WSL2 で新たに GPU がサポートされます。 CUDA 利用できたり、GPU 版 Tensorfow が動作できたりします。

○ パッケージ マネージャー

winget という Windows Package Manager (Preview 版) がリリースされました。

Linux のようにコマンド ラインから簡単にアプリケーションなどがインストールできます。

参考:Windows Package Manager Preview | Windows Command Line
○ API

Project Reunion というプロジェクトが発表されました。

Windows の API は現在次のように 2 つ ありますが、これを統合しようというプロジェクトです。

  • Windows の 2 つの API
    • Win32: 従来の API
    • WinRT: UWP (Universal Windows Platform) 用の API

■ 関連サイト

2020年08月06日

[C#]【ライブコーディング】連結リスト LinkedList ‐自作で学ぶコレクションの基礎‐

YouTubexin9le さんとコーディング ライブをやりました。

お題は、C#連結リスト LinkedList<T>

テストを書きながら少しずつ実装していくというものになっています。

Visual StudioLive ShareLive Unit Testing を使い、2人で快適にコーディングすることができました。

About .NET

ブログ「プログラミング C# - 翔ソフトウェア (Sho's)」のカテゴリ「.NET」に投稿されたすべてのエントリーのアーカイブのページです。過去のものから新しいものへ順番に並んでいます。

次のカテゴリはAI/MLです。

他にも多くのエントリーがあります。メインページアーカイブページも見てください。

Powered by
Movable Type 3.35