佐々木屋

技術的なことから趣味まで色々書きます

取得メソッド①(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に乗り換えてみました。
f:id:sasaki816:20190522093208j:plain

新機能

詳細は↓を見てもらうとして・・・。
docs.microsoft.com
docs.microsoft.com

以下が大まかな内容です。C#のみとなり、VB.NETは今回は何もなさそうです。

  • switchのパターンを式で書ける
  • インターフェースの仕様変更(メンバーアクセサーの実装)
  • using宣言(ステートメントの改良)
  • 静的ローカル関数
  • null許容参照型

機会があれば実際に挙動を確認しながら説明したいと思います。

メモリ使用量

約1ヶ月利用しましたが、今のところ2017に比べて困るというこはなく、むしろ高速です。今まで400MB~600MBくらい喰っていたメモリも30%くらいダウンした感じ。まぁまぁ快適です。
f:id:sasaki816:20190522095231j:plain

リファクタリング

LINQへのリファクタリングが結構優秀。まぁ最初から有無を言わさず書いてしまうLINQerな私には不要ですが、.NET Framework2.0時代の過去の遺産を整理するには楽でいいかもですね。
f:id:sasaki816:20190522222502j:plain
こんな感じで簡単にリファクタリングできます。

C#8.0ベータ版公開

プロジェクトのプロパティのビルドから、ビルド詳細を開きます。ここで言語バージョンを選択できるわけですが、C#8.0(beta)が入っています。こちらを使ってみたい場合は手動で設定する必要があります。
f:id:sasaki816:20190522095258j:plain
ただ、Frameworkランタイムも影響しますので、.NET Framework4.8は入れた方が良いです。

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 'コンパイルエラー

変数にカーソルを合わせれば、ちゃんと型が決定されていることが分かります。
f:id:sasaki816:20190511204452j:plain
逆に言えば、型がちゃんと特定されている環境下でなければ型推論は働かないということになります。

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

OrderByメソッド

OrderByメソッドはLINQのソート機能です。コレクションの要素を並べ替えることが可能です。

昇順の場合はOrderByメソッド、降順の場合はOrderByDescendingメソッドを使用します。

int[] values = { 1, 9, 5, 6, 8, 6, 2, 5, 3 };

//昇順
var res_ask = values.OrderBy(x => x).ToList();

//降順
var res_desk = values.OrderByDescending(x => x).ToList();
Dim values As Integer() = {1, 9, 5, 6, 8, 6, 2, 5, 3}

'昇順
Dim res_ask = values.OrderBy(Function(x) x).ToList()

'降順
Dim res_desk = values.OrderByDescending(Function(x) x).ToList()



ValueTupleなどの構造体やデータベース連携などで複数要素を持ったコレクションに対してもソートは有効です。

var sitenList = new List<(int sitenCD, string sitenName)>
                        { (9, "岐阜支店"), (5, "滋賀支店"), (10, "京都支店")
                        , (7, "大阪支店"), (9, "兵庫支店"), (2, "三重支店")};

//sitenCDで昇順
var res_ask = sitenList.OrderBy(x => x.sitenCD).ToList();

//sitenNameで降順
var res_desk = sitenList.OrderByDescending(x => x.sitenName).ToList();
Dim sitenList = New List(Of (sitenCD As Integer, sitenName As String)) From
                         {(9, "岐阜支店"), (5, "滋賀支店"), (10, "京都支店"),
                          (7, "大阪支店"), (9, "兵庫支店"), (2, "三重支店")}

'sitenCDで昇順
Dim res_ask = sitenList.OrderBy(Function(x) x.sitenCD).ToList()

'sitenNameで降順
Dim res_desk = sitenList.OrderByDescending(Function(x) x.sitenName).ToList()

ここがダメだよ!VB.NET④(「=」の意味)

C#の場合、単に「=」の場合は代入を表します。つまり、下記のコードは変数xに1を代入します。

x = 1;

では、等価の意味はというと、「==」のようにイコールを重ねます。

if (x == y) {
    //~
}

このように等価と代入が異なる演算子として用意されています。

しかしVB.NETは以下のように、代入も等価も同じ「=」を使います。

x = y

If x = y Then
    '処理
End If

前後の文脈を考えないといけないので、少し面倒なのです。