実例からみるC#でのメタプログラミング用法集

Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用、という題でMetro.cs #1にて話してきました。

Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例 from Yoshifumi Kawai

現在、PhotonWireというフレームワークを作っているのですが、それで使ったメタプロ技法を紹介しました。ExpressionTree, T4, ILGenerator, Roslyn(Analyzer), Mono.Cecilとそれなりに満遍なく使っているので、それらをどーいう時に使えばいいのかというヒントになれば幸いです。まとめに書きましたが、手法自体は少なくするに越したこたぁないです、メタプロってついやりすぎちゃう傾向にあるんで、目的Firstを忘れないようにしないと本末転倒になりがちです。あと、それぞれは別にそんなに難しくない、というか難しくやらないようにするのが良いですね、そもそも長い式木とか長いILとか書きたくないですし……。

Proxyのvirtual強制は制約強くてゲロなので喪われた技法って感じですが、Roslyn Analyzerでコンパイラエラーに制限できることによって復活したかもしれない気がするかもしれない!あと、Taskは大事ですね、非同期のシグネチャとしてTaskで表明できるようになったのはひじょーーーーーーに大きな事です。これは実際めちゃくちゃ大きなことなのに過小評価してたり勘違いしてたりすると、いくない。もちろん、async/awaitで手軽にハンドリングできるようになったことも大事。RPC Revisitedですよ。そんなわけでごった煮しつつも、私的な独断と偏見に基づくバランス感覚で取捨してます。この辺の感覚はかなり大事だと思うんだな。

なお、書籍ではメタプログラミング.NETが良書なのでオススメです。

PhotonWire

題材のPhotonWireは、グラニで現在開発中のリアルタイムネットワーク通信用フレームワークです(ところでUnity、特にUniRxをゴリゴリ活用した先端的(エキセントリックともいう)なスクリプティング環境や、クライアントからサーバーまで全てC#で統一したあいそもーふぃっくな開発に興味のある人はいつでもウェルカムで採用募集中です)。といってもレイヤー的には比較的高レベルで、下回りではPhoton Serverというミドルウェアを採用していて、その上のRPCフレームワークを提供という感じです。キャッチコピーは「Typed Asynchronous RPC Layer for Photon Server + Unity」ということで、特にUnityとの繋ぎ込みを重視していて、クライアント-サーバー、サーバー-クライアント、サーバー-サーバーの方向のRPCを提供します。クライアント-クライアントは非サポート(あれは百害あって一利なし)。

クライアント-サーバーはご存知SignalR、サーバー-サーバーはOrleansという分散アクターフレームワークのAPIを参考にしています、が、サーバーの分散に関しては、別に全然賢くないです。というか機能全く無いです。もともとのPhotonがそこに対するサポートがゼロで、PhotonWireでもたいしたサポートを入れてません。私的にはこの素朴な割り切りは結構好きですね。変に透過的に見せるよりも、それぞれのサーバー/それぞれのレイヤーを独立して、ある程度プリミティブな操作を可能にしたほうがはまりどころも少ないし。別に賢くはないんだけど、手堅い。ゲームという用途で考えると、あまりカシコイものよりも、愚直なシステムのほうがマッチしそうな感触があります。必要になったら、まぁ適当に考える。

Photon(+Unity)にはもともとPhoton Unity Network(PUN)という高レベルなクライアントが用意されているのですが、正直あんまり良いものでもない(特にPhoton Serverで自前ロジックを入れてくような場合は)ので、無視です、無視。で、PUNを通さない低レベルのSDKもあって、こちらは相当低レベルで本当に接続とデータ転送しか提供していないので(ただし低レベルSDKとしてはこのぐらいのほうが好ましい、へたに変なのがゴチャゴチャついてるよりも)、サーバーSDK(こちらもかなり低レベル)ともども統一した形で、ちょっと高レベルなもの、ぐらいの位置づけで作り上げてみました。

クラスとメソッドに属性でIDつけさせて、それで振り分けしているのでJSON-RPC的なメソッド名なども送っちゃうのでサイズが大きくなる、ということはなく、通常の転送に較べてもオーバーヘッドは2byteです。別に全然ない。ユーザー定義の型を送る場合(通常のPhotonはこれをサポートしてない)はMsgPack-CLIでシリアライズ/デシリアライズするため、その際の容量増大も極小です。また、シリアライザ/デシリアライザはその型に合致したものを事前生成するため、Unityにおいても高速に動作させられます、といったシステムも含まれています。

デバッグ用の専用クライアント(WPF製)なども込み込みで(これのデザイン面の話はMaterial Design In XAML Toolkitでお手軽にWPFアプリを美しくに書いてます)、痒い所に手が届きつつも、機能自体は小さく「型付きの非同期RPCの提供」から逸脱しない程度におさめているので、まーまー使いやすいんじゃないかなー、と思いますね。もちろん、クライアント側はUniRx前提です。

UniRx同様、GitHub/AssetStoreでの公開予定はあるというか、早く公開したいんですが、Photonの次バージョンのベータ版を使って開発してるので、そっちが正式リリースされないと公開できないので早く出ないかなぁ(チラッ とオモッテマス。

Profile

Yoshifumi Kawai

Cysharp, Inc
CEO/CTO

Microsoft MVP for Developer Technologies(C#)
April 2011
|
July 2024

Twitter:@neuecc GitHub:neuecc

Archive