|
Visual Studio 2008と共にリリースされた.NET Framework 3.5の目玉機能の1つが LINQ らしいのだが、私は否定的だ。その理由は、LINQの実現のために追加された機能の乱用が、ソースコードの可読性、保守性を下げ、品質の維持が難しくなると考えるからだ。 コード記述の自由度を上げること自体はいい。しかし、選択肢を増やすのならば、その選択肢を選べないように規制する仕組みも同時に設けてほしい。実際の開発現場では、便利なコードの書き方よりも、均一で統制の取れたコード記述が品質の向上に繋がる。それは長い目で見れば、生産性の向上にも繋がるはずだ。 そう考えると、以下のような記述方法を、今後どのように規制していくかを考慮せざるをえない。コードを書く楽しさだけで、よい業務アプリは生まれないと思うから…。 ** LINQ [#w0b27910] var query = from cust in customers where cust.City == "Tokyo" orderby cust.Name ascending select new { Name = cust.Name, Phone = cust.Phone }; 上記のクエリ式は、interface IEnumerable<T>に対する拡張メソッドを用いた以下のコードと同じ意味になるらしい。 var query = customers .Where(cust => cust.City == "Tokyo") .OrderBy(cust => cust.Name) .Select(cust => new { Name = cust.Name, Phone = cust.Phone }); こうして見ると、LINQのクエリ式には、.NET Framework 3.5で追加された型推論、オブジェクト初期化子、匿名型、拡張メソッド、ラムダ式が使われていることがわかる。 *** 型推論 [#e83d6e4a] var s = "Hello World!"; - 変数に格納される型をコンパイラが推測するらしい。 - なので、実行時に型を決定する遅延バインディングとは異なるものらしい。 - 人の目でソースを追ったり、コード解析ツール等で、型がわかりにくくなり、解析が難しくなる。 - コンパイル時にバグを検出できる確率が低下する。 - (Guideline)LINQ文以外では、なるべく利用しない。 *** オブジェクト初期化子 [#m74dbe04] Customer cust = new Customer() { Name = "Taro" }; - インスタンス化とプロパティ値代入の2行に分解したコードと同義。 - 引数付きコンストラクタのように、必要な全てのプロパティがセットされることを保障するものではない。使い方を誤ってはいけない。 *** 匿名型 [#c6638926] var product = new { Name = "ノート", Price = 100 }; - メソッドを持たず、プロパティのみのクラス。 - メソッドを持たず、getプロパティのみのimmutableクラス。 -- ただし、プロパティの型をDelegateにすれば、メソッドみたいに振る舞うプロパティを定義できるらしい。 var obj = new { Greet=(Func<string, string>)((name) => "Hello " + name) }; Console.WriteLine(obj.Greet("World!")); - 型名はコンパイラが自動で決定するので、var型である必要がある。 - [Serializeable]等の属性付与ができない。 *** 拡張メソッド [#xd386d44] namespace ExtensionMethods { public static class MyExtensions { public static int WordCount(this String str) { return str.Split(new char[] { ' ', '.', '?' }, StringSplitOptions.RemoveEmptyEntries).Length; } } } - 特殊な静的メソッドを定義することで、既存のクラスに新しいメソッドを追加できる。 -- (Guideline)特定のクラスを拡張するすべての拡張メソッドを1つの静的クラスにまとめ、そのクラスに「<ClassName>Extensions」という名前を付けるようにしよう。 - 上の例だと、using ExtensionMethods; することで、この拡張メソッドをスコープに取り込み、アプリケーションから実行可能になる。 -- (Guideline)名前の衝突が起きる可能性を低くするため、拡張メソッドのクラスを固有のネームスペース内に保存するようにしよう。 - カプセル化の原則に違反するような拡張をされる危険がある。 *** ラムダ式 [#me67d096] // define in class delegate bool MyFunc(string str); MyFunc func = str => str == "Tokyo" Console.WriteLine(func("Osaka")) - 単一の結果を返す無名のメソッドを定義する -- 匿名メソッドの進化形と考えれば、使いどころもほぼ同じでしょう。