ガード節によるネスト防止
ステートメントをネストし過ぎると可読性が著しく低下します。逆に言えばネストが少なければ見やすくなるのです。
課題
以下のような数値をチェックするような関数を考えます。
private int test1(int x, int y, int z) { int res = 0; if (x < 0) { res = 1; } else { if (y < 0) { res = 2; } else { if (z < 0) { res = 3; } } } return res; }
Private Function test1(ByVal x As Integer, ByVal y As Integer, ByVal z As Integer) As Integer Dim res As Integer = 0 If x < 0 Then res = 1 Else If y < 0 Then res = 2 Else If z < 0 Then res = 3 End If End If End If Return res End Function
リファクタリング
この場合はガード節を利用したリファクタリングが効果的です。ガード節とは、レアケースと通常ケースを切り分け、早めにreturnなどで抜けてしまう手法をいいます。ガード節を利用するとステートメントのネストを減らすことができ、また異常系と正常系の切り分けが認識しやすくなるメリットがあります。
private int test2(int x,int y,int z) { if (x < 0) return 1; if (y < 0) return 2; if (z < 0) return 3; return 0; }
Private Function test2(ByVal x As Integer, ByVal y As Integer, ByVal z As Integer) As Integer If (x < 0) Then Return 1 If (y < 0) Then Return 2 If (z < 0) Then Return 3 Return 0 End Function
このようにガード節を用いると、「条件に全く一致しない場合は、初期値を返す(何もしない)」を明確に提供することが出来ます。
集計メソッド(Max、MIn、Average、Sum、Count)
数値コレクションの最大値、最小値、平均値、合計、コレクション数を取得することができるメソッドです。
int[] values = { 1, 9, 5, 6, 8, 6, 2, 5, 3 }; Console.WriteLine(values.Max()); Console.WriteLine(values.Min()); Console.WriteLine(values.Average()); Console.WriteLine(values.Sum()); Console.WriteLine(values.Count());
Dim values As Integer() = {1, 9, 5, 6, 8, 6, 2, 5, 3} Console.WriteLine(values.Max()) Console.WriteLine(values.Min()) Console.WriteLine(values.Average()) Console.WriteLine(values.Sum()) Console.WriteLine(values.Count())
9 1 5 45 9
取得メソッド①(First、FirstOrDefaullt、Last、LastOrDefault)
Firstメソッドはコレクションの先頭を取得するメソッドです。
int[] values = { 1, 9, 5, 6, 8, 6, 2, 5, 3 }; Console.WriteLine(values.First());
Dim values As Integer() = {1, 9, 5, 6, 8, 6, 2, 5, 3} Console.WriteLine(values.First())
1
正直単体で使用することはほとんどないメソッドですが。
Firstメソッドは他にも引数に条件を指定することで、最初に条件に合った要素を取得することが可能です。
//偶数の最初のデータを取得 Console.WriteLine(values.First(x => x % 2 == 0));
'偶数の最初のデータを取得 Console.WriteLine(values.First(Function(x) x Mod 2 = 0))
6
Firstメソッドの欠点として、コレクションがnullの場合や条件に合った要素が無い場合、System.InvalidOperationExceptionの例外が発生してしまいます。これを回避するメソッドとして、FirstOrDefaultメソッドがあります。FirstOrDefaultメソッドはSystem.InvalidOperationException例外が発生する場合、対象の型の初期値を返します。
Console.WriteLine(values.FirstOrDefault(x => x > 9));
Console.WriteLine(values.FirstOrDefault(Function(x) x > 9))
0
但し、この場合0が最初に見つかったので0を返したのか、例外が発生する状況になったので0を返したのかの判断ができませんので、実際のコーディングではもう少し工夫が必要です。
First、FirstOrDefaultメソッドの逆で、最後の要素から検索するメソッドがLast、LastOrDefaultメソッドです。こちらも例外発生と初期値を返す挙動はFirst、FirstOrDefaultメソッドと同じです。
Visual Studio 2019
docs.microsoft.com
先月の4月2日にVisualStudio2019が公開され、4月30日にバージョン16.0.3がリリースされました。そろそろ安定したかな?ということと、GWでまとまった休みもあったので、この機会に開発環境を2017から2019に乗り換えてみました。
新機能
詳細は↓を見てもらうとして・・・。
docs.microsoft.com
docs.microsoft.com
以下が大まかな内容です。C#のみとなり、VB.NETは今回は何もなさそうです。
- switchのパターンを式で書ける
- インターフェースの仕様変更(メンバーアクセサーの実装)
- using宣言(ステートメントの改良)
- 静的ローカル関数
- null許容参照型
機会があれば実際に挙動を確認しながら説明したいと思います。
メモリ使用量
約1ヶ月利用しましたが、今のところ2017に比べて困るというこはなく、むしろ高速です。今まで400MB~600MBくらい喰っていたメモリも30%くらいダウンした感じ。まぁまぁ快適です。
Whereメソッド
Whereメソッドはコレクションに対して指定した条件に一致する要素を抽出します。
例えば、適当な数配列があって、5以上の要素をListへ格納します。
int[] values = { 1, 9, 5, 6, 8, 6, 2, 5, 3 }; //従来の方法 List<int> res1 = new List<int>(); foreach(int v in values) { if (v > 5) { res1.Add(v); } } //LINQ List<int> res2 = values.Where(x => x > 5).ToList();
Dim values As Integer() = {1, 9, 5, 6, 8, 6, 2, 5, 3} '従来の方法 Dim res1 As New List(Of Integer) For Each v As Integer In values If v > 5 Then res1.Add(v) End If Next 'LINQ Dim res2 As List(Of Integer) = values.Where(Function(x) x > 5).ToList()
WHEREメソッドはFor文だけでなく、If文も削除してしまいます。
型推論のススメ
以前型推論を説明しましたが、これが乱用されてしまうと問題になりそう、と思うのは正しい感覚です。ただし、時と場合によっては型推論は非常に有用ですので使える場面においては積極的に使っていこうじゃないか、というのが今回のテーマです。
Variant型ではない!
何度も言いますが、型推論はVariant型ではないということです。ちゃんと型が定まらない場合や型があっていない場合はコンパイルエラーとなり、何でも通るわけではありません。
var a = 10; //型推論でint型 var b = 1.5; //型推論でdouble型 a += b; //コンパイルエラー
Dim a = 10 '型推論でInteger型 Dim b = 1.5 '型推論でDouble型 a += b 'コンパイルエラー
変数にカーソルを合わせれば、ちゃんと型が決定されていることが分かります。
逆に言えば、型がちゃんと特定されている環境下でなければ型推論は働かないということになります。
DRY原則
プログラミング原則の一つにDRY原則というものがあります。恐らくプログラミングをしている人であれば誰でも一度は聞いたことがある言葉です。
詳しく書くと非常に長くなるので、補足は自分で書籍を探して読んでみてください。「Don't Repeat Yourself」の略で、簡単に言えば冗長性・重複性の排除です。これらが点在するようなものは、拡張縮小の手間になるだけでなく、バグの要因にもなるため出来るだけやめましょうね、というものです。これはプログラミングコードだけではなく、ドキュメントや仕様書など全般的な話です。
つまり、型推論とは冗長性の排除という点を考えれば理にかなっているわけです。
何を指標に使うか
例えば、以下のように宣言を簡素化できるのは言うまでもありません。
List<int> a = new List<int>() { 1, 2, 3 }; var b = new List<int>() { 1, 2, 3 };
Dim a As List(Of Integer) = New List(Of Integer) From {1, 2, 3} Dim b = New List(Of Integer) From {1, 2, 3}
じゃあ、長い宣言はすべて型推論で書くのか?という極端な話にはならず、あくまで私個人のルールとして以下のように考えています。
- 右辺をぱっと見で型が「明らかに」判別できる
- 最低メソッド内、出来れば一つの構造化内で収まるようなより短いスコープの変数
- int型やstring型などの組み込み型には使わない
意外と厳しそうに感じるかもしれませんが、プロラミングをしていると上記3つに当てはまる場面って結構多いです。
HTMLからのPOSTがASP.NETで受け取れない
久々に一人でハマった話。備忘として残しておきます。
ひょんなことから、HTML(正しくはASP)からPOSTしてASP.NET側よりRequest.Formで値を取得していますが、突然空(nullではない)が返ってきて正しく取得できない状況になりました。
<form action="http://xxx.aspx" method="post"> <input type="hidden" name="SitenCD" value="5">
通常であればRequest.Form("SitenCD")で簡単に取得出来るはずなのですが・・・。
さんざんネットやら本やらを探しましたが有力情報ゲット出来ず。一抹の不安が頭をよぎります・・・。
ネットに情報が無い=超初歩的ミスをやらかしている
結論から言うと、全く関係の無い情報から答えに行きつきました。原因は短縮URL機能です。ASP.NETにはGlobal.asaxのApplication_Startイベントにより、拡張子を取り除いた短縮URLにルート設定するRouteConfig.RegisterRoutesメソッドが呼び出されます。こいつが原因でした。
public class Global : HttpApplication { void Application_Start(object sender, EventArgs e) { // アプリケーションのスタートアップで実行するコードです RouteConfig.RegisterRoutes(RouteTable.Routes); //←コレ BundleConfig.RegisterBundles(BundleTable.Bundles); } }
Public Class Global_asax Inherits HttpApplication Sub Application_Start(sender As Object, e As EventArgs) ' アプリケーションの起動時に呼び出されます RouteConfig.RegisterRoutes(RouteTable.Routes) '←コレ BundleConfig.RegisterBundles(BundleTable.Bundles) End Sub End Class
xxx.aspxに要求が来ると応答はxxxを指定してしまいます。つまり、xxxにリダイレクトされる=xxxをGET要求するということです。だからRequest.Formで取得出来なかったというオチでした。
なお、URLルーティングの話は以下を参考にどうぞ。
docs.microsoft.com