C# C#らしくC#を書こう 自己紹介 岩永 信之 ぐぐれ このサイトの中の人 テーマ 「C#らしいコードの書き方」というテーマを頼まれています その前にアンケート 経歴 プログラミング自体経験が浅い人? C++? Java? Flash/ActionScript? LAMP系、LLな人? 開発系じゃないけども聞きに来た? 今 Unity? ASP.NET? その他 C#? 今C#使ってるわけじゃないけど聞きに来た? C#らしい? C#だからというわけじゃないけども 他の言語から移ってきた人が戸惑うところ ソースコード/ライブラリ/アプリの依存関係 イベント LINQ Visual Studioに頼ったコーディング リファクタリング クラスやメソッドの生成 テスト自動化 前振り: コードの依存関係 依存関係は一方通行に 特定のフレームワークへの依存は少なく 依存関係(1) 基本: 依存は少ないに越したことはない 依存グラフ どっちがいい? 依存関係(2) 基本: 循環依存はダメ class A { B _b; } class B { A _a; } 特に、ライブラリをまたいだ循環依存はダメ ライブラリX ライブラリY class A { B _b; } class B { A _a; } 依存関係(3) 基本: 画面用フレームワークへの依存は極力避ける 循環、ダメ! ゲーム画面 GameModel ゲームの中核処理 Unityなどの フレームワーク ASP.NET, Silverlight などでも同様 画面用フレームワーク への依存、ダメ! 依存関係を切りたい理由 移植性 違うフレームワークを対象にしたい サーバーとクライアントで同じコードを共有したい テストしやすさ 依存関係が複雑になるほどテストが大変 いわゆるprintfデバッグ、ステップ実行デバッグするならコンソール ア プリが一番 事例: 部分的に流用 全部は要らない 例えば山ほどソース コードがあったとして そのうち使いクラスは 1つだけだとして これだけコピーして 動くかどうか 依存性 = 付随して、何ファイルのコピーが必要になるか 事例: サーバーとクライアントで同じ処理 サーバーとクライアントで同じ処理をしたいことが 例: 毎秒のように更新したいもの 資源の増加量計算 施設建設の完了判定 毎秒問い合わせるのも コネクションの無駄遣い クライアント 同じ処理を再実装 サーバー チート対策もあるので、 クライアント側任せにも できない 同じ処理は同じコードで 書ければいいけども… サーバーとクライアントで言語が違う! クライアント Flash/ActionScript, HTML5/JavaScript‡ サーバー 違う PHP, Python, Perl, Ruby, Java† でも、C#ならば クライアント Unity/C#, Silverlight/C# サーバー 同じ ASP.NET/C# †クライアントがAndroidならJava使えるけども ‡サーバー側にNode.js使う? コード共有するなら 特定のフレームワークに依存しない書き方を クライアント (Unityとか) 中核処理 UnityにもASP.NETにも 依存したくない サーバー (ASP.NET) Unityに依存 • UnityEngine名前空間 Silverlightに依存 • System.Windows名前空間 ASP.NETに依存 • System.Web名前空間 この辺りの名前空間のクラス は、中核処理では使わない というか、コード共有するなら 特定のフレームワークに依存しない書き方を こいつも クライアント (Unityとか) サーバー (ASP.NET) こいつもデバッグ 実行遅くない? 中核処理 コンソール アプリ 単体テスト こっちでデバッグするのが低ストレス 依存切り 依存関係を一方通行にする 特定のフレームワークへの依存をなくす 依存を最小限にしたいフレームワーク GUI ウェブがらみ Unity、 Silverlight、 ASP.NET(HTML画面を生成) ASP.NET(JSON返すだけとかでも) セキュリティ的に怪しい部分 ファイル読み書きなど 書き込み権限与えていいのか スマホだと、アプリをまたいだファイル共有認めてない ブラウザーなんかはそもそもファイルアクセス無理 依存切りの定石1 フレームワーク依存部分に処理を書かない ビューとかコントローラーとか言われる部分 フレームワーク依存部分に処理を書かない 例えばUnity 画面側コード(この例だとコントローラー) using UnityEngine; public class CityController : MonoBehaviour { void Update() { こういうところにゲームの } 中核処理を書いちゃダメ } Unity依存 コントローラーなら、プレイヤーの入力に応じてモデルを呼ぶだけ ビューなら、描画などの処理だけ書く フレームワーク依存部分に処理を書かない 例えばASP.NET MVC 画面側コード(この例だとコントローラー) using System.Web; using System.Web.Mvc; namespace MvcApplication1.Controllers { public class HomeController : Controller { public ActionResult Index() { こういうところにゲームの } 中核処理を書いちゃダメ } } ASP.NET依存 依存切りの定石2 インターフェイスやデリゲートを利用 インターフェイスやデリゲートを使った依存切り 循環依存をなくすための定石 class A { B _b; } class B { A _a; } class A { IB _b; } class B : IB { A _a; } interface IB { } 一方通行に C#の場合はデリゲート (関数ポインターみたいなもの) を使うことも多い。 インターフェイスやデリゲートを使った依存切り 例: いわゆるprintfデバッグ Unityの場合、UnityEngine.Debugクラスを利用 中核処理側(Unityに依存したくない) ログの出力先を 外部から与える public class Debug { public static Action<string> Logger { get; set; } public static void Write(string message) { if (Logger != null) Logger(message); } } 画面側(Unityを利用) Models.Debug.Logger = UnityEngine.Debug.Log; インターフェイスやデリゲートを使った依存切り 例: いわゆるprintfデバッグ Unityの場合、UnityEngine.Debugクラスを利用 ちゃんと一方通行 ゲーム画面 ゲームの中核処理 • Models.Debugクラス Unity • UnityEngine.Debugクラス ここに依存関係なし イベント 他の言語からC#に移ってきた人が戸惑う機能その1 オブザーバー パターンを言語構文としてサポート イベント(一般用語として) イベント(処理のきっかけ)が外部からやってくる 例: GUIアプリ 1. イベント登録 GUIフレームワーク (Silverlightなど) プレイヤーがボタンを 押したら教えて 2. イベント通知 ボタン、押されたよ アプリのコード イベントの例 例: WPFアプリ(C#、デスクトップ向けGUI) using System; using System.Windows; using System.Windows.Controls; public class Program { [STAThread] static void Main() イベント登録 ボタンが押された時の処理 { var button = new Button { Content = "ここを押せ" }; button.Click += (sender, e) => MessageBox.Show("ようこそ"); var win = new Window { Content = button }; var app = new Application(); app.Run(win); } } イベントの例 Flash使ったことある人なら addEventListener ボタンが押された時の処理 function myEvent(eventObj:MouseEvent) { trace("ボタンがクリックされました。"); } myButton.addEventListener(MouseEvent.CLICK,myEvent); イベント登録 C#のイベント構文 イベントの登録口を生成 add/removeEventListener相当のものを作ってくれる機能 C#のイベント構文を使った、イベント登録口の作成 public class CityModel { public event Action<Resource> ResourceUpdated; } デリゲートの前にeventキーワードを付けるだけ 意味合い的にはこれに近い private event Action<Resource> _resourceUpdated; public void AddResourceUpdatedListener(Action<Resource> listener) { _resourceUpdated += listener; } public void RemoveResourceUpdatedListener(Action<Resource> listener) { _resourceUpdated -= listener; } 登録口 解除口 C#のイベント構文 イベントの登録口を生成 add/removeEventListener相当のものを作ってくれる機能 C#のイベント構文を使った、イベント登録 CityModel _city; void Start() 登録 { _city.ResourceUpdated += _city_ResourceUpdated; } void _city_ResourceUpdated(Resource obj) { // 更新があった時の処理 } 解除は -= で イベントの使いどころ 受け身に処理したいとき イベントが起きた時にだけ動きたい (イベント駆動型プログラム) 普段は何もせず待機 変化が少ない(イベントがあまり起きない)とき、効率的 注意: 2タイプのGUIフレームワーク ゲーム系 すごく動く上に、パフォーマンスが求められる フレーム単位で、入力・描画、全部自前で管理 メニュー系 イベント駆動向き ユーザーが何か操作した時にだけ動く 「ユーザー操作」というイベントを起点にして駆動 ゲームでもメニューよく使うでしょ カード系ゲームとか、ブラ三的なゲームだと、むしろメニュー系 UIがメイン 一般のゲームでも 例えばRPGでよく見るような画面 C#たん ステータス 装備 レベル 99 次のレベルまで 0 所持金 1,000,000 G プレイ時間30:15:00 時間経過で変化 (更新頻度は秒単位) アイテム パーティ プレイ記録 設定 イベント駆動向き プレイヤーが何か操作す るまで再描画必要ない なのでゲームでもイベント駆動 イベント駆動で画面を再描画 モデル側 内政資源、数秒に1回更新する 更新したとき、イベント発行 public class CityModel { public event Action<Resource> ResourceUpdated; } 画面側 void Start() { _city.ResourceUpdated += _city_ResourceUpdated; } 資源の量が変化したときだけ 再描画処理を動かす LINQ 他の言語からC#に移ってきた人が戸惑う機能その2 データ処理を簡単に データ処理 例 IDで検索 条件を満たす要素が1つでもあるかどうか探す 合計値や最大値を計算 要はSQLで書くような処理 サーバー側だとデータベース持って、SQL書けばいいけども データをクライアント側にキャッシュした場合どうする? メモリ上に読み込んだデータに対してSQL的な処理 LINQ C#は、SQL的なデータ処理が得意 C# 3.0で導入されたLINQ(Language Integrated Query) 例: 顧客データ一覧から、女性客の年齢分布を求める var 女性客の年齢分布 = from c in 顧客一覧 where c.性別 == "女" group c.年齢 by c.年齢 into g orderby g.Key select new { 年齢 = g.Key, 数 = g.Count() }; クエリ式 • SQL的なクエリを書ける • selectが末尾にくるくらいで、かなりSQLそのまんま • C#の型をそのまま使える(補完も効く) LINQ C#は、SQL的なデータ処理が得意 C# 3.0で導入されたLINQ(Language Integrated Query) 例: 顧客データ一覧から、女性客の年齢分布を求める var 女性客の年齢分布 = 顧客一覧 .Where(c => c.性別 == "女") .GroupBy(c => c.年齢, c => c.年齢) .OrderBy(g => g.Key) .Select(g => new { 年齢 = g.Key, 数 = g.Count() }); クエリ式の展開結果(メソッドの連鎖になる) • 元のSQL的な句に対応するメソッドがある • 使うためには、System.Linq名前空間をusing データ処理のパーツ 条件選択、グループ化、整列、集計、射影 入力 a b c d 出力 集計 射影 グループ化 整列 集計 射影 where group by order by 集計 射影 ・・・ from Aggregate, Sum, Countなど だいたいSQLと一緒 select ・・・ ・・・ 条件選択 α β γ δ 一時リストを作っちゃダメ 例: 偶数だけ選択、二乗を計算 悪い例(一時リストを作成) var results = new List<int>(); foreach (var x in data) { if ((x % 2) == 0) results.Add(x * x); } 良い例(LINQ) var results = data .Where(x => (x % 2) == 0) .Select(x => x * x); サンプル MSDN公式サンプル: 101 LINQ Samples http://code.msdn.microsoft.com/101-LINQ-Samples3fb9811b 補足: 匿名関数 その場限りの関数を作れる var results = data .Where(x => (x % 2) == 0) .Select(x => x * x); こんなんのために、いちいち // x が偶数のとき true static bool IsEven(int x) { return (x % 2) == 0; } =>(goes to)演算子 引数 => 式 こんな感じのメソッド作りたくない // 二乗を計算 static int Square(int x) { return x * x; } 補足: 拡張メソッド LINQのSelectなどは、実は「拡張メソッド」 静的メソッドを、後置き記法で書く機能 本来、こう書く必要がある(実は静的メソッド) var results = Enumerable.Select(data, x => x * x); 拡張メソッドなら 見かけ上、インスタンス メソッド的に書ける(後置き記法にできる) var results = data.Select(x => x * x); メソッドを連鎖的に書きやすい Enumerableなどのクラス名を省略できる EnumerableクラスはSystem.Linq名前空間にある 補足: 拡張メソッド LINQのSelectなどは、実は「拡張メソッド」 静的メソッドを、後置き記法で書く機能 拡張メソッドの実装例 public static class Enumerable { 第1引数にthispublic static IEnumerable<U> Select<T, U>( this IEnumerable<T> source, 修飾子を付ける Func<T, U> selector) { foreach (var x in source) { yield return selector(x); } } } Visual Studioに頼ろう ライブ コーディングでも見せようかと Visual Studio もちろん、生産性向上のためのツールだけども 学習ツールとしても優秀 まず、Visual Studioの作るテンプレや、補完で出てくるコードを覚 えよう デモ コード スニペット スマート タグ デモ foreach メソッド抽出 usege-first 単体テスト生成 LINQ サンプル HTMLスクレイピング 15パズル http://code.msdn.microsoft.com/C-15Visual-Studio5511658e http://code.msdn.microsoft.com/C-2-83b48d3f 東方弾幕風もどき
© Copyright 2024 ExpyDoc