ぬるりこわい
よく見たらなんともなかったのだけどぱっと見はnull怖いってこと。
↓のコード
class nanka { public int value { get; set; } public void proc() { //なんかする } } class myclass { private nanka nanka_; //いっぱいメソッドある //このproc3はいろんなところから呼び出されているが private void proc3() { nanka_.proc(); } }
proc3で使用しているnanka_が初期化されている(=nullでない)確証を得るべくproc3の呼び出し経路を全部逆回しで確認するはめに。
ぬるり(他言語ではぬるぽ)怖いんだからー。
バグ直した。
バグ修正の依頼があったのでコネコネとさばく。
今日のバグはこんなの。
「文字列をnバイトで切断する処理にて全角文字の真ん中をぶったぎってしまう」
"ABCDEあい"を8バイトで切る処理にて"ABCDEあ"ここまでで7バイト。"い"をつけると9バイトになるのでやめて半角スペース補充で終了させておくのが本来の要求だが、”い"の文字コードの前半部分がまでくっつけてくれているというバグ。
データベースの都合で文字列をバイト単位で切断する必要があるのですが、弊現場では極めてまれな扱いのために当初の開発担当者は慣れておらずにバグを出してしまったもよう。5年ほどバレなかったためにバグ出した当人は逃げおおせたが後任のおいらが対応することになりました。
こういうしょうもないバグは修正が楽しい。修正するメソッドについて以下のユニットテストを書く。
・切断対象文字列がちょうどnバイト("ABCDEFGH")
・切断対象文字列がnバイト未満("ABCDEFG")
・切断対象文字列が空文字("")
・切断対象文字列がnバイト超かつ切断箇所が全角文字の真ん中ではない("ABCDEFGHI")
・切断対象文字列がnバイト超かつ切断箇所が全角文字の真ん中("ABCDEFGHあ")
で、ユニットテストを実行して最後のだけ×になることを確認してメソッドをいじくりまわしつつユニットテストを実行。全部〇になればOK。すばらしい。ユニットテスト万歳。
追記 元のコードこんな感じ。
private static string NewMethod1(string org,int n)
{
var enc = System.Text.Encoding.GetEncoding("Shift_JIS");
var a = enc.GetBytes(org).Take(n).ToArray();
return enc.GetString(a);
}
あ、サロゲートペアとか言いっこなしで。
ちょっと吐き出したいだけ
弊現場では型付きDataSetを使っております。
DBからデータを読み込む処理を実行すると、読み込まれたデータが型付きDataSet、型付きDataTableに入ります。
あとは型付きDataTableのrowsから必要なデータを取得するだけ。
素直です。型付きなのでIntellisenseが効きます。楽です。ありがたいです。
ここまではたぶんごくごく普通のことなのでしょう。ここからが弊現場ならではのこと。
弊現場の型付きDataTableの全カラムがstring型になっているのです。DB上の型が何であれ読み込まれた時点でstringになってしまうのです。DB設計時に型を決めたはずなのに読み込みで全部無視。読み込んで使うに当たっては自分で変換をかける必要ががが。
rowを読み込んだあとにやたらとConvert.To*があるのが正直うっとうしい。何の目的があってこんなことしたのだ?難読化か?
まぢうっとうしい。
新規開発でない現場は前任者が作ったコードの出来に左右される。
おいらもけっして出来のいいコーダーではないんですが、可能な限り後続に迷惑かけたくないなと。
VisualStudio2010→VisualStudio2017
(以下VisualStudioはVSと略す)
手元にあるVS2010のソリューションをVS2017で開いてみた。
世代はだいぶ違うが下位互換ぐらいはあるだろうとおもったらビルドが通らない。
原因を探りに探ったら、識別子に全角の[・]を使っていたところでエラーCS1056が出ているとのこと。
コード分析かけてCS1056出ている箇所をVS2010に戻って[リファクター]→[名前の変更]で名前を変更していこう。
ただちょっと気になるのは該当箇所がプロパティ名だった場合。
①双方向Bindingでプロパティ名を文字列で指定するコードがあるがこれは追従してくれないので別途文字列検索が必要。
②VSのデザイン画面でBindingの設定をしている場合、追従してくれるのだろうか?(未確認)
廃棄予定PCの棚卸
部屋隅に寝転がってたPCを棚卸した。
お名前 | CPU | Socket | コア数 | クロック(GHz) | メモリ(GB) | メモリスロット | 光学ドライブ | 備考 |
AS3810T-P22F | Core2Duo SU9400 | - | 2 | 1.4 | 2 | DDR3 SODIMM | なし | ノートPC |
idid i345 | Core2Duo T5600 | Socket479 | 2 | 1.83 | 1 | DDR2 SODIMM | なし | |
SN68SG2 | Athlon64 X2 5200+ | SocketAM2 | 2 | 2.7 | 4 | DDR2 DIMM | DVD | FDD+メモリカード+2.5HDD×2リムーバブルラック |
SG31G2 | Core2Duo E8200 | LGA775 | 2 | 2.66 | 1 | DDR2 DIMM | DVD | |
SG33G5 | Core2Quad ???? | LGA775 | 4 | 2 | 4 | DDR2 DIMM | DVD | CPU型番不明 おそらくクロック設定(333*6)がおかしい? |
SP35P2 Pro | Core2Duo E6550 | LGA775 | 2 | 2.33 | 6 | DDR2 DIMM | なし | |
XG41 | Pentium Dual-Core E6600 | LGA775 | 2 | 3.06 | 8 | DDR3 DIMM | なし |
かってにコードいじって自己満足に浸る(3) 別
手元に下記のようなメソッドがある。
身バレ防止でだいぶ簡略にしてあるが実物はもっと改装が深くて条件が複雑である
public bool Judge(int p) { if (p == 4 || p == 7 || p == 13) return false; return true; }
別に動作には問題ないのだが機能追加でthis.value<40の場合には(p == 4 || p == 7 || p == 13)ではなく(p == 3 || p == 9)にする必要がある。
さて、どう改修したものか?
まず機能追加する前にやれることをやっておく。
比較対象を全部配列にうつしてContainsメソッドで判定する
public bool Judge(int p) { int[] v = new[] { 4, 7, 13 }; //配列 if(v.Contains(p)) //判定はContainsメソッドで return false; return true; }
これで今回の機能追加で変更する必要があるのはvの中身であろうということがうすうすわかる
public bool Judge(int p) { int[] v = new[] { 4, 7, 13 }; if (this.value1 < 40) //vの中身を変更 v = new[] { 3, 9 }; if(v.Contains(p)) return false; return true; }
v設定部分をメソッド抽出
public bool Judge(int p) { int[] v = NewMethod1(); if (v.Contains(p)) return false; return true; } private int[] NewMethod1() { int[] v = new[] { 4, 7, 13 }; if (this.value1 < 40) v = new[] { 3, 9 }; return v; }
vをインライン化
NewMethod1()の中身を整理
public bool Judge(int p) { if (NewMethod1().Contains(p)) return false; return true; } private int[] NewMethod1() { if (this.value1 < 40) return new[] { 3, 9 }; else return new[] { 4, 7, 13 }; }
Judgeメソッドのreturn部分を整理
public bool Judge(int p) { return !NewMethod1().Contains(p); } private int[] NewMethod1() { if (this.value1 < 40) return new[] { 3, 9 }; else return new[] { 4, 7, 13 }; }
かってにコードいじって自己満足に浸る(3)
手元に下記のようなメソッドがある。
身バレ防止でだいぶ簡略にしてあるが実物はもっと改装が深くて条件が複雑である
public bool Judge(int p) { if (p == 4 || p == 7 || p == 13) return false; return true; }
別に動作には問題ないのだが機能追加でthis.value<40の場合には(p == 4 || p == 7 || p == 13)ではなく(p == 3 || p == 9)にする必要がある。
さて、どう改修したものか?
素直に書くと
public bool Judge(int p) { if (this.value1 < 40) { if (p == 3 || p == 9) return false; } else { if (p == 4 || p == 7 || p == 13) return false; } return true; }
だが、if文が二手に分かれるのがいやなので動作を変えないように注意深く変更していく。
1 まずは比較対象を配列におさめる
int[] v; //配列 if (this.value1 < 40) { v = new int[] { 3, 9 };//配列 if (p == 3 || p == 9) return false; } else { v = new int[] { 4, 7, 13 };//配列 if (p == 4 || p == 7 || p == 13) return false; } return true;
2 条件判定部分をv.Contain(p)に置き換える
int[] v; if (this.value1 < 40) { v = new int[] { 3, 9 }; if (v.Contains(p)) //置き換えた return false; } else { v = new int[] { 4, 7, 13 }; if (v.Contains(p)) //置き換えた return false; } return true;
3 if{}の中身およびelse{}の中身で共通する末端部分をくくりだす
int[] v; if (this.value1 < 40) { v = new int[] { 3, 9 }; } else { v = new int[] { 4, 7, 13 }; } if (v.Contains(p)) //くくりだされました return false; return true;
4 vを設定している箇所をメソッドの抽出
public bool Judge(int p) { int[] v = NewMethod1(); //抽出 if (v.Contains(p)) return false; return true; } private int[] NewMethod1() //抽出されました { int[] v; if (this.value1 < 40) { v = new int[] { 3, 9 }; } else { v = new int[] { 4, 7, 13 }; } return v; }
5 return部分がうっとうしいのでついでに書き換える
6 vをインライン化して消去
public bool Judge(int p) { return !NewMethod1().Contains(p); } private int[] NewMethod1() { int[] v; if (this.value1 < 40) { v = new int[] { 3, 9 }; } else { v = new int[] { 4, 7, 13 }; } return v; }