条件式というのは何かと複雑になってくるものです。
1つ1つはシンプルなはず。なのになぜ?
そこを複数の視点から解明して実用的に使っていたら3個に集約されました。
プログラムの条件式をシンプルにする2つだけの手法
条件式は除外ケースから先に処理(書いて)いく
例えば、stringを正の整数にする関数でint型で返すとします。
仕様としては数字のみで、小数点「.」があれば切り捨てる。「-」その他の文字があれば仕様外ということで常に -1 を返す、とします。また0は許容(OK)とします。
また、nullは-1で、""(空文字)は0とします。
tryparse関数がありますが、あえてparse関数を利用するものとして、tryも利用しないこととします。
順序としては
1. nullならreturn -1
2. 0と1~9と「.」以外の文字があればreutrn -1
3. 「.」があれば、stringの最初の「.」以降の右側を除去する。
4. 先頭に0があれば除去する
5. ""ならreturn 0
上記1~5を行っておくことで、ここに処理が遷移してきた段階では数字のみで来ます。
例外エラーが生じない状態で、parse関数を使えますし、万が一にも例外エラーが出るケースがあれば、それに対応できます。
多くのケースではtry~catchでよいのですが、例外の内容によって細かく処理が分かれるようなケースでは
このように除外するケースを先に書いておく、というのがとても有用でした。
関数内の諸条件ではローカル関数もしくは匿名関数として処理する
VisualStudio2013の頃から、個人的には匿名関数が好きになってしまったのでご紹介します。
感覚としては関数の中に、その関数専用の特化チョイ関数を書く簡便さで使っています。
今では(C#7からは)ローカル関数としてシンプルに記述できます。(コメントで教えて頂きました)
匿名関数ではFunc<bool>is_correct_value=(in_text)=>{};だったのが、ローカル関数ではboolis_correct_value(in_text){}と、普通にかけます。
privateintget_positive_integer(stringin_int_text){Func<bool>is_correct_value=(in_text)=>{if(in_text==null)returnfalse;if(Regex.IsMatch("[^0-9.]",in_text)returnfalse;returntrue;};if(is_corrct_value(in_int_text)==false)return-1;stringtmp_text=in_int_text;if(tmp_text.contains(".")==true)tmp_int_text=tmp_int_text.substring(0,tmp_int_text.IndexOf('.'));if(tmp_int_text=="")return0;intret_value=int.parse(tmp_int);returnret_value;}
のような感じです。
ローカル関数なら IEnumerable内でも yield return を使える
yieldのおかげで複雑な処理を複数段に分けて、簡潔な処理記述にできるのですが、
匿名関数のときは IEnumerable の中で匿名関数が使えずに「仕方ないな」と思っていました。
ですが、ローカル関数ではいけるのでもっと早くにあればよかった、という感じです。
(匿名関数が使えなくても専属のprivate関数を書いた程度の違いではありますが、数ヶ月後の可読性というか
思い出しやすさが違います(汗)
ちなにみyieldは個人的には処理手順の段階化(レイヤーを分けられる)・単純化に有用だなと心底思っています。この機能のおかげでオフコン世代のインタープリタ記述のものをプリンタの独自命令にまで変遷させていくことができました。