最初に断っておきたいこと
C#3.0の話です。 Func型や匿名メソッド自体の説明はしていません。
まず最初に、私が考える読みやすいコードの要素について断片的に提示します。
1.処理のスコープを最小にする。
2.今、読みたい粒度のコードしか読まなくても良い。
というものがあります。
どれも、いくつかある読みやすいコードの要素の断片でしかありませんが、この1と2は矛盾を孕んでいます。
それでは、どんな矛盾を孕んでいるかサンプルを交えて説明します。
001 public class サンプル1 002 { 003 public void サンプルメソッド() 004 { 005 if (鼻毛抜きの在庫 == 0 && 鼻毛カッターの在庫 == 0) 006 { 007 return; 008 } 009 010 鼻毛処理(); 011 } 012 } 013
鼻毛処理のできる道具があれば、鼻毛処理ができる。
という仕様を実現しています。
しかしながら、「鼻毛処理のできる道具があれば」の詳細部分まで同じメソッド内に記述されてしまっています。
そこで、
001 public class サンプル2 002 { 003 public void サンプルメソッド() 004 { 005 if (!鼻毛処理の道具がある()) 006 { 007 return; 008 } 009 010 鼻毛処理(); 011 } 012 013 private bool 鼻毛処理の道具がある() 014 { 015 if (鼻毛抜きの在庫 > 0) 016 { 017 return true; 018 } 019 020 if (鼻毛カッターの在庫 > 0) 021 { 022 return true; 023 } 024 025 return false; 026 } 027 028 public void その他の処理() 029 { 030 throw new NotImplementedException(); 031 } 032 033 } 034
これで、「2.今、読みたい粒度のコードしか読まなくても良い。」という要素が満たされた実装になりました。
しかしメソッドアウトする前は、処理のスコープがメソッド内にしかなかったのに、メソッドアウトした事で処理のスコープがクラス全体に広がってしまいました。
実装したばかりの今なら、まだサンプルメソッド内でしか使われていないとわかりますが、将来このコードを見たときに、サンプルメソッド内でしか使われていないと断定することができません。
これでは、「1.処理のスコープを最小にする」という要素が満たされなくなってしまっています。
そこで、以下のようにFunc型と匿名メソッドを利用してサンプルを修正してみます。
001 public class サンプル3 002 { 003 public void サンプルメソッド() 004 { 005 Func<bool> 鼻毛処理の道具がある = 006 delegate 007 { 008 if (鼻毛抜きの在庫 > 0) 009 { 010 return true; 011 } 012 013 if (鼻毛カッターの在庫 > 0) 014 { 015 return true; 016 } 017 018 return false; 019 }; 020 021 if (!鼻毛処理の道具がある()) 022 { 023 return ; 024 } 025 026 鼻毛処理(); 027 } 028
これで、匿名メソッドという形でメソッドアウトし「2.今、読みたい粒度のコードしか読まなくても良い。」という要素を満たした上に、処理のスコープもメソッド内にとどまりますので、「1.処理のスコープを最小にする。」という要素も満たされます。
「鼻毛処理の道具があれば」という仕様の詳細まで知る必要が無いときは匿名メソッドを読み飛ばせば良いのです。
以上のように「ひとつのメソッドの中でしか利用しないが、粒度が違うためメソッドアウトする。」といったような局面において最小のスコープを維持したままメソッドアウトする道具として匿名メソッドを使う例の紹介でした。
ちなみに、サンプル3をラムダ式で書くと
001 public class サンプル3 002 { 003 public void サンプルメソッド() 004 { 005 Func<bool> 鼻毛処理の道具がある = 006 () => 007 { 008 if (鼻毛抜きの在庫 > 0) 009 { 010 return true; 011 } 012 013 if (鼻毛カッターの在庫 > 0) 014 { 015 return true; 016 } 017 018 return false; 019 }; 020 021 if (!鼻毛処理の道具がある()) 022 { 023 return ; 024 } 025 026 鼻毛処理(); 027 } 028
となります。