- 投稿日:2020-03-10T19:05:03+09:00
C#のLINQで何気なく使っているラムダ式について。
まずはLINQのサンプルをどうぞ
こちらをご覧ください。C#のソースコードで、LINQのSelectメソッドのサンプルです。
using System; using System.Linq; class Program { static void Main() { int[] a = { 1, 2, 3, 4, 5 }; var result = a.Select(c => c + 3); foreach (var item in result) Console.WriteLine(item); Console.Read(); } }実行結果はこちら。配列のアイテムすべてに 3 が足されています。
さてさて、c => c + 3はラムダ式による書き方ですね。この
c => c + 3はいったい何者なのか?
少しずつ書き換えながら考えていきます。ラムダ式を書き換えていくよ
var result = a.Select(c => c + 3);この
c => c + 3って、cを渡すとc + 3という結果を返す、まるでひとつの関数みたいな働きをしてますね。
cが引数で、c + 3が返り値、みたいな。実はこれ、下記のように書き換えてもまったく同じ結果となります。
var result = a.Select((int c) => c + 3 );さっきの書き方だと
cがいきなり出てきてビックリでしたが、(int c)と書けばちゃんと引数の宣言に見える!
intが省略されていたのです。
だってc + 3;とかやってるし、どう見てもint型でしょ。型推論というやつです。型推論可能なときは型指定を省略できるんですねえ。さらにここから、下記のように書き換えも可能です。
var result = a.Select((int c) => { return c + 3; });さっきまでは
c + 3がポツンと書いてあったけど、{ return c + 3; }と書けばちゃんと値を返しているように見える!
returnも省略可能なんですねえ。ちなみに
{と}の中、何行も書くことができるよ。var result = a.Select((int c) => { int tmp = c + 3; return tmp; });まぁ何行も書くんだったらちゃんと改行すべきだと思うけど…。
var result = a.Select( (int c) => { int tmp = c + 3; return tmp; } );ここまで書き換えると、なんかもう完全に関数っぽい見た目ですね。
ラムダ式は「匿名関数」の書き方のひとつ!
ラムダ式で省略されていたところを丁寧に書いていくと、関数っぽい見た目になりました。
それもそのはず、匿名関数を書くための手段のひとつがラムダ式なんです!さっき
Selectメソッドに渡していた、(int c) => { int tmp = c + 3; return tmp; }を、あえてラムダ式を使わずに書くと、
delegate(int c) { int tmp = c + 3; return tmp; }はい、どう見ても関数です。
よく見るとdelegateというキーワードが出現していますね。
匿名関数は通常、デリゲート(delegate)という特別な変数に入れて扱うものなのです。
デリゲートはいわば「関数の定義への参照」みたいなものだと筆者は思っておりますが、詳細が気になる方は各自ググってください!(丸投げ)まとめ
var result = a.Select(c => c + 3);C#のLINQをしっかり理解したい場合、
デリゲートを勉強して、匿名関数を勉強して、ラムダ式を勉強して、LINQを勉強する、
という手順を踏むのが正攻法ですかね。でも筆者のように「いきなり仕事でLINQのプログラムをやることになったゾ…」という人は、
この手順を知らないまま、検索ワード「LINQ 入門」とかでググってしまい、
ラムダ式って何?匿名関数って何?デリゲートって何?となってしまいがちな気がする…だって自分がそうだったから…。勉強は積み重ねが大切ってよく言うけど、何を積み重ねればいいのかを明確にするのも大切ですな。
参考文献
- Qiita記事
- 書籍
- 【省エネ対応】 C#プログラムの効率的な書き方(ISBN:978-4774149752)
- 投稿日:2020-03-10T18:41:36+09:00
【Unity】Listから条件に一致する要素をランダムに1個抽出する拡張メソッド
概要
LINQのWhereを極力使いたくない(Object allocationを極力避けたい)人向けです。
拡張メソッド
IEnumarableExtensions.csusing System; using System.Collections.Generic; using System.Linq; using Object = System.Object; using Random = UnityEngine.Random; public static class IEnumarableExtensions { private static readonly List<int> RandomIndexList = new List<int>(); private static readonly Object LockObject = new Object(); /// <summary> /// ieが空だと例外になるので要素の存在チェックを行ってから実行しましょう /// </summary> public static T GetAtRandom<T>(this IEnumerable<T> ie, Func<T,bool> predicate) { if (!ie.Any()) { throw new Exception("要素が空です!"); } // 非同期実行時に同時アクセスしないようlockします lock (LockObject) { RandomIndexList.Clear(); // 条件に一致する要素のindexを取得します for (int i = 0; i < ie.Count(); i++) { if (predicate(ie.ElementAt(i))) { RandomIndexList.Add(i); } } if (RandomIndexList.Count < 1) { throw new Exception("一致する要素がありません!"); } // 抽出したindexから抽選して返します int randomIndex = RandomIndexList[Random.Range(0, RandomIndexList.Count)]; return ie.ElementAt(randomIndex); } } }ビフォアー
void RandomBefore() { List<int> intList = new List<int> {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}; int[] randomPool = intList.Where(x => x % 3 == 0).ToArray(); for (int i = 0; i < 10; i++) { Debug.Log(randomPool[(Random.Range(0, randomPool.Length)]); } }アフター
void RandomAfter() { List<int> intList = new List<int> {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}; // ** Whereが要らなくなる!! ** for (int i = 0; i < 10; i++) { Debug.Log(intList.GetAtRandom(x => x % 3 == 0)); } }備考
タイトルには便宜的にListと書いてますが、IEnumerableを継承してれば使えます。
乱数の再現、シード値の固定をやりたい方は各自調整ください。追記
大抵の場合、IReadOnlyListで間に合い、こちらの方が早いと紹介頂いたので、こちらにも載せます。
IReadOnlyListExtensionsusing System; using System.Collections.Generic; using Object = System.Object; using Random = UnityEngine.Random; public static class IReadOnlyListExtensions { private static readonly int[] TempIndices = new int[1024]; private static readonly Object LockObject = new Object(); /// <summary> /// 候補が空のときはdefault値を返します /// </summary> public static T GetAtRandom<T>(this IReadOnlyList<T> ir, Func<T,bool> predicate) { lock (LockObject) { int count = 0; // 条件に一致する要素のindexを取得します for (int i = 0; i < ir.Count; i++) { if (predicate(ir[i])) { TempIndices[count] = i; count++; } } if (count == 0) { return default; } // 抽出したindexから抽選して返します int randomIndex = TempIndices[Random.Range(0, TempIndices.Length)]; return ir[randomIndex]; } } }
- 投稿日:2020-03-10T17:47:29+09:00
dataGridViewのチェックボックス型セルの値は、次に他のセルを選択した時に確定される。
dataGridViewのチェックボックス型セルをクリックして、trueになった瞬間(チェックマークが表示された瞬間)に他の動作を起こしたかったが、どうしても実装できなかった。
ネットで教えてもらったのは…
「チェックマークが表示されていも内部の値は変らない、次のセルを選択した際にそれが確定される。チェックマークが表示/非表示された瞬間に値(true/false)を確定させるには、DataGridView.CommitEdit を呼ぶ必要がある」ということだった。
あのチェックマークは見えてるだけなんや~
そんなん判らんでぇ…(TT)サンプルは以下の通り。
C#private void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e) { // 0番目がチェックボックス型の列として、これだけを処理対象とする if (dataGridView1.CurrentCell.ColumnIndex != 0) return; // 値を確定する!!! dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit); } private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e) { //ここに、すぐさま起こしたいコードを書く }
- 投稿日:2020-03-10T16:53:28+09:00
C#のLINQでよく見かけるメソッドについて復習だ。
はじめに
こんにちは。
突然ですが、C#のLINQでよく見かけると思うメソッドを3つ、頭に思い浮かべてみてください。
筆者はSelectメソッド, Whereメソッド, FirstOrDefaultメソッドだと思うのです。
ということで本記事ではこれら3つのメソッドについて簡単なサンプルをご紹介。Selectメソッド
やっぱり最初はSelectメソッドじゃない?
Selectメソッドの効果は加工や変形です。あえて数学っぽい言葉で言うなら射影か。using System; using System.Linq; class Program { static void Main() { int[] a = { 1, 2, 3, 4, 5 }; var result = a.Select(c => c + 3); foreach (var item in result) Console.WriteLine(item); Console.Read(); } }この例では、配列のアイテムひとつひとつに対して
c => c + 3してます。
ちなみにConsole.Read();とあるけど、これは結果画面を閉じずに待機したかっただけなので気にしないで。
実行結果は…
ちゃんと全部 3 が足されてます。Whereメソッド
Whereメソッドは条件に合うやつだけを絞り込む、つまりフィルタリングですね。
using System; using System.Linq; class Program { static void Main() { int[] a = { 1, 2, 3, 4, 5 }; var result = a.Where(c => c >= 3); foreach (var item in result) Console.WriteLine(item); Console.Read(); } }配列のアイテムから
c => c >= 3という条件を満たすものだけを取り出しています。
なんか形が似てるけど、=>はラムダ式の記号で、>=は不等号(≧)よね。
実行結果は…
3以上のアイテムだけが取り出されました。FirstOrDefaultメソッド
これも個人的にはよく見かけるメソッドだと思うのです。
ファーストまたはデフォルト。つまり最初のアイテムを返すけど該当アイテムがなかったら既定値を返すよという、とても賢い子。
該当アイテムがなかったら例外をぶん投げてしまうFirstメソッドよりたぶん便利。using System; using System.Linq; class Program { static void Main() { int[] a = { 1, 2, 3, 4, 5 }; var result = a.FirstOrDefault(c => c >= 3); Console.WriteLine(result); Console.Read(); } }実行結果は…
たしかにc => c >= 3という条件を満たすアイテムのうち、最初に見つかるのは 3 。
ちなみにもし見つからなかったら、今回はint型なので既定値 0 が出力されます。まとめ
Selectメソッドは加工、変形、射影。
Whereメソッドはフィルタリング。
FirstOrDefaultメソッドは最初のアイテムを取り出すけど見つからなかったら既定値。LINQのメソッドはたくさんあって全ては覚えられないけど、有名なところだけでもしっかり使い方をおさえておきたいですな。
今回紹介したメソッドのほかにはCountメソッド、OrderByメソッドあたりも頻出かな?
ちなみに本記事は筆者のQiita初投稿なので、見出しとか段落とかフォントとかでミスがあっても大目に見ていただきたい…!
- 投稿日:2020-03-10T16:44:41+09:00
Windows FormのDataGridViewに設置したチェックボックスをカチカチしたらがあべこべになった解決策
結論から言うと、ダブルクリックイベントも考慮する必要があった。
どんな処理をしようとしていたか
DataGridViewには下記のようにデータは表示される。
No Data □ 1 AAA □ 1-1 BBB □ 1-2 CCC □ 1-3 DDD □ 2 AAA □ 2-1 BBB 例として、No列の1のチェックボックスをクリックすると
1-1~1-3のチェックボックスにもチェックが付き、2~2-1にはチェックが付かないようにする。
こんな感じ。
No Data ■ 1 AAA ■ 1-1 BBB ■ 1-2 CCC ■ 1-3 DDD □ 2 AAA □ 2-1 BBB カチカチするとあべこべに
普通にポチッとクリックするだけなら問題なく動いたが、ダブルクリックみたいに間髪入れずにクリックするとこんな感じになる。
No Data ■ 1 AAA □ 1-1 BBB □ 1-2 CCC □ 1-3 DDD とか
No Data □ 1 AAA ■ 1-1 BBB ■ 1-2 CCC ■ 1-3 DDD になる。
こうなるともう無言で画面再表示。どう修正したか
CellContentDoubleClickイベントを追加して同じ処理を行うようにした。
private void DgvCellContentClick(object sender, DataGridViewCellEventArgs e) { // 大本のチェックボックスが選択されたか判定する if (dgv["1", e.RowIndex].Value.ToString() == "1") { // チェックボックスの状態を反映する dgv.CommitEdit(DataGridViewDataErrorContexts.Commit); // 関連するデータすべてのチェックボックスの状態を変更する if ((bool)dgv[e.ColumnIndex, e.RowIndex].EditedFormattedValue) { // チェックが付いた場合はチェックを付ける // 省略 } else { // チェックが外れた場合はチェックを外す // 省略 } } } private void DgvCellContentDoubleClick(object sender, DataGridViewCellEventArgs e) { // 全く同じ内容なので省略 }データの種別を判定したり何なりは省略。
カチカチッ(コンマ秒)だけではなく、カチ‥カチ(1秒くらい)でもあべこべになっていたのがこれで解決。
おわり。
- 投稿日:2020-03-10T16:44:41+09:00
Windows FormのDataGridViewに設置したチェックボックスをカチカチしたらがあべこべになったメモ
注意
実務上で起きた話です。
プロジェクトから離れたので、具体的な特効薬コードは手元にない。
具体的なメソッドやそのときの解決策は出るので許して。
何番煎じその2。どんな処理をしようとしていたか
DataGridViewには下記のようにデータは表示される。
No Data □ 1 AAA □ 1-1 BBB □ 1-2 CCC □ 1-3 DDD □ 2 AAA □ 2-1 BBB 例として、No列の1のチェックボックスをクリックすると
1-1~1-3のチェックボックスにもチェックが付き、2~2-1にはチェックが付かないようにする。
こんな感じ。
No Data ■ 1 AAA ■ 1-1 BBB ■ 1-2 CCC ■ 1-3 DDD □ 2 AAA □ 2-1 BBB カチカチするとあべこべに
普通にポチッとクリックするだけなら問題なく動いたが、ダブルクリックみたいに間髪入れずにクリックするとこんな感じになる。
No Data ■ 1 AAA □ 1-1 BBB □ 1-2 CCC □ 1-3 DDD とか
No Data □ 1 AAA ■ 1-1 BBB ■ 1-2 CCC ■ 1-3 DDD になる。
こうなるともう無言で画面再表示。どう修正したか
CellContentDoubleClickイベントを追加して同じ処理を行うようにした。
private void DgvCellContentClick(object sender, DataGridViewCellEventArgs e) { // 大本のチェックボックスが選択されたか判定する if (dgv["1", e.RowIndex].Value.ToString() == "1") { // チェックボックスの状態を反映する dgv.CommitEdit(DataGridViewDataErrorContexts.Commit); // 関連するデータすべてのチェックボックスの状態を変更する if ((bool)dgv[e.ColumnIndex, e.RowIndex].EditedFormattedValue) { // チェックが付いた場合はチェックを付ける // 省略 } else { // チェックが外れた場合はチェックを外す // 省略 } } } private void DgvCellContentDoubleClick(object sender, DataGridViewCellEventArgs e) { // 全く同じ内容なので省略 }データの種別を判定したり何なりは省略。
カチカチッ(コンマ秒)だけではなく、カチ‥カチ(1秒くらい)でもあべこべになっていたのがこれで解決。
おわり。
- 投稿日:2020-03-10T16:11:59+09:00
Windows FormでDataGridViewの描画処理が無限ループしたときの解決策
結論から言うと、別のイベントで処理するようにしたら解決した。
どういう画面で起きたのか
みんな大好きDataGridViewを使って、List内に格納されているデータを表示する画面。
DataGridViewに表示する際に、値によって罫線を描画したりしなかったりがあってちょっとややこしい表。
俗に言うセルを結合というやつ。見かけだけ。この罫線描画が今回の話の中心。
なぜ無限ループが起きた
- 原因
- CellPaintingのメソッド内で、罫線を描画しようとして無限ループが発生していた
やらかしたのはこういう感じのソース(※記憶とコピペ頼り)
private void DgvCellPainting(object sender, DataGridViewCellPaintingEventArgs e) { //セルの値が0ではないか判定 if (dgv.Rows[e.RowsIndex,e.ColumnIndex].Value != 0) { // 罫線を描画する int startX = dgv.RowHeadersVisible ? dgv.RowHeadersWidth : 0; int startY = e.RowBounds.Top + e.RowBounds.Height - 1; int endX = startX + dgv.Columns.GetColumnsWidth( DataGridViewElementStates.Visible) - dgv.HorizontalScrollingOffset; //線を引く e.Graphics.DrawLine(linePen, startX, startY, endX, startY); } else { // ~罫線は描画しない、代わりに別の処理略~ } // キーイベントを完了する e.Handled = true; }これだと無限ループが発生して画面の動きがもっさりしだした。
コンソール出力したら延々垂れ流しで焦った。調べてみた
マイクロソフトさん家では下記のように解説している
セルが描画されなければならないときに発生します。
参照元:https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.datagridview.cellpainting?view=netframework-4.8簡素。
11時間労働が続く日の中で調べ上げたのがこちら。
CellPaintingイベントは、DataGridViewに1mmでも触れれば発生して描画しにかかるとっても繊細なイベント(イベントメソッド内にコンソール仕込んで、触ってみるとわかると思う)
描画イベントが発生するタイミングは大体こんな感じ。
- 画面表示時
- 表示する値が1コでも変更(DataGridViewが保持している内部の値が書き換わったのを検知したとき、バインド未完了の状態)
- DataGridViewの見た目が変わる(セルの幅や高さが変わったときなど)
- ヘッダーにマウスカーソルが触れる(ちょっと曖昧、発火した気がする)
- セルをクリックしたり方向キーで選択する(見た目が変わるとかぶる)
- 再描画メソッド呼び出したあと
さらにCellPaintingメソッドは、引数のeからセルの行列数が取得できる。
これは左上(-1,0)から右下へ(XX,XX)とセル一つずつ描画していくため。
ちなみに-1行目はヘッダーで、0行目からデータ表示部になる。上記の内容から見ると・・・
- CellPaintingイベント発生
- 罫線の描画を行う
- 描画発生を検知する
- 1に戻る
こうなってしまうっぽい。
コンソール出力するとこういう感じで無限ループになる。コンソール出力例.// 描画開始 (-1,0) (-1,1) ~~~ (99,99) // ここですべてのセルを描画し終わった・・・ // と思いきや描画開始 (-1,0) (-1,1) ~~~ (99,99) // ここですべてのセルを描画し終わった・・・ // と思いきや無限ループすべてのセルに対して描画処理を行ったあとにまた1から描画し直すということになる。
どう修正したか
RowPrePaintイベントを使用して、CellPaintingイベントから切り離した。
例によってマイクロソフトさん家の解説が簡素なので省略、DOBON.NETさん家の解説を引用
RowPrePaintイベントは行が描画される前に発生
参照元:https://dobon.net/vb/dotnet/datagridview/rowpostpaint.htmlCellPaintingイベントはセル単位なのに対して、RowPostPaintイベントは行単位になるため引数のeでは行列は取得できない。
下記コードは特定の列まで行罫線なし、特定列以降最後まで行罫線を描画する
private void DgvRowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e) { // 線描画位置を計算 Pen linePen = Pens.DarkGray; int lineAllStartX = dgv.RowHeadersVisible ? dgv.RowHeadersWidth : 0; int lineStartX = 1; int lineStartY = e.RowBounds.Top - 1; int lineEndX = lineAllStartX + dgv.Columns.GetColumnsWidth(DataGridViewElementStates.Visible) - dgv.HorizontalScrollingOffset; foreach (DataGridViewColumn col in dgv.Columns) { // セル合計幅を取得 if (col.Index < 6) { lineStartX += col.Width; } } // 罫線を描画する if (dgv.Rows[e.RowIndex].Cells["XXX"].Value.ToString() == "XXX") { e.Graphics.DrawLine(linePen, lineAllStartX, lineStartY, lineEndX, lineStartY); } }これで無限ループはしなくなった。
余談
完全な解決ではなかったことを実装後すぐに気づいて泣きそうになった。
クリックバチバチしまくると罫線が消えたり出たりする。
CellPaintingイベント内でデフォルトで罫線を一旦消す処理をしていて、そっちが勝っちゃうことがあった。
クリックイベントとダブルクリックイベントに再描画メソッド付けて無理くり直した。
おわり。
- 投稿日:2020-03-10T16:11:59+09:00
Windows FormでDataGridViewの描画処理が無限ループしてエラいことになったメモ
注意
実務上で起きた話です。
プロジェクトから離れたので、具体的な特効薬コードは手元にない。
具体的なメソッドやそのときの解決策は出るので許して。
何番煎じ。どういう画面で起きたのか
みんな大好きDataGridViewを使って、List内に格納されているデータを表示する画面。
DataGridViewに表示する際に、値によって罫線を描画したりしなかったりがあってちょっとややこしい表。
俗に言うセルを結合というやつ。見かけだけ。この罫線描画が今回の話の中心。
なぜ無限ループが起きた
- 原因
- CellPaintingのメソッド内で、罫線を描画しようとして無限ループが発生していた
やらかしたのはこういう感じのソース(※記憶とコピペ頼り)
private void DgvCellPainting(object sender, DataGridViewCellPaintingEventArgs e) { //セルの値が0ではないか判定 if (dgv.Rows[e.RowsIndex,e.ColumnIndex].Value != 0) { // 罫線を描画する int startX = dgv.RowHeadersVisible ? dgv.RowHeadersWidth : 0; int startY = e.RowBounds.Top + e.RowBounds.Height - 1; int endX = startX + dgv.Columns.GetColumnsWidth( DataGridViewElementStates.Visible) - dgv.HorizontalScrollingOffset; //線を引く e.Graphics.DrawLine(linePen, startX, startY, endX, startY); } else { // ~罫線は描画しない、代わりに別の処理略~ } // キーイベントを完了する e.Handled = true; }これだと無限ループが発生して画面の動きがもっさりしだした。
コンソール出力したら延々垂れ流しで焦った。調べてみた
マイクロソフトさん家では下記のように解説している
セルが描画されなければならないときに発生します。
参照元:https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.datagridview.cellpainting?view=netframework-4.8簡素。
11時間労働が続く日の中で調べ上げたのがこちら。
CellPaintingイベントは、DataGridViewに1mmでも触れれば発生して描画しにかかるとっても繊細なイベント(イベントメソッド内にコンソール仕込んで、触ってみるとわかると思う)
描画イベントが発生するタイミングは大体こんな感じ。
- 画面表示時
- 表示する値が1コでも変更(DataGridViewが保持している内部の値が書き換わったのを検知したとき、バインド未完了の状態)
- DataGridViewの見た目が変わる(セルの幅や高さが変わったときなど)
- ヘッダーにマウスカーソルが触れる(ちょっと曖昧、発火した気がする)
- セルをクリックしたり方向キーで選択する(見た目が変わるとかぶる)
- 再描画メソッド呼び出したあと
さらにCellPaintingメソッドは、引数のeからセルの行列数が取得できる。
これは左上(-1,0)から右下へ(XX,XX)とセル一つずつ描画していくため。
ちなみに-1行目はヘッダーで、0行目からデータ表示部になる。上記の内容から見ると・・・
- CellPaintingイベント発生
- 罫線の描画を行う
- 描画発生を検知する
- 1に戻る
こうなってしまうっぽい。
コンソール出力するとこういう感じで無限ループになる。コンソール出力例.// 描画開始 (-1,0) (-1,1) ~~~ (99,99) // ここですべてのセルを描画し終わった・・・ // と思いきや描画開始 (-1,0) (-1,1) ~~~ (99,99) // ここですべてのセルを描画し終わった・・・ // と思いきや無限ループすべてのセルに対して描画処理を行ったあとにまた1から描画し直すということになる。
どう修正したか
RowPrePaintイベントを使用して、CellPaintingイベントから切り離した。
例によってマイクロソフトさん家の解説が簡素なので省略、DOBON.NETさん家の解説を引用
RowPrePaintイベントは行が描画される前に発生
参照元:https://dobon.net/vb/dotnet/datagridview/rowpostpaint.htmlCellPaintingイベントはセル単位なのに対して、RowPostPaintイベントは行単位になるため引数のeでは行列は取得できない。
下記コードは特定の列まで行罫線なし、特定列以降最後まで行罫線を描画する
private void DgvRowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e) { // 線描画位置を計算 Pen linePen = Pens.DarkGray; int lineAllStartX = dgv.RowHeadersVisible ? dgv.RowHeadersWidth : 0; int lineStartX = 1; int lineStartY = e.RowBounds.Top - 1; int lineEndX = lineAllStartX + dgv.Columns.GetColumnsWidth(DataGridViewElementStates.Visible) - dgv.HorizontalScrollingOffset; foreach (DataGridViewColumn col in dgv.Columns) { // セル合計幅を取得 if (col.Index < 6) { lineStartX += col.Width; } } // 罫線を描画する if (dgv.Rows[e.RowIndex].Cells["XXX"].Value.ToString() == "XXX") { e.Graphics.DrawLine(linePen, lineAllStartX, lineStartY, lineEndX, lineStartY); } }これで無限ループはしなくなった。
余談
完全な解決ではなかったことを実装後すぐに気づいて泣きそうになった。
クリックバチバチしまくると罫線が消えたり出たりする。
CellPaintingイベント内でデフォルトで罫線を一旦消す処理をしていて、そっちが勝っちゃうことがあった。
クリックイベントとダブルクリックイベントに再描画メソッド付けて無理くり直した。
おわり。
- 投稿日:2020-03-10T15:05:04+09:00
プログラマー初学の人へ61の質問に答えてみた
はじめに
プログラマー初学の人へ61の質問
言語はC#(とC++)を前提に回答していきます。
ちなみに経験1年未満の新人です。1. 変数と定数の違いは何ですか?
どちらもメモリ領域に名前をつけたものです。
変数は再代入が可能。
定数は再代入が不可能。C++だと定数はさらにconstとconstexprに分けられます。
const : 初期化後に値を変更できない変数でRAMのみに配置されます
constexpr : コンパイル時にインライン展開される定数でRAMまたはROMに配置されます
ということでconstは厳密には変数だったりします。あとHaskellなんかは変数が存在しません。
2. 変数と定数はどのように使い分けますか?
後から変更されないものはすべて定数で宣言しましょう。
こうすることで値が変化しないことを明示できるので処理を追いやすくなります。
JavaSctiptではconstを積極的に使うみたいな話です。3. コメントの書き方はわかりますか?
文法的には
// コメントと/* コメント */で書けます。
でもマルチラインコメントの方はどこからどこまでがコメント行なのか見難いので忘れていいでしょう。どんなコメントを残すかは宗教戦争。基本方針はリーダブルコードとかに則って書きます。
4. 四則演算はどのように行いますか?
文法的には
+-*/
これらは組み込み型へ定義された演算子です。自分で演算子をオーバーロードすることも可能です。
5. 演算子、論理式、比較子って何ですか?
- 演算子 オペランド間の演算を定義した記号。4の四則演算やnull演算の?、否定の!など。
- 論理式 AND/OR演算のこと。もしくはBoolean。
- 比較子 比較演算子。オペランドの比較を行う。
==, !=, >, <これら。6. 三項演算子って何ですか?
Boolean?"true":"false"という記法で書ける条件分岐の記法です。
if(Boolean) return "true"; return "false";これと同じです。
7. Integer型とFloat(Double)型の違いは何ですか?
まずJavaの話になりますがint型 != Integer型です。値型とラッパーの参照型という違い。C系列にはないのでint32型とfloat型の違いとして進めます。
どちらも値型でintは整数、floatは浮動小数点を格納できます。
確保するメモリサイズは実装次第だけどintが4バイト、floatが8バイト。
そしてfloatは基本的に使わずdoubleを使います。大抵のハードウェアはfloatを演算するときに内部でdoubleにキャストしてから計算して後で戻しています。
組み込みの特殊な状況ではfloatを使うこともあるらしいです。
--20/03/11 追記
AVX命令だとfloat演算はdoubleより数倍速くなるとか。
あとコンパイラが判断してfloatをdoubleに置き換える最適化を行うこともあるようです。
float型とdouble型
いずれにせよシビアなチューニングを行う場面でない限りdoubleを使ってよさそうです。8. 分岐処理の構文を教えてください。
if、switch、三項演算子、null演算子。関数型だとパターンマッチもあります。
9. ループ構文を教えてください。
for(変数宣言;終了条件;繰り返し演算) while(終了条件) foreach(変数宣言 in コレクション)
do-whileは忘れていいでしょう。C#だとLINQというコレクション(
IEnumarable)の各要素に対して行うメソッド群があります。これもループ。そしてすべてのループは再帰関数のシンタックスシュガーです。
10. 配列の構文を教えてください。
<型>[要素数]またはList<T>。ArrayListは忘れていいでしょう。C#のListは片方向連結リストで実装されており、InsertやRemoveメソッドの計算量はO(N)です。つまり可変長配列です。
List<T> クラス
InsertやRemoveを多用する場合は双方向連結リストのLinkedList<T>を使用する方がパフォーマンスの向上が望めます。
LinkedList<T> クラス11. ハッシュ(辞書)の構文を教えてください。
KeyValuePair<TKey,TValue>とそのコレクションであるDictionary<TKey,TValue>。C#もmapでいいのに。
Dictionary<TKey,TValue> クラス12. 関数(メソッド)の構文を教えてください。
アクセス修飾子 static修飾子 戻り値の型 関数名(引数) { 関数本体(具体的な処理) }これに継承だと
:がついたりジェネリックだとWhereでTを指定したり増えていきます。13. 関数(メソッド)を使用する利点を教えてください。
むしろ使用しないデメリットを答えた方が早いというか、使わないとまともなプログラムを組めません。
14. 標準出力の方法を教えてください。
標準出力って結構な闇では?
標準入力・標準出力ってなに?
Console.WriteLine("Hello")。15. 文字列の連結、検索方法を教えてください。
まずC#のString型はimmutable。なので
+などで結合した場合は新しい文字列オブジェクトが生成されます。そのうえでvar greeting = "Hello" + ", " + "World.";もしくはStringBuilderを使って
var greeting = new StringBuilder().Append("Hello").Append(", ").Apped("World.").ToString();違いはStringBuilderはmutableなこと。
文字の検索(というか比較)はJavaとC#でちょっと違います。
str.equal("Hello")とstr == "Hello"の違い。
Javaは前者でC#はどっちでもいいです。普通は後者。これはC#の言語仕様でString同士の==がオーバーロードされているため。参照型同士の==比較はオブジェクト自体が同一かを判定します。16. クラスの構文を教えてください。
アクセス修飾子 class クラス名 { 中身 }アクセス修飾子を省略するとinternalになります。
17. インスタンス変数とは何ですか?
オブジェクトのインスタンスに対して定義された変数です。
18. クラス変数とインスタンス変数の違いは何ですか?
staticおじさんがやってくるぞ。
確保されるメモリ領域が異なります。クラス変数はスタックスタティック領域でインスタンス変数はヒープ領域。というよりヒープ領域にメモリを確保することがインスタンス化。
19. スタティック領域とは何ですか?
そんなものはない。
あるのはスタック領域。-- 20/03/11 追記
スタック領域(自動記憶域)とスタティック領域(静的記憶域)は別物です。
プログラムの起動時から終了時まで一貫して割り当てられる領域をスタティック領域と呼びます。
グローバル変数やstaticローカル変数、staticクラスやstatic関数を使用すると割り当てられます。
記憶クラスとスコープこの人のブログはハードウェア周りについてだいぶ勉強になります。
ヒープとスタック20. クラスの継承構文を教えてください。
class クラス名 : 継承するクラス名(もしくはインターフェース名)21. 型キャストはできますか?
C++のキャストは闇なので触れません。
C#はint -> longなどデータの情報量が保証される場合は暗黙キャストが可能です。
その他の場合はキャストするメソッドを呼び出します。
int.Parse("1")22. 型チェックの構文を教えてください。
typeofまたはis演算子。C# の is 演算子と typeof の型判定の挙動の違い
23. 関数(メソッド)の初期値って設定できますか?
ちょっと何いってるかわからないですね。
引数の初期値設定ならint Sum(int x = 0, int y = 0)のような記法で可能です。24. インターフェース(objective-cやswiftではプロトコル)とは何ですか?
クラスに対して、あるメソッドを実装することを約束するものです。
25. インターフェース(objective-cやswiftではプロトコル)はどのような局面で使用しますか?
ポリモーフィズムを用いて処理を各クラスに記述したいときなどに使います。
26. クラスの継承をするメリットは何ですか?
具体クラスを継承することはデメリットが大きいです。
基本はインターフェース(抽象)に対して処理を行いましょう。
abstractクラスの継承はインスタンス変数を実装したいときに使います。Go言語なんかはクラスの継承がないですね。モダンな言語だとインターフェースの実装はあってもクラス継承はなくなってきています。
27. 自分自身のクラスを示すキーワードは何ですか?
this。superは親クラスを指します。
加えるとthisが指すのは自身のインスタンスです。28. ポリモルフィズムとは何ですか?
ポリモーフィズムって書いてないとゾワゾワします。
ある要素が複数の型に属することを指します。29. ダックタイピングとポリモルフィズムの違いを教えてください。
あらかじめインターフェースを定義せずに多態性を利用するのがダックタイピングです。動的型付け言語の特徴のひとつですね。
ポリモーフィズムを利用する場合は事前にフィールドやメソッドの定義を行う必要があります。30. カプセル化のメリットは何ですか?
複数のデータや操作をひと塊として扱えるようになります。
情報の隠匿とかよりこれが重要です。31. アクセス演算子って何ですか?
アクセス修飾子。
カプセル化を実現するための仕組みのひとつで、要素へのアクセスレベルを制限します。
C#ではデフォルトでinternalです。Access Modifiers (C# Reference)
32. タプルとはなんですか?
値の組です。C#7からは無名タプルがサポートされました。
無名タプル
使うには.NET Framework 4.7以上が必要です。使えない場合はTuple型を使いましょう。
Tupleクラス33. クラスを使うメリットは何ですか?
カプセル化ができます。
34. 例外処理はどうやって書きますか?
try { hoge.ExceptionMethod(); } catch(HogeException ex) { }
catch(Exception e)とかcatch{}とかはやめましょう。あとスタックトレースを消さないように気を付けましょう。35. バージョン管理システム(SCM)とは何ですか?
Gitのこと。SVN?知らない子ですね。
ファイルの変更履歴を管理できるシステムです。
36. バージョン管理システム(SCM)は何を使ってますか?
GitHubとGitBucketを使っています。
37. スクリプト言語とコンパイル言語の違いは何ですか?
スクリプト言語は簡単に読み書きできる言語の総称で、コンパイル型言語と比較するならインタプリタ型言語でしょう。
あらかじめコンパイラを通して機械語のソースに変換しておくのがコンパイル型言語。
実行時にインタプリタを通して逐次翻訳していくのがインタプリタ型言語。コンパイル型は動作が高速、インタプリタ型は修正が比較的容易です。
38. データベースって何ですか?
何かの定義に従って整理された情報の集合のことです。
おそらく広くイメージするのはRDBMS。これは複数の属性をセットとして情報を扱う形式のデータベースです。SQL ServerとかMySQLとか。
対してKey-Valueのセットで保存しテーブルを持たないNoSQLというものもあります。ただし全部がKVS方式ではないですが。
39. トランザクション管理って何ですか?
一連の処理をひと塊として扱うことです。処理の単位をトランザクションといいます。
トランザクションの途中で処理に失敗したときに、その一処理だけでなくトランザクションごとロールバックされます。40. スレッドって何ですか?
まず実行中のプログラムがプロセスです。プロセスは処理を行うために1個以上のスレッドを内部に持っています。このスレッドがCPUコアに対して命令を送って処理を行います。
ということで、スレッドはプロセス内部の命令を実行する部分です。
【図解】CPUのコアとスレッドとプロセスの違い・関係性、同時マルチスレッディング、コンテキストスイッチについて
41. プリミティブ型とオブジェクト型って何ですか?
組み込み型と参照型のこと。言語仕様として実装しているのが組み込み型。
でもStringは組み込み型だけど参照型だって?
値型と組み込み型を混同している説。値型の変数はスタック領域に確保されます。
参照型の変数はヒープ領域に確保されます。例えばintは値型の組み込み型で、Stringは参照型の組み込み型です。構造体は値型のユーザ定義型です。
42. 無名関数/匿名関数って何ですか?
関数名を宣言をせずに使える関数です。
(int x) => { hoge.Method(); }ラムダ式は匿名関数です。
C#開発者が「ラムダ式が最初からあれば、匿名メソッド式の構文はC#には不要だった」といっているので、まあラムダ式が使えればいいんじゃないでしょうか。43. メタプログラミングって何ですか?
プログラムを扱うプログラムを書くこと。
あるいはコードを生成するコードを書くこと。DB情報を読み取って対応するDTOを自動生成するのなんかがこれなんでしょうか。
44. Optionalはどのようなケースで使用しますか?(java/Swift)
null安全のお話ですね。
null安全でない言語は、もはやレガシー言語だC#8からnull安全が導入されました。機会があったら使ってみたいですね。
45. リフレクションとは何ですか?
クラス名やメソッド名といったメタデータをプログラム実行時に取り出すための機能です。
C#だとAttributeやprivateメソッドのテストなんかで関わってきます。ちなみに果てしなく遅いです。
46. クラス拡張はできますか?
C#では拡張メソッドがあります。
47. インスタンス変数にメソッドを追加できますか?
C#ではできません。
48. アノテーションはどのように使用しますか?
Javaだと
@Overrideとか書きますね。
C#だとAttributeです。
Enum型に付けてメソッド拡張とかで使うこともできますが遅いのでやったことないです。49. DIコンテナとは何ですか?
まずDependency Injectionから。邦訳するとオブジェクトの注入で、要は移譲では?とか考えています。
ある処理をするためのインスタンスをフィールドに持っておき、処理を行うときはそのインスタンスを通じて実行します。これを実現するためのツール(あるいはフレームワーク)がDIコンテナです。
50. ガベージコレクションとはなんですか?
参照されなくなったメモリ領域を解放するための仕組みです。
C++などはメモリを手動で管理していましたが、C#などではメモリの解放を自動で行います。C#ではマーク&スイープ方式と3世代の世代別GCを行っています。
51. ガベージコレクションを使うとなんのメリットがありますか?
プログラマがメモリを意識することが減ります。
逆にいえばメモリを意識したチューニングをするときにはGCが邪魔になることがあります。52. ARCとは何ですか?
Automatic Reference Counting、参照の自動追跡でGCの一種です。
インスタンスへの参照をカウントしておき、0になったらメモリを解放します。53. Weakキーワードはどのような局面で使用しますか?
ARCでカウントする参照は強参照と呼ばれています。これに対してARCでカウントされない参照を弱参照と呼び、
Weakを使って変数宣言して使います。強参照同士でインスタンスを結びつけるとメモリが解放されなくなるため、弱参照を使ってGCを行います。
54. スタック領域とヒープ領域の違いは何ですか?
スタック領域はOSやコンパイラが割り当てと解放を行い、サイズはコンパイル時に定まります。
ヒープ領域はアプリケーションが割り当てと解放を行い、サイズは動的に指定します。55. 横断的処理の記述方法を教えてください。
おそらくアスペクト指向プログラミングのことでしょう。
オブジェクトとして分離できないLoggerなどをアスペクトとして扱い、アスペクト記述言語を使って分離することで柔軟なプログラムにすることです。AOP(Aspect Oriented Programming) の解説
56. クロージャって何ですか?
JavaScriptで出てくるやつ。C#でも使えます。
Func<int, Func<int>> method = x => { return () => ++x; }; var increment = method(100); Console.WriteLine(increment()); // => 101 Console.WriteLine(increment()); // => 102methodがクロージャです。このときのint型変数xをレキシカル変数と呼びます。これはスコープの限定された静的な変数です。
incrementに代入した時点でmethodの変数xに100がバインドされ、その後incrementを呼び出すときはバインドされた変数を参照します。関数型の考え方に踏み込んでいくので、なかなか理解が難しい部分かもしれません。
57. 高階関数って何ですか?
関数を引数や戻り値とする関数のことです。
C#ではデリゲート型オブジェクトにメソッドを格納することができ、デリゲート型を引数や戻り値とすることで高階関数を実現しています。LINQも高階関数を使っています。
58. ブロック構文って何ですか?
Rubyの
do-end構文のことらしいです。hoges.each do |hoge| hoge.method() endこんな感じで使いますね。具体的に何をやってるかはイマイチ分かりませんでした。
Rubyをやっていてブロック処理(do 〜 end)がちゃんとわかってない人は、怒らないから見ていきなさい
59. 変数に関数を設定してください。
デリゲート型を使ってください。
Func<>が戻り値があるデリゲート型、Action<>が戻り値のないデリゲート型です。60. 部分適用とカリー化の違いは何ですか?
どちらもラムダ式の引数を減らすものです。
クロージャに対して仮引数を定めているものが部分適用、減らした後の引数が1個だけのものに対して行うのがカリー化です。つまりカリー化は部分適用の一種です。クロージャの項も参考にしてください。
61. モナドって何ですか?
モナドは単なる自己関手の圏におけるモノイド対象です。何も問題ないですね。
- 投稿日:2020-03-10T14:10:36+09:00
paizaさん家でC#の腕試しをした所感(D~Cランクまで)
書いた時点では多少の修正や実装はできるけど、技術レベルは初心者に毛が生えた程度の難しいお年頃・・・。
そういうのがやってみた所感を書く。Dランクの問題をやってみた
1 + 1は2から配列の中身を取得してちょっといじくって出力するような問題が多い
※stringやcharをintに型変換する一手間は必要簡単なものだと、Console内に直書きしてハイおわり。
実務だと色々想定して処理を書いていくと思うけど、そんな難しいことは考えずに見たまんまで大丈夫だった。
たとえばこんな問題(※非実在)
- 問題文
- 1袋毎に10粒のチョコレートが入っている商品があります。
合計で何袋買ったか入力されるので、チョコレートが全部で何粒あるか出力してください。
入力値
3書いてみたコード.public static void Main() { var input_line = System.Console.ReadLine(); System.Console.WriteLine(int.Parse(input_line) * 10); }出力結果
30型の扱い、計算の順番ができれば大体問題ないけど、中にはIF文を使わないと解けないのもあったりする(ヒントは正解率)
標準入力が難易度高いけど、新卒の人にやらせるにはちょうどいいし、熟練者には格好のタイムアタックステージになりそうだと思った。
Cランクの問題をやってみた
難易度がぐっと上がってびっくり、ここからfor、switch、if~else文が登場。
入力値が2回以上に分けて入るパターンもある。問題文がややこしくなってきて、コード記述と問題文確認で往復する時間と量が増えた。
たとえばこんな問題(※以下略)
- 問題文
- 入力値に対して別の文字に置き換えて出力してください。
置き換え前と置き換え後の対応表は下記の通りになります。
置き換え前 置き換え後 D P O I N Z U Z T A 入力値
DDDONUTS!書いてみたコード.var input_line = System.Console.ReadLine(); for(int i = 0; i < input_line.Length; i++) { switch(input_line[i]) { case 'D': System.Console.Write("P"); break; case 'O': System.Console.Write("I"); break; case 'N': System.Console.Write("Z"); break; case 'U': System.Console.Write("Z"); break; case 'T': System.Console.Write("A"); break; default: System.Console.Write(input_line[i]); break; } }出力結果
PPPIZZAS!こんな感じで対応表にはない文字に対してどうするかの記述も必要になる。
正解率が30パーセント台もあって関門感ある。
難易度設定ミスってるのではと疑ったりあと、下記のようなトラップが仕掛けられていることもあって満点狙うのが難しくなってきた。
- 入力例だけで提出前動作確認すると、2ケースくらい失敗する
- ソートや重複チェックを使うのでは・・・?な問題もある(解けなかったので不明)
動作確認でミス連発したり、悩みに悩んで時間切れになったり、提出したら失敗ケースが出て減点されたり、ムキーッてなる回数が増えた。
まとめ
Dランクでイキってた人を叩き潰しにくるCランクさんパネェ。
初心者産毛マンより。細かいことはまろさん家に流すスタイル
https://maro28.com/paiza-skill-check-dc
- 投稿日:2020-03-10T10:20:06+09:00
C# EnumのString変換 速度比較
enumをstringにする際、いろんな方法があります。
- Enum.GetName
- ToString
- nameof
これらに速度の違いはあるのか?速度比較してみました。
結論
nameofが圧倒的に爆速
検証環境
- macOS 10.14.6
- Visual Studio for Mac
- .NET Framework 4.7.2
Xamarin.Macで利用しているライブラリなので、.NET Frameworkを使っています。
.NET Coreであれば結果も変わってくると思います。時間があればやってみようと思います。検証コード
class MainClass { public static void Main(string[] args) { Console.WriteLine("--------------------------------------"); ExecuteTest(350000); } private static void ExecuteTest(int iterations) { var type = "small"; string s = string.Empty; try { var sw = new Stopwatch(); var testData = new List<SmallEnum> { SmallEnum.Small, SmallEnum.Larger, SmallEnum.Small, SmallEnum.Small, SmallEnum.Larger }; var toStringTimings = new List<float>(); var getNameTimings = new List<float>(); var nameOfTimings = new List<float>(); for (int i = 0; i < iterations; i++) { for (int dataIndex = 0; dataIndex < testData.Count; dataIndex++) { var item = testData[dataIndex]; sw.Start(); s = item.ToString(); sw.Stop(); toStringTimings.Add(sw.ElapsedTicks); sw.Restart(); s = Enum.GetName(item.GetType(), item); sw.Stop(); getNameTimings.Add(sw.ElapsedTicks); sw.Restart(); s = nameof(item); sw.Stop(); nameOfTimings.Add(sw.ElapsedTicks); sw.Reset(); } } Console.WriteLine($"{type}ToString\tDataCount\t2\tIterations:\t{iterations}\tAverage:\t{toStringTimings.Sum() / toStringTimings.Count():0.000}\tticks"); Console.WriteLine($"{type}GetName\tDataCount\t2\tIterations:\t{iterations}\tAverage:\t{getNameTimings.Sum()/getNameTimings.Count():0.000}\tticks"); Console.WriteLine($"{type}nameOf\tDataCount\t2\tIterations:\t{iterations}\tAverage:\t{nameOfTimings.Sum()/nameOfTimings.Count():0.000}\tticks"); } catch (Exception ex) { Console.WriteLine($"{type}Fail\tDataCount\t2\tIterations:\t{iterations}\tFailed\t{ex.Message}"); } } }上から順に、
- ToString
- Enum.GetName
- nameof
を変換し、引数として渡した数分ループを回してテストしています。
結果
smallToString DataCount 2 Iterations: 350000 Average: 15.523 ticks smallGetName DataCount 2 Iterations: 350000 Average: 16.810 ticks smallnameOf DataCount 2 Iterations: 350000 Average: 0.464 ticks本当にコードがあっているのか?と思うくらい圧倒的にnameofが早いです。もし間違っていたら教えて下さいm(_ _)m
EnumをStringにしたければnameofを使いましょう。
差としては微々たるものですが、こういう所が積もり積もって地味に速度低下に結びついています。
大規模リファクタと違ってお手軽に導入できるのでぜひやってみて下さいね〜
- 投稿日:2020-03-10T01:17:16+09:00
UnityでVisual Studioの自動補完/候補が出ない
経緯
新しいPCでUnityを始めた際に、VisualStudioで予想変換/自動補完(インテリセンス)が出なくて困っていました。今まで使っていた環境では「g」と入力すれば、「GameObject」が候補に出たのに……
症状
こちらの記事 を参照しましたが
そもそもソリューションウインドウ上でも更新マーク()も表示されず。
原因
Unity側のC#のエディタがVisualStudioになっておらず
C#スクリプトがUnityのProjectと紐づいていませんでした。
ソリューションファイル(.sln)も作成されていませんでした。対応
Unity側のC#のエディタをVisualStudioに設定します。
結果
無事にインテリセンス(自動補完/候補)が表示されるようになりました。
以上です。
お疲れ様でした。参考
- 投稿日:2020-03-10T00:55:35+09:00
ASP.NET CoreでWeb APIのパスにバージョン情報を入れてみた
Web APIを作成することになりました。「とりあえず動くもので良い」という指示が、とても不安です。絶対、仕様変更がたくさん入ります。
というわけで、よくあるAPIバージョンをパスに入れてみることを検討することにしました。参考にしたのは次のURLです。
Qiitaに近い
http://qiita.com/api/v2/な感じのURLを目指すことにします。ASP.NET CoreでWeb APIのパスにバージョン情報を入れるには
実は詳細なサンプルがあるので、これに倣います。
- Versioning via the URL PathWeb APIプロジェクトを作成する
ASP.NET Core Web アプリケーション > APIで、サンプルを含んだプロジェクトが作成されます。
実行すると、
https://localhost:44376/weatherforecastをブラウザで起動します。これを変更するには、プロジェクトのプロパティ > デバッグ > 「ブラウザーの起動」の値を設定することで実現できます。プロジェクトにAPI Versioning拡張を追加する
おもむろに
services.AddApiVersioning();をStartup.csのConfigureServicesに追加します。// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddApiVersioning(); services.AddControllers(); }パス(Route)を書き換える
WeatherForecastController.csのWeatherForecastControllerクラスのパス(Route)を書き換えます。[Route("api/v{version:apiVersion}/[controller]")] public class WeatherForecastController : ControllerBase { ... }バージョン番号を設定する
Qiitaのバージョン番号は整数のようですが、好みとしては小数点が入ってる方です。そこでとりあえず、
1.0を設定してみます。[ApiVersion("1.0")] [Route("api/v{version:apiVersion}/[controller]")] public class WeatherForecastController : ControllerBase { ... }ここで実行してみると、
https://localhost:44376/weatherforecast(ポート番号は違うかもしれませんが)をブラウザで起動し、404エラーになります。が、構わずhttps://localhost:44376/api/v1.0/weatherforecastに接続してみると、正しくJSONデータが表示されるようになります。マイナーバージョンを追加する
バージョン
1.1を追加してみます。[ApiVersion("1.0")] [ApiVersion("1.1")] [Route("api/v{version:apiVersion}/[controller]")] public class WeatherForecastController : ControllerBase { ... }当然、同じメソッド名は使えないので、異なるメソッド名を作成し、
1.1をマッピングするようです。結果が分かりやすいようRange(1, 5)をRange(1, 1)に変更してみました。[HttpGet, MapToApiVersion("1.1")] public IEnumerable<WeatherForecast> GetV11() { var rng = new Random(); return Enumerable.Range(1, 1).Select(index => new WeatherForecast { Date = DateTime.Now.AddDays(index), TemperatureC = rng.Next(-20, 55), Summary = Summaries[rng.Next(Summaries.Length)] }) .ToArray(); }で、
https://localhost:44376/api/v1.1/weatherforecastにアクセスしてみると、先ほどは5データ返されていましたが、1データのみ返されるようになりました。成功、ですね。
雑感
あまりに簡単でびっくりしました。
Microsoftさんすごい。




